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