QGIS API Documentation  master-59fd5e0
src/core/symbology-ng/qgsgraduatedsymbolrendererv2.cpp
Go to the documentation of this file.
00001 /***************************************************************************
00002     qgsgraduatedsymbolrendererv2.cpp
00003     ---------------------
00004     begin                : November 2009
00005     copyright            : (C) 2009 by Martin Dobias
00006     email                : wonder dot sk at gmail dot com
00007  ***************************************************************************
00008  *                                                                         *
00009  *   This program is free software; you can redistribute it and/or modify  *
00010  *   it under the terms of the GNU General Public License as published by  *
00011  *   the Free Software Foundation; either version 2 of the License, or     *
00012  *   (at your option) any later version.                                   *
00013  *                                                                         *
00014  ***************************************************************************/
00015 #include "qgsgraduatedsymbolrendererv2.h"
00016 
00017 #include "qgssymbolv2.h"
00018 #include "qgssymbollayerv2utils.h"
00019 #include "qgsvectorcolorrampv2.h"
00020 
00021 #include "qgsfeature.h"
00022 #include "qgsvectorlayer.h"
00023 #include "qgslogger.h"
00024 #include "qgsvectordataprovider.h"
00025 
00026 #include <QDomDocument>
00027 #include <QDomElement>
00028 #include <QSettings> // for legend
00029 #include <limits> // for jenks classification
00030 #include <cmath> // for pretty classification
00031 #include <ctime>
00032 
00033 QgsRendererRangeV2::QgsRendererRangeV2()
00034     : mLowerValue( 0 ), mUpperValue( 0 ), mSymbol( 0 ), mLabel()
00035 {
00036 }
00037 
00038 QgsRendererRangeV2::QgsRendererRangeV2( double lowerValue, double upperValue, QgsSymbolV2* symbol, QString label )
00039     : mLowerValue( lowerValue )
00040     , mUpperValue( upperValue )
00041     , mSymbol( symbol )
00042     , mLabel( label )
00043 {
00044 }
00045 
00046 QgsRendererRangeV2::QgsRendererRangeV2( const QgsRendererRangeV2& range )
00047     : mLowerValue( range.mLowerValue )
00048     , mUpperValue( range.mUpperValue )
00049     , mLabel( range.mLabel )
00050 {
00051   mSymbol = range.mSymbol->clone();
00052 }
00053 
00054 QgsRendererRangeV2::~QgsRendererRangeV2()
00055 {
00056   delete mSymbol;
00057 }
00058 
00059 QgsRendererRangeV2& QgsRendererRangeV2::operator=( const QgsRendererRangeV2 & range )
00060 {
00061   mLowerValue = range.mLowerValue;
00062   mUpperValue = range.mUpperValue;
00063   mLabel = range.mLabel;
00064   mSymbol = 0;
00065   if ( range.mSymbol )
00066   {
00067     mSymbol = range.mSymbol->clone();
00068   }
00069   return *this;
00070 }
00071 
00072 double QgsRendererRangeV2::lowerValue() const
00073 {
00074   return mLowerValue;
00075 }
00076 
00077 double QgsRendererRangeV2::upperValue() const
00078 {
00079   return mUpperValue;
00080 }
00081 
00082 QgsSymbolV2* QgsRendererRangeV2::symbol() const
00083 {
00084   return mSymbol;
00085 }
00086 
00087 QString QgsRendererRangeV2::label() const
00088 {
00089   return mLabel;
00090 }
00091 
00092 void QgsRendererRangeV2::setSymbol( QgsSymbolV2* s )
00093 {
00094   if ( mSymbol == s )
00095     return;
00096   delete mSymbol;
00097   mSymbol = s;
00098 }
00099 
00100 void QgsRendererRangeV2::setLabel( QString label )
00101 {
00102   mLabel = label;
00103 }
00104 
00105 void QgsRendererRangeV2::setUpperValue( double upperValue )
00106 {
00107   mUpperValue = upperValue;
00108 }
00109 
00110 void QgsRendererRangeV2::setLowerValue( double lowerValue )
00111 {
00112   mLowerValue = lowerValue;
00113 }
00114 
00115 QString QgsRendererRangeV2::dump()
00116 {
00117   return QString( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel ).arg( mSymbol->dump() );
00118 }
00119 
00120 void QgsRendererRangeV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
00121 {
00122   if ( !mSymbol || props.value( "attribute", "" ).isEmpty() )
00123     return;
00124 
00125   QString attrName = props[ "attribute" ];
00126 
00127   QDomElement ruleElem = doc.createElement( "se:Rule" );
00128   element.appendChild( ruleElem );
00129 
00130   QDomElement nameElem = doc.createElement( "se:Name" );
00131   nameElem.appendChild( doc.createTextNode( mLabel ) );
00132   ruleElem.appendChild( nameElem );
00133 
00134   QDomElement descrElem = doc.createElement( "se:Description" );
00135   QDomElement titleElem = doc.createElement( "se:Title" );
00136   QString descrStr = QString( "range: %1 - %2" ).arg( mLowerValue ).arg( mUpperValue );
00137   titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
00138   descrElem.appendChild( titleElem );
00139   ruleElem.appendChild( descrElem );
00140 
00141   // create the ogc:Filter for the range
00142   QDomElement filterElem = doc.createElement( "ogc:Filter" );
00143   QString filterFunc = QString( "%1 > %2 AND %1 <= %3" )
00144                        .arg( attrName.replace( "\"", "\"\"" ) )
00145                        .arg( mLowerValue ).arg( mUpperValue );
00146   QgsSymbolLayerV2Utils::createFunctionElement( doc, filterElem, filterFunc );
00147   ruleElem.appendChild( filterElem );
00148 
00149   mSymbol->toSld( doc, ruleElem, props );
00150 }
00151 
00153 
00154 QgsGraduatedSymbolRendererV2::QgsGraduatedSymbolRendererV2( QString attrName, QgsRangeList ranges )
00155     : QgsFeatureRendererV2( "graduatedSymbol" ),
00156     mAttrName( attrName ),
00157     mRanges( ranges ),
00158     mMode( Custom ),
00159     mSourceSymbol( NULL ),
00160     mSourceColorRamp( NULL ),
00161     mScaleMethod( DEFAULT_SCALE_METHOD ),
00162     mRotationFieldIdx( -1 ),
00163     mSizeScaleFieldIdx( -1 )
00164 {
00165   // TODO: check ranges for sanity (NULL symbols, invalid ranges)
00166 }
00167 
00168 QgsGraduatedSymbolRendererV2::~QgsGraduatedSymbolRendererV2()
00169 {
00170   mRanges.clear(); // should delete all the symbols
00171   delete mSourceSymbol;
00172   delete mSourceColorRamp;
00173 }
00174 
00175 QgsSymbolV2* QgsGraduatedSymbolRendererV2::symbolForValue( double value )
00176 {
00177   for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
00178   {
00179     if ( it->lowerValue() <= value && it->upperValue() >= value )
00180       return it->symbol();
00181   }
00182   // the value is out of the range: return NULL instead of symbol
00183   return NULL;
00184 }
00185 
00186 QgsSymbolV2* QgsGraduatedSymbolRendererV2::symbolForFeature( QgsFeature& feature )
00187 {
00188   const QgsAttributes& attrs = feature.attributes();
00189   if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
00190   {
00191     QgsDebugMsg( "attribute required by renderer not found: " + mAttrName + "(index " + QString::number( mAttrNum ) + ")" );
00192     return NULL;
00193   }
00194 
00195   // Null values should not be categorized
00196   if ( attrs[mAttrNum].isNull() )
00197     return NULL;
00198 
00199   // find the right category
00200   QgsSymbolV2* symbol = symbolForValue( attrs[mAttrNum].toDouble() );
00201   if ( symbol == NULL )
00202     return NULL;
00203 
00204   if ( mRotationFieldIdx == -1 && mSizeScaleFieldIdx == -1 )
00205     return symbol; // no data-defined rotation/scaling - just return the symbol
00206 
00207   // find out rotation, size scale
00208   double rotation = 0;
00209   double sizeScale = 1;
00210   if ( mRotationFieldIdx != -1 )
00211     rotation = attrs[mRotationFieldIdx].toDouble();
00212   if ( mSizeScaleFieldIdx != -1 )
00213     sizeScale = attrs[mSizeScaleFieldIdx].toDouble();
00214 
00215   // take a temporary symbol (or create it if doesn't exist)
00216   QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
00217 
00218   // modify the temporary symbol and return it
00219   if ( tempSymbol->type() == QgsSymbolV2::Marker )
00220   {
00221     QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
00222     if ( mRotationFieldIdx != -1 )
00223       markerSymbol->setAngle( rotation );
00224     if ( mSizeScaleFieldIdx != -1 )
00225       markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
00226     markerSymbol->setScaleMethod( mScaleMethod );
00227   }
00228   else if ( tempSymbol->type() == QgsSymbolV2::Line )
00229   {
00230     QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
00231     if ( mSizeScaleFieldIdx != -1 )
00232       lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
00233   }
00234   return tempSymbol;
00235 }
00236 
00237 void QgsGraduatedSymbolRendererV2::startRender( QgsRenderContext& context, const QgsVectorLayer *vlayer )
00238 {
00239   // find out classification attribute index from name
00240   mAttrNum = vlayer ? vlayer->fieldNameIndex( mAttrName ) : -1;
00241 
00242   mRotationFieldIdx  = ( mRotationField.isEmpty()  ? -1 : vlayer->fieldNameIndex( mRotationField ) );
00243   mSizeScaleFieldIdx = ( mSizeScaleField.isEmpty() ? -1 : vlayer->fieldNameIndex( mSizeScaleField ) );
00244 
00245   QgsRangeList::iterator it = mRanges.begin();
00246   for ( ; it != mRanges.end(); ++it )
00247   {
00248     it->symbol()->startRender( context, vlayer );
00249 
00250     if ( mRotationFieldIdx != -1 || mSizeScaleFieldIdx != -1 )
00251     {
00252       QgsSymbolV2* tempSymbol = it->symbol()->clone();
00253       tempSymbol->setRenderHints(( mRotationFieldIdx != -1 ? QgsSymbolV2::DataDefinedRotation : 0 ) |
00254                                  ( mSizeScaleFieldIdx != -1 ? QgsSymbolV2::DataDefinedSizeScale : 0 ) );
00255       tempSymbol->startRender( context, vlayer );
00256       mTempSymbols[ it->symbol()] = tempSymbol;
00257     }
00258   }
00259 }
00260 
00261 void QgsGraduatedSymbolRendererV2::stopRender( QgsRenderContext& context )
00262 {
00263   QgsRangeList::iterator it = mRanges.begin();
00264   for ( ; it != mRanges.end(); ++it )
00265     it->symbol()->stopRender( context );
00266 
00267   // cleanup mTempSymbols
00268 #if QT_VERSION < 0x40600
00269   QMap<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
00270 #else
00271   QHash<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
00272 #endif
00273   for ( ; it2 != mTempSymbols.end(); ++it2 )
00274   {
00275     it2.value()->stopRender( context );
00276     delete it2.value();
00277   }
00278   mTempSymbols.clear();
00279 }
00280 
00281 QList<QString> QgsGraduatedSymbolRendererV2::usedAttributes()
00282 {
00283   QSet<QString> attributes;
00284   attributes.insert( mAttrName );
00285   if ( !mRotationField.isEmpty() )
00286   {
00287     attributes.insert( mRotationField );
00288   }
00289   if ( !mSizeScaleField.isEmpty() )
00290   {
00291     attributes.insert( mSizeScaleField );
00292   }
00293 
00294   QgsSymbolV2* symbol = 0;
00295   QgsRangeList::const_iterator range_it = mRanges.constBegin();
00296   for ( ; range_it != mRanges.constEnd(); ++range_it )
00297   {
00298     symbol = range_it->symbol();
00299     if ( symbol )
00300     {
00301       attributes.unite( symbol->usedAttributes() );
00302     }
00303   }
00304   return attributes.toList();
00305 }
00306 
00307 bool QgsGraduatedSymbolRendererV2::updateRangeSymbol( int rangeIndex, QgsSymbolV2* symbol )
00308 {
00309   if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
00310     return false;
00311   mRanges[rangeIndex].setSymbol( symbol );
00312   return true;
00313 }
00314 
00315 bool QgsGraduatedSymbolRendererV2::updateRangeLabel( int rangeIndex, QString label )
00316 {
00317   if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
00318     return false;
00319   mRanges[rangeIndex].setLabel( label );
00320   return true;
00321 }
00322 
00323 bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double value )
00324 {
00325   if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
00326     return false;
00327   mRanges[rangeIndex].setUpperValue( value );
00328   return true;
00329 }
00330 
00331 bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double value )
00332 {
00333   if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
00334     return false;
00335   mRanges[rangeIndex].setLowerValue( value );
00336   return true;
00337 }
00338 
00339 QString QgsGraduatedSymbolRendererV2::dump()
00340 {
00341   QString s = QString( "GRADUATED: attr %1\n" ).arg( mAttrName );
00342   for ( int i = 0; i < mRanges.count(); i++ )
00343     s += mRanges[i].dump();
00344   return s;
00345 }
00346 
00347 QgsFeatureRendererV2* QgsGraduatedSymbolRendererV2::clone()
00348 {
00349   QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( mAttrName, mRanges );
00350   r->setMode( mMode );
00351   if ( mSourceSymbol )
00352     r->setSourceSymbol( mSourceSymbol->clone() );
00353   if ( mSourceColorRamp )
00354     r->setSourceColorRamp( mSourceColorRamp->clone() );
00355   r->setUsingSymbolLevels( usingSymbolLevels() );
00356   r->setRotationField( rotationField() );
00357   r->setSizeScaleField( sizeScaleField() );
00358   r->setScaleMethod( scaleMethod() );
00359   return r;
00360 }
00361 
00362 void QgsGraduatedSymbolRendererV2::toSld( QDomDocument& doc, QDomElement &element ) const
00363 {
00364   QgsStringMap props;
00365   props[ "attribute" ] = mAttrName;
00366   if ( !mRotationField.isEmpty() )
00367     props[ "angle" ] = QString( mRotationField ).append( "\"" ).prepend( "\"" );
00368   if ( !mSizeScaleField.isEmpty() )
00369     props[ "scale" ] = QString( mSizeScaleField ).append( "\"" ).prepend( "\"" );
00370 
00371   // create a Rule for each range
00372   for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); it++ )
00373   {
00374     QgsStringMap catProps( props );
00375     it->toSld( doc, element, catProps );
00376   }
00377 }
00378 
00379 QgsSymbolV2List QgsGraduatedSymbolRendererV2::symbols()
00380 {
00381   QgsSymbolV2List lst;
00382   for ( int i = 0; i < mRanges.count(); i++ )
00383     lst.append( mRanges[i].symbol() );
00384   return lst;
00385 }
00386 
00387 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
00388 {
00389 
00390   // Equal interval algorithm
00391   //
00392   // Returns breaks based on dividing the range ('minimum' to 'maximum')
00393   // into 'classes' parts.
00394 
00395   double step = ( maximum - minimum ) / classes;
00396 
00397   QList<double> breaks;
00398   double value = minimum;
00399   for ( int i = 0; i < classes; i++ )
00400   {
00401     value += step;
00402     breaks.append( value );
00403   }
00404 
00405   // floating point arithmetics is not precise:
00406   // set the last break to be exactly maximum so we do not miss it
00407   breaks[classes-1] = maximum;
00408 
00409   return breaks;
00410 }
00411 
00412 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
00413 {
00414 
00415   // q-th quantile of a data set:
00416   // value where q fraction of data is below and (1-q) fraction is above this value
00417   // Xq = (1 - r) * X_NI1 + r * X_NI2
00418   //   NI1 = (int) (q * (n+1))
00419   //   NI2 = NI1 + 1
00420   //   r = q * (n+1) - (int) (q * (n+1))
00421   // (indices of X: 1...n)
00422 
00423   // sort the values first
00424   qSort( values );
00425 
00426   QList<double> breaks;
00427 
00428   int n = values.count();
00429   double Xq = n > 0 ? values[0] : 0.0;
00430 
00431   for ( int i = 1; i < classes; i++ )
00432   {
00433     if ( n > 1 )
00434     {
00435       double q = i  / ( double ) classes;
00436       double a = q * ( n - 1 );
00437       int aa = ( int )( a );
00438 
00439       double r = a - aa;
00440       Xq = ( 1 - r ) * values[aa] + r * values[aa+1];
00441     }
00442     breaks.append( Xq );
00443   }
00444 
00445   breaks.append( values[ n-1 ] );
00446 
00447   return breaks;
00448 }
00449 
00450 static QList<double> _calcPrettyBreaks( double minimum, double maximum, int classes )
00451 {
00452 
00453   // C++ implementation of R's pretty algorithm
00454   // Based on code for determining optimal tick placement for statistical graphics
00455   // from the R statistical programming language.
00456   // Code ported from R implementation from 'labeling' R package
00457   //
00458   // Computes a sequence of about 'classes' equally spaced round values
00459   // which cover the range of values from 'minimum' to 'maximum'.
00460   // The values are chosen so that they are 1, 2 or 5 times a power of 10.
00461 
00462   QList<double> breaks;
00463   if ( classes < 1 )
00464   {
00465     breaks.append( maximum );
00466     return breaks;
00467   }
00468 
00469   int minimumCount = ( int ) classes / 3;
00470   double shrink = 0.75;
00471   double highBias = 1.5;
00472   double adjustBias = 0.5 + 1.5 * highBias;
00473   int divisions = classes;
00474   double h = highBias;
00475   double cell;
00476   int U;
00477   bool small = false;
00478   double dx = maximum - minimum;
00479 
00480   if ( dx == 0 && maximum == 0 )
00481   {
00482     cell = 1.0;
00483     small = true;
00484     U = 1;
00485   }
00486   else
00487   {
00488     cell = qMax( qAbs( minimum ), qAbs( maximum ) );
00489     if ( adjustBias >= 1.5 * h + 0.5 )
00490     {
00491       U = 1 + ( 1.0 / ( 1 + h ) );
00492     }
00493     else
00494     {
00495       U = 1 + ( 1.5 / ( 1 + adjustBias ) );
00496     }
00497     small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 );
00498   }
00499 
00500   if ( small )
00501   {
00502     if ( cell > 10 )
00503     {
00504       cell = 9 + cell / 10;
00505       cell = cell * shrink;
00506     }
00507     if ( minimumCount > 1 )
00508     {
00509       cell = cell / minimumCount;
00510     }
00511   }
00512   else
00513   {
00514     cell = dx;
00515     if ( divisions > 1 )
00516     {
00517       cell = cell / divisions;
00518     }
00519   }
00520   if ( cell < 20 * 1e-07 )
00521   {
00522     cell = 20 * 1e-07;
00523   }
00524 
00525   double base = pow( 10.0, floor( log10( cell ) ) );
00526   double unit = base;
00527   if (( 2 * base ) - cell < h *( cell - unit ) )
00528   {
00529     unit = 2.0 * base;
00530     if (( 5 * base ) - cell < adjustBias *( cell - unit ) )
00531     {
00532       unit = 5.0 * base;
00533       if (( 10.0 * base ) - cell < h *( cell - unit ) )
00534       {
00535         unit = 10.0 * base;
00536       }
00537     }
00538   }
00539   // Maybe used to correct for the epsilon here??
00540   int start = floor( minimum / unit + 1e-07 );
00541   int end = ceil( maximum / unit - 1e-07 );
00542 
00543   // Extend the range out beyond the data. Does this ever happen??
00544   while ( start * unit > minimum + ( 1e-07 * unit ) )
00545   {
00546     start = start - 1;
00547   }
00548   while ( end * unit < maximum - ( 1e-07 * unit ) )
00549   {
00550     end = end + 1;
00551   }
00552   QgsDebugMsg( QString( "pretty classes: %1" ).arg( end ) );
00553 
00554   // If we don't have quite enough labels, extend the range out
00555   // to make more (these labels are beyond the data :( )
00556   int k = floor( 0.5 + end - start );
00557   if ( k < minimumCount )
00558   {
00559     k = minimumCount - k;
00560     if ( start >= 0 )
00561     {
00562       end = end + k / 2;
00563       start = start - k / 2 + k % 2;
00564     }
00565     else
00566     {
00567       start = start - k / 2;
00568       end = end + k / 2 + k % 2;
00569     }
00570     divisions = minimumCount;
00571   }
00572   else
00573   {
00574     divisions = k;
00575   }
00576   double minimumBreak = start * unit;
00577   //double maximumBreak = end * unit;
00578   int count = end - start;
00579 
00580   for ( int i = 1; i < count + 1; i++ )
00581   {
00582     breaks.append( minimumBreak + i * unit );
00583   }
00584 
00585   if ( breaks.isEmpty() )
00586     return breaks;
00587 
00588   if ( breaks.first() < minimum )
00589   {
00590     breaks[0] = minimum;
00591   }
00592   if ( breaks.last() > maximum )
00593   {
00594     breaks[breaks.count()-1] = maximum;
00595   }
00596 
00597   return breaks;
00598 } // _calcPrettyBreaks
00599 
00600 
00601 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<int> &labels )
00602 {
00603 
00604   // C++ implementation of the standard deviation class interval algorithm
00605   // as implemented in the 'classInt' package available for the R statistical
00606   // prgramming language.
00607 
00608   // Returns breaks based on '_calcPrettyBreaks' of the centred and scaled
00609   // values of 'values', and may have a number of classes different from 'classes'.
00610 
00611   double mean = 0.0;
00612   double stdDev = 0.0;
00613   int n = values.count();
00614   double minimum = values[0];
00615   double maximum = values[0];
00616 
00617   for ( int i = 0; i < n; i++ )
00618   {
00619     mean += values[i];
00620     minimum = qMin( values[i], minimum ); // could use precomputed max and min
00621     maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
00622   }
00623   mean = mean / ( double ) n;
00624 
00625   double sd = 0.0;
00626   for ( int i = 0; i < n; i++ )
00627   {
00628     sd = values[i] - mean;
00629     stdDev += sd * sd;
00630   }
00631   stdDev = sqrt( stdDev / n );
00632 
00633   QList<double> breaks = _calcPrettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
00634   for ( int i = 0; i < breaks.count(); i++ )
00635   {
00636     labels.append(( int ) breaks[i] );
00637     breaks[i] = ( breaks[i] * stdDev ) + mean;
00638   }
00639 
00640   return breaks;
00641 } // _calcStdDevBreaks
00642 
00643 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
00644                                        double minimum, double maximum,
00645                                        int maximumSize = 1000 )
00646 {
00647   // Jenks Optimal (Natural Breaks) algorithm
00648   // Based on the Jenks algorithm from the 'classInt' package available for
00649   // the R statistical prgramming language, and from Python code from here:
00650   // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
00651   // and is based on a JAVA and Fortran code available here:
00652   // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
00653 
00654   // Returns class breaks such that classes are internally homogeneous while
00655   // assuring heterogeneity among classes.
00656 
00657   if ( classes <= 1 )
00658   {
00659     return QList<double>() << maximum;
00660   }
00661 
00662   if ( classes >= values.size() )
00663   {
00664     return values;
00665   }
00666 
00667   QVector<double> sample;
00668 
00669   // if we have lots of values, we need to take a random sample
00670   if ( values.size() > maximumSize )
00671   {
00672     // for now, sample at least maximumSize values or a 10% sample, whichever
00673     // is larger. This will produce a more representative sample for very large
00674     // layers, but could end up being computationally intensive...
00675 
00676     qsrand( time( 0 ) );
00677 
00678     sample.resize( qMax( maximumSize, values.size() / 10 ) );
00679 
00680     QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
00681     QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
00682 
00683     sample[ 0 ] = minimum;
00684     sample[ 1 ] = maximum;;
00685     for ( int i = 2; i < sample.size(); i++ )
00686     {
00687       // pick a random integer from 0 to n
00688       double r = qrand();
00689       int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
00690       sample[ i ] = values[ j ];
00691     }
00692   }
00693   else
00694   {
00695     sample = values.toVector();
00696   }
00697 
00698   int n = sample.size();
00699 
00700   // sort the sample values
00701   qSort( sample );
00702 
00703   QVector< QVector<int> > matrixOne( n + 1 );
00704   QVector< QVector<double> > matrixTwo( n + 1 );
00705 
00706   for ( int i = 0; i <= n; i++ )
00707   {
00708     matrixOne[i].resize( classes + 1 );
00709     matrixTwo[i].resize( classes + 1 );
00710   }
00711 
00712   for ( int i = 1; i <= classes; i++ )
00713   {
00714     matrixOne[0][i] = 1;
00715     matrixOne[1][i] = 1;
00716     matrixTwo[0][i] = 0.0;
00717     for ( int j = 2; j <= n; j++ )
00718     {
00719       matrixTwo[j][i] = std::numeric_limits<double>::max();
00720     }
00721   }
00722 
00723   for ( int l = 2; l <= n; l++ )
00724   {
00725     double s1 = 0.0;
00726     double s2 = 0.0;
00727     int w = 0;
00728 
00729     double v = 0.0;
00730 
00731     for ( int m = 1; m <= l; m++ )
00732     {
00733       int i3 = l - m + 1;
00734 
00735       double val = sample[ i3 - 1 ];
00736 
00737       s2 += val * val;
00738       s1 += val;
00739       w++;
00740 
00741       v = s2 - ( s1 * s1 ) / ( double ) w;
00742       int i4 = i3 - 1;
00743       if ( i4 != 0 )
00744       {
00745         for ( int j = 2; j <= classes; j++ )
00746         {
00747           if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
00748           {
00749             matrixOne[l][j] = i4;
00750             matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
00751           }
00752         }
00753       }
00754     }
00755     matrixOne[l][1] = 1;
00756     matrixTwo[l][1] = v;
00757   }
00758 
00759   QVector<double> breaks( classes );
00760   breaks[classes-1] = sample[n-1];
00761 
00762   for ( int j = classes, k = n; j >= 2; j-- )
00763   {
00764     int id = matrixOne[k][j] - 1;
00765     breaks[j - 2] = sample[id];
00766     k = matrixOne[k][j] - 1;
00767   }
00768 
00769   return breaks.toList();
00770 } //_calcJenksBreaks
00771 
00772 
00773 QgsGraduatedSymbolRendererV2* QgsGraduatedSymbolRendererV2::createRenderer(
00774   QgsVectorLayer* vlayer,
00775   QString attrName,
00776   int classes,
00777   Mode mode,
00778   QgsSymbolV2* symbol,
00779   QgsVectorColorRampV2* ramp )
00780 {
00781   if ( classes < 1 )
00782     return NULL;
00783 
00784   int attrNum = vlayer->fieldNameIndex( attrName );
00785 
00786   double minimum = vlayer->minimumValue( attrNum ).toDouble();
00787   double maximum = vlayer->maximumValue( attrNum ).toDouble();
00788   QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
00789 
00790   QList<double> breaks;
00791   QList<int> labels;
00792   if ( mode == EqualInterval )
00793   {
00794     breaks = _calcEqualIntervalBreaks( minimum, maximum, classes );
00795   }
00796   else if ( mode == Pretty )
00797   {
00798     breaks = _calcPrettyBreaks( minimum, maximum, classes );
00799   }
00800   else if ( mode == Quantile || mode == Jenks || mode == StdDev )
00801   {
00802     // get values from layer
00803     QList<double> values;
00804     QgsFeature f;
00805     QgsAttributeList lst;
00806     lst.append( attrNum );
00807 
00808     QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( lst ) );
00809 
00810     // create list of non-null attribute values
00811     while ( fit.nextFeature( f ) )
00812       if ( !f.attribute( attrNum ).isNull() )
00813         values.append( f.attribute( attrNum ).toDouble() );
00814 
00815     // calculate the breaks
00816     if ( mode == Quantile )
00817     {
00818       breaks = _calcQuantileBreaks( values, classes );
00819     }
00820     else if ( mode == Jenks )
00821     {
00822       breaks = _calcJenksBreaks( values, classes, minimum, maximum );
00823     }
00824     else if ( mode == StdDev )
00825     {
00826       breaks = _calcStdDevBreaks( values, classes, labels );
00827     }
00828   }
00829   else
00830   {
00831     Q_ASSERT( false );
00832   }
00833 
00834   QgsRangeList ranges;
00835   double lower, upper = minimum;
00836   QString label;
00837 
00838   // "breaks" list contains all values at class breaks plus maximum as last break
00839   int i = 0;
00840   for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
00841   {
00842     lower = upper; // upper border from last interval
00843     upper = *it;
00844     if ( mode == StdDev )
00845     {
00846       if ( i == 0 )
00847       {
00848         label = "< " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
00849       }
00850       else if ( i == labels.count() - 1 )
00851       {
00852         label = ">= " + QString::number( labels[i-1], 'i', 0 ) + " Std Dev";
00853       }
00854       else
00855       {
00856         label = QString::number( labels[i-1], 'i', 0 ) + " Std Dev" + " - " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
00857       }
00858     }
00859     else
00860     {
00861       label = QString::number( lower, 'f', 4 ) + " - " + QString::number( upper, 'f', 4 );
00862     }
00863 
00864     QgsSymbolV2* newSymbol = symbol->clone();
00865     double colorValue = ( breaks.count() > 1 ? ( double ) i / ( breaks.count() - 1 ) : 0 );
00866     newSymbol->setColor( ramp->color( colorValue ) ); // color from (0 / cl-1) to (cl-1 / cl-1)
00867 
00868     ranges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
00869   }
00870 
00871   QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
00872   r->setSourceSymbol( symbol->clone() );
00873   r->setSourceColorRamp( ramp->clone() );
00874   r->setMode( mode );
00875   return r;
00876 }
00877 
00878 QgsFeatureRendererV2* QgsGraduatedSymbolRendererV2::create( QDomElement& element )
00879 {
00880   QDomElement symbolsElem = element.firstChildElement( "symbols" );
00881   if ( symbolsElem.isNull() )
00882     return NULL;
00883 
00884   QDomElement rangesElem = element.firstChildElement( "ranges" );
00885   if ( rangesElem.isNull() )
00886     return NULL;
00887 
00888   QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
00889   QgsRangeList ranges;
00890 
00891   QDomElement rangeElem = rangesElem.firstChildElement();
00892   while ( !rangeElem.isNull() )
00893   {
00894     if ( rangeElem.tagName() == "range" )
00895     {
00896       double lowerValue = rangeElem.attribute( "lower" ).toDouble();
00897       double upperValue = rangeElem.attribute( "upper" ).toDouble();
00898       QString symbolName = rangeElem.attribute( "symbol" );
00899       QString label = rangeElem.attribute( "label" );
00900       if ( symbolMap.contains( symbolName ) )
00901       {
00902         QgsSymbolV2* symbol = symbolMap.take( symbolName );
00903         ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label ) );
00904       }
00905     }
00906     rangeElem = rangeElem.nextSiblingElement();
00907   }
00908 
00909   QString attrName = element.attribute( "attr" );
00910 
00911   QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
00912 
00913   // delete symbols if there are any more
00914   QgsSymbolLayerV2Utils::clearSymbolMap( symbolMap );
00915 
00916   // try to load source symbol (optional)
00917   QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
00918   if ( !sourceSymbolElem.isNull() )
00919   {
00920     QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
00921     if ( sourceSymbolMap.contains( "0" ) )
00922     {
00923       r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
00924     }
00925     QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
00926   }
00927 
00928   // try to load color ramp (optional)
00929   QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
00930   if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
00931   {
00932     r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
00933   }
00934 
00935   // try to load mode
00936   QDomElement modeElem = element.firstChildElement( "mode" );
00937   if ( !modeElem.isNull() )
00938   {
00939     QString modeString = modeElem.attribute( "name" );
00940     if ( modeString == "equal" )
00941       r->setMode( EqualInterval );
00942     else if ( modeString == "quantile" )
00943       r->setMode( Quantile );
00944     else if ( modeString == "jenks" )
00945       r->setMode( Jenks );
00946     else if ( modeString == "stddev" )
00947       r->setMode( StdDev );
00948     else if ( modeString == "pretty" )
00949       r->setMode( Pretty );
00950   }
00951 
00952   QDomElement rotationElem = element.firstChildElement( "rotation" );
00953   if ( !rotationElem.isNull() )
00954     r->setRotationField( rotationElem.attribute( "field" ) );
00955 
00956   QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
00957   if ( !sizeScaleElem.isNull() )
00958   {
00959     r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
00960     r->setScaleMethod( QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ) );
00961   }
00962 
00963   // TODO: symbol levels
00964   return r;
00965 }
00966 
00967 QDomElement QgsGraduatedSymbolRendererV2::save( QDomDocument& doc )
00968 {
00969   QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
00970   rendererElem.setAttribute( "type", "graduatedSymbol" );
00971   rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
00972   rendererElem.setAttribute( "attr", mAttrName );
00973 
00974   // ranges
00975   int i = 0;
00976   QgsSymbolV2Map symbols;
00977   QDomElement rangesElem = doc.createElement( "ranges" );
00978   QgsRangeList::const_iterator it = mRanges.constBegin();
00979   for ( ; it != mRanges.constEnd(); it++ )
00980   {
00981     const QgsRendererRangeV2& range = *it;
00982     QString symbolName = QString::number( i );
00983     symbols.insert( symbolName, range.symbol() );
00984 
00985     QDomElement rangeElem = doc.createElement( "range" );
00986     rangeElem.setAttribute( "lower", QString::number( range.lowerValue() ) );
00987     rangeElem.setAttribute( "upper", QString::number( range.upperValue() ) );
00988     rangeElem.setAttribute( "symbol", symbolName );
00989     rangeElem.setAttribute( "label", range.label() );
00990     rangesElem.appendChild( rangeElem );
00991     i++;
00992   }
00993 
00994   rendererElem.appendChild( rangesElem );
00995 
00996   // save symbols
00997   QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
00998   rendererElem.appendChild( symbolsElem );
00999 
01000   // save source symbol
01001   if ( mSourceSymbol )
01002   {
01003     QgsSymbolV2Map sourceSymbols;
01004     sourceSymbols.insert( "0", mSourceSymbol );
01005     QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
01006     rendererElem.appendChild( sourceSymbolElem );
01007   }
01008 
01009   // save source color ramp
01010   if ( mSourceColorRamp )
01011   {
01012     QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp, doc );
01013     rendererElem.appendChild( colorRampElem );
01014   }
01015 
01016   // save mode
01017   QString modeString;
01018   if ( mMode == EqualInterval )
01019     modeString = "equal";
01020   else if ( mMode == Quantile )
01021     modeString = "quantile";
01022   else if ( mMode == Jenks )
01023     modeString = "jenks";
01024   else if ( mMode == StdDev )
01025     modeString = "stddev";
01026   else if ( mMode == Pretty )
01027     modeString = "pretty";
01028   if ( !modeString.isEmpty() )
01029   {
01030     QDomElement modeElem = doc.createElement( "mode" );
01031     modeElem.setAttribute( "name", modeString );
01032     rendererElem.appendChild( modeElem );
01033   }
01034 
01035   QDomElement rotationElem = doc.createElement( "rotation" );
01036   rotationElem.setAttribute( "field", mRotationField );
01037   rendererElem.appendChild( rotationElem );
01038 
01039   QDomElement sizeScaleElem = doc.createElement( "sizescale" );
01040   sizeScaleElem.setAttribute( "field", mSizeScaleField );
01041   sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
01042   rendererElem.appendChild( sizeScaleElem );
01043 
01044   return rendererElem;
01045 }
01046 
01047 QgsLegendSymbologyList QgsGraduatedSymbolRendererV2::legendSymbologyItems( QSize iconSize )
01048 {
01049   QSettings settings;
01050   bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
01051 
01052   QgsLegendSymbologyList lst;
01053   if ( showClassifiers )
01054   {
01055     lst << qMakePair( classAttribute(), QPixmap() );
01056   }
01057 
01058   int count = ranges().count();
01059   for ( int i = 0; i < count; i++ )
01060   {
01061     const QgsRendererRangeV2& range = ranges()[i];
01062     QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
01063     lst << qMakePair( range.label(), pix );
01064   }
01065   return lst;
01066 }
01067 
01068 QgsLegendSymbolList QgsGraduatedSymbolRendererV2::legendSymbolItems()
01069 {
01070   QSettings settings;
01071   bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
01072 
01073   QgsLegendSymbolList lst;
01074   if ( showClassifiers )
01075   {
01076     lst << qMakePair( classAttribute(), ( QgsSymbolV2* )0 );
01077   }
01078 
01079   foreach ( const QgsRendererRangeV2& range, mRanges )
01080   {
01081     QgsSymbolV2* symbol;
01082     if ( mRotationFieldIdx == -1 && mSizeScaleFieldIdx == -1 )
01083     {
01084       symbol = range.symbol();
01085     }
01086     else
01087     {
01088       symbol = mTempSymbols[range.symbol()];
01089     }
01090     lst << qMakePair( range.label(), symbol );
01091   }
01092   return lst;
01093 }
01094 
01095 QgsSymbolV2* QgsGraduatedSymbolRendererV2::sourceSymbol()
01096 {
01097   return mSourceSymbol;
01098 }
01099 void QgsGraduatedSymbolRendererV2::setSourceSymbol( QgsSymbolV2* sym )
01100 {
01101   delete mSourceSymbol;
01102   mSourceSymbol = sym;
01103 }
01104 
01105 QgsVectorColorRampV2* QgsGraduatedSymbolRendererV2::sourceColorRamp()
01106 {
01107   return mSourceColorRamp;
01108 }
01109 void QgsGraduatedSymbolRendererV2::setSourceColorRamp( QgsVectorColorRampV2* ramp )
01110 {
01111   delete mSourceColorRamp;
01112   mSourceColorRamp = ramp;
01113 }
01114 
01115 void QgsGraduatedSymbolRendererV2::updateColorRamp( QgsVectorColorRampV2 *ramp )
01116 {
01117   int i = 0;
01118   foreach ( QgsRendererRangeV2 range, mRanges )
01119   {
01120     QgsSymbolV2* symbol = range.symbol()->clone();
01121     double colorValue = ( mRanges.count() > 1 ? ( double ) i / ( mRanges.count() - 1 ) : 0 );
01122     symbol->setColor( ramp->color( colorValue ) );
01123     updateRangeSymbol( i, symbol );
01124     ++i;
01125   }
01126   this->setSourceColorRamp( ramp );
01127 }
01128 
01129 void QgsGraduatedSymbolRendererV2::updateSymbols( QgsSymbolV2 *sym )
01130 {
01131   int i = 0;
01132   foreach ( QgsRendererRangeV2 range, mRanges )
01133   {
01134     QgsSymbolV2* symbol = sym->clone();
01135     symbol->setColor( range.symbol()->color() );
01136     updateRangeSymbol( i, symbol );
01137     ++i;
01138   }
01139   this->setSourceSymbol( sym->clone() );
01140 }
01141 
01142 void QgsGraduatedSymbolRendererV2::setScaleMethod( QgsSymbolV2::ScaleMethod scaleMethod )
01143 {
01144   mScaleMethod = scaleMethod;
01145   foreach ( QgsRendererRangeV2 range, mRanges )
01146   {
01147     setScaleMethodToSymbol( range.symbol(), scaleMethod );
01148   }
01149 }
01150 
01151 void QgsGraduatedSymbolRendererV2::addClass( QgsSymbolV2* symbol )
01152 {
01153   QgsSymbolV2* newSymbol = symbol->clone();
01154   QString label = "0.0 - 0.0";
01155   mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
01156 
01157 }
01158 
01159 void QgsGraduatedSymbolRendererV2::deleteClass( int idx )
01160 {
01161   mRanges.removeAt( idx );
01162 }
01163 
01164 void QgsGraduatedSymbolRendererV2::deleteAllClasses()
01165 {
01166   mRanges.clear();
01167 }
01168 
01169 void QgsGraduatedSymbolRendererV2::moveClass( int from, int to )
01170 {
01171   if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() ) return;
01172   mRanges.move( from, to );
01173 }
01174 
01175 bool valueLessThan( const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2 )
01176 {
01177   return r1.lowerValue() < r2.lowerValue();
01178 }
01179 
01180 bool valueGreaterThan( const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2 )
01181 {
01182   return !valueLessThan( r1, r2 );
01183 }
01184 
01185 void QgsGraduatedSymbolRendererV2::sortByValue( Qt::SortOrder order )
01186 {
01187   QgsDebugMsg( "Entered" );
01188   if ( order == Qt::AscendingOrder )
01189   {
01190     qSort( mRanges.begin(), mRanges.end(), valueLessThan );
01191   }
01192   else
01193   {
01194     qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
01195   }
01196 }
01197 
01198 bool labelLessThan( const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2 )
01199 {
01200   return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
01201 }
01202 
01203 bool labelGreaterThan( const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2 )
01204 {
01205   return !labelLessThan( r1, r2 );
01206 }
01207 
01208 void QgsGraduatedSymbolRendererV2::sortByLabel( Qt::SortOrder order )
01209 {
01210   if ( order == Qt::AscendingOrder )
01211   {
01212     qSort( mRanges.begin(), mRanges.end(), labelLessThan );
01213   }
01214   else
01215   {
01216     qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
01217   }
01218 }
01219 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Defines