QGIS API Documentation  2.5.0-Master
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
qgsgraduatedsymbolrendererv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgraduatedsymbolrendererv2.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
16 
17 #include "qgssymbolv2.h"
18 #include "qgssymbollayerv2utils.h"
19 #include "qgsvectorcolorrampv2.h"
20 
21 #include "qgsfeature.h"
22 #include "qgsvectorlayer.h"
23 #include "qgslogger.h"
24 #include "qgsvectordataprovider.h"
25 #include "qgsexpression.h"
26 #include <QDomDocument>
27 #include <QDomElement>
28 #include <QSettings> // for legend
29 #include <limits> // for jenks classification
30 #include <cmath> // for pretty classification
31 #include <ctime>
32 
34  : mLowerValue( 0 ), mUpperValue( 0 ), mSymbol( 0 ), mLabel()
35 {
36 }
37 
38 QgsRendererRangeV2::QgsRendererRangeV2( double lowerValue, double upperValue, QgsSymbolV2* symbol, QString label, bool render )
39  : mLowerValue( lowerValue )
40  , mUpperValue( upperValue )
41  , mSymbol( symbol )
42  , mLabel( label )
43  , mRender( render )
44 {
45 }
46 
48  : mLowerValue( range.mLowerValue )
49  , mUpperValue( range.mUpperValue )
50  , mSymbol( range.mSymbol.data() ? range.mSymbol->clone() : NULL )
51  , mLabel( range.mLabel )
52  , mRender( range.mRender )
53 {
54 }
55 
56 // cpy and swap idiom, note that the cpy is done with 'pass by value'
58 {
59  swap( range );
60  return *this;
61 }
62 
64 {
65  qSwap( mLowerValue, other.mLowerValue );
66  qSwap( mUpperValue, other.mUpperValue );
67  qSwap( mSymbol, other.mSymbol );
68  std::swap( mLabel, other.mLabel );
69 }
70 
72 {
73  return mLowerValue;
74 }
75 
77 {
78  return mUpperValue;
79 }
80 
82 {
83  return mSymbol.data();
84 }
85 
87 {
88  return mLabel;
89 }
90 
92 {
93  if ( mSymbol.data() != s ) mSymbol.reset( s );
94 }
95 
96 void QgsRendererRangeV2::setLabel( QString label )
97 {
98  mLabel = label;
99 }
100 
101 void QgsRendererRangeV2::setUpperValue( double upperValue )
102 {
104 }
105 
106 void QgsRendererRangeV2::setLowerValue( double lowerValue )
107 {
109 }
110 
112 {
113  return mRender;
114 }
115 
117 {
118  mRender = render;
119 }
120 
122 {
123  return QString( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel ).arg( mSymbol->dump() );
124 }
125 
126 void QgsRendererRangeV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
127 {
128  if ( !mSymbol.data() || props.value( "attribute", "" ).isEmpty() )
129  return;
130 
131  QString attrName = props[ "attribute" ];
132 
133  QDomElement ruleElem = doc.createElement( "se:Rule" );
134  element.appendChild( ruleElem );
135 
136  QDomElement nameElem = doc.createElement( "se:Name" );
137  nameElem.appendChild( doc.createTextNode( mLabel ) );
138  ruleElem.appendChild( nameElem );
139 
140  QDomElement descrElem = doc.createElement( "se:Description" );
141  QDomElement titleElem = doc.createElement( "se:Title" );
142  QString descrStr = QString( "range: %1 - %2" ).arg( mLowerValue ).arg( mUpperValue );
143  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
144  descrElem.appendChild( titleElem );
145  ruleElem.appendChild( descrElem );
146 
147  // create the ogc:Filter for the range
148  QString filterFunc = QString( "%1 > %2 AND %1 <= %3" )
149  .arg( attrName.replace( "\"", "\"\"" ) )
150  .arg( mLowerValue ).arg( mUpperValue );
151  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, filterFunc );
152 
153  mSymbol->toSld( doc, ruleElem, props );
154 }
155 
157 
159  : QgsFeatureRendererV2( "graduatedSymbol" ),
160  mAttrName( attrName ),
161  mRanges( ranges ),
162  mMode( Custom ),
163  mInvertedColorRamp( false ),
164  mScaleMethod( DEFAULT_SCALE_METHOD )
165 {
166  // TODO: check ranges for sanity (NULL symbols, invalid ranges)
167 }
168 
170 {
171  mRanges.clear(); // should delete all the symbols
172 }
173 
175 {
176  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
177  {
178  if ( it->lowerValue() <= value && it->upperValue() >= value )
179  {
180  if ( it->renderState() || mCounting )
181  return it->symbol();
182  else
183  return NULL;
184  }
185  }
186  // the value is out of the range: return NULL instead of symbol
187  return NULL;
188 }
189 
191 {
192  const QgsAttributes& attrs = feature.attributes();
193  QVariant value;
194  if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
195  {
196  value = mExpression->evaluate( &feature );
197  }
198  else
199  {
200  value = attrs[mAttrNum];
201  }
202 
203  // Null values should not be categorized
204  if ( value.isNull() )
205  return NULL;
206 
207  // find the right category
208  QgsSymbolV2* symbol = symbolForValue( value.toDouble() );
209  if ( symbol == NULL )
210  return NULL;
211 
212  if ( !mRotation.data() && !mSizeScale.data() )
213  return symbol; // no data-defined rotation/scaling - just return the symbol
214 
215  // find out rotation, size scale
216  const double rotation = mRotation.data() ? mRotation->evaluate( feature ).toDouble() : 0;
217  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( feature ).toDouble() : 1.;
218 
219  // take a temporary symbol (or create it if doesn't exist)
220  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
221 
222  // modify the temporary symbol and return it
223  if ( tempSymbol->type() == QgsSymbolV2::Marker )
224  {
225  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
226  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
227  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
228  markerSymbol->setScaleMethod( mScaleMethod );
229  }
230  else if ( tempSymbol->type() == QgsSymbolV2::Line )
231  {
232  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
233  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
234  }
235  return tempSymbol;
236 }
237 
239 {
240  mCounting = context.rendererScale() == 0.0;
241 
242  // find out classification attribute index from name
243  mAttrNum = fields.fieldNameIndex( mAttrName );
244 
245  if ( mAttrNum == -1 )
246  {
247  mExpression.reset( new QgsExpression( mAttrName ) );
248  mExpression->prepare( fields );
249  }
250 
251  QgsRangeList::iterator it = mRanges.begin();
252  for ( ; it != mRanges.end(); ++it )
253  {
254  it->symbol()->startRender( context, &fields );
255 
256  if ( mRotation.data() || mSizeScale.data() )
257  {
258  QgsSymbolV2* tempSymbol = it->symbol()->clone();
259  tempSymbol->setRenderHints(( mRotation.data() ? QgsSymbolV2::DataDefinedRotation : 0 ) |
261  tempSymbol->startRender( context, &fields );
262  mTempSymbols[ it->symbol()] = tempSymbol;
263  }
264  }
265 }
266 
268 {
269  QgsRangeList::iterator it = mRanges.begin();
270  for ( ; it != mRanges.end(); ++it )
271  it->symbol()->stopRender( context );
272 
273  // cleanup mTempSymbols
274  QHash<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
275  for ( ; it2 != mTempSymbols.end(); ++it2 )
276  {
277  it2.value()->stopRender( context );
278  delete it2.value();
279  }
280  mTempSymbols.clear();
281 }
282 
284 {
285  QSet<QString> attributes;
286 
287  // mAttrName can contain either attribute name or an expression.
288  // Sometimes it is not possible to distinguish between those two,
289  // e.g. "a - b" can be both a valid attribute name or expression.
290  // Since we do not have access to fields here, try both options.
291  attributes << mAttrName;
292 
293  QgsExpression testExpr( mAttrName );
294  if ( !testExpr.hasParserError() )
295  attributes.unite( testExpr.referencedColumns().toSet() );
296 
297  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
298  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
299 
300  QgsRangeList::const_iterator range_it = mRanges.constBegin();
301  for ( ; range_it != mRanges.constEnd(); ++range_it )
302  {
303  QgsSymbolV2* symbol = range_it->symbol();
304  if ( symbol )
305  {
306  attributes.unite( symbol->usedAttributes() );
307  }
308  }
309  return attributes.toList();
310 }
311 
313 {
314  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
315  return false;
316  mRanges[rangeIndex].setSymbol( symbol );
317  return true;
318 }
319 
320 bool QgsGraduatedSymbolRendererV2::updateRangeLabel( int rangeIndex, QString label )
321 {
322  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
323  return false;
324  mRanges[rangeIndex].setLabel( label );
325  return true;
326 }
327 
328 bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double value )
329 {
330  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
331  return false;
332  mRanges[rangeIndex].setUpperValue( value );
333  return true;
334 }
335 
336 bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double value )
337 {
338  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
339  return false;
340  mRanges[rangeIndex].setLowerValue( value );
341  return true;
342 }
343 
345 {
346  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
347  return false;
348  mRanges[rangeIndex].setRenderState( value );
349  return true;
350 }
351 
353 {
354  QString s = QString( "GRADUATED: attr %1\n" ).arg( mAttrName );
355  for ( int i = 0; i < mRanges.count(); i++ )
356  s += mRanges[i].dump();
357  return s;
358 }
359 
361 {
363  r->setMode( mMode );
364  if ( mSourceSymbol.data() )
365  r->setSourceSymbol( mSourceSymbol->clone() );
366  if ( mSourceColorRamp.data() )
367  {
368  r->setSourceColorRamp( mSourceColorRamp->clone() );
370  }
374  r->setScaleMethod( scaleMethod() );
375  return r;
376 }
377 
378 void QgsGraduatedSymbolRendererV2::toSld( QDomDocument& doc, QDomElement &element ) const
379 {
380  QgsStringMap props;
381  props[ "attribute" ] = mAttrName;
382  if ( mRotation.data() )
383  props[ "angle" ] = mRotation->expression();
384  if ( mSizeScale.data() )
385  props[ "scale" ] = mSizeScale->expression();
386 
387  // create a Rule for each range
388  for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
389  {
390  QgsStringMap catProps( props );
391  it->toSld( doc, element, catProps );
392  }
393 }
394 
396 {
397  QgsSymbolV2List lst;
398  for ( int i = 0; i < mRanges.count(); i++ )
399  lst.append( mRanges[i].symbol() );
400  return lst;
401 }
402 
403 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
404 {
405 
406  // Equal interval algorithm
407  //
408  // Returns breaks based on dividing the range ('minimum' to 'maximum')
409  // into 'classes' parts.
410 
411  double step = ( maximum - minimum ) / classes;
412 
413  QList<double> breaks;
414  double value = minimum;
415  for ( int i = 0; i < classes; i++ )
416  {
417  value += step;
418  breaks.append( value );
419  }
420 
421  // floating point arithmetics is not precise:
422  // set the last break to be exactly maximum so we do not miss it
423  breaks[classes-1] = maximum;
424 
425  return breaks;
426 }
427 
428 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
429 {
430  // q-th quantile of a data set:
431  // value where q fraction of data is below and (1-q) fraction is above this value
432  // Xq = (1 - r) * X_NI1 + r * X_NI2
433  // NI1 = (int) (q * (n+1))
434  // NI2 = NI1 + 1
435  // r = q * (n+1) - (int) (q * (n+1))
436  // (indices of X: 1...n)
437 
438  // sort the values first
439  qSort( values );
440 
441  QList<double> breaks;
442 
443  // If there are no values to process: bail out
444  if ( !values.count() )
445  return breaks;
446 
447  int n = values.count();
448  double Xq = n > 0 ? values[0] : 0.0;
449 
450  for ( int i = 1; i < classes; i++ )
451  {
452  if ( n > 1 )
453  {
454  double q = i / ( double ) classes;
455  double a = q * ( n - 1 );
456  int aa = ( int )( a );
457 
458  double r = a - aa;
459  Xq = ( 1 - r ) * values[aa] + r * values[aa+1];
460  }
461  breaks.append( Xq );
462  }
463 
464  breaks.append( values[ n-1 ] );
465 
466  return breaks;
467 }
468 
469 static QList<double> _calcPrettyBreaks( double minimum, double maximum, int classes )
470 {
471 
472  // C++ implementation of R's pretty algorithm
473  // Based on code for determining optimal tick placement for statistical graphics
474  // from the R statistical programming language.
475  // Code ported from R implementation from 'labeling' R package
476  //
477  // Computes a sequence of about 'classes' equally spaced round values
478  // which cover the range of values from 'minimum' to 'maximum'.
479  // The values are chosen so that they are 1, 2 or 5 times a power of 10.
480 
481  QList<double> breaks;
482  if ( classes < 1 )
483  {
484  breaks.append( maximum );
485  return breaks;
486  }
487 
488  int minimumCount = ( int ) classes / 3;
489  double shrink = 0.75;
490  double highBias = 1.5;
491  double adjustBias = 0.5 + 1.5 * highBias;
492  int divisions = classes;
493  double h = highBias;
494  double cell;
495  int U;
496  bool small = false;
497  double dx = maximum - minimum;
498 
499  if ( dx == 0 && maximum == 0 )
500  {
501  cell = 1.0;
502  small = true;
503  U = 1;
504  }
505  else
506  {
507  cell = qMax( qAbs( minimum ), qAbs( maximum ) );
508  if ( adjustBias >= 1.5 * h + 0.5 )
509  {
510  U = 1 + ( 1.0 / ( 1 + h ) );
511  }
512  else
513  {
514  U = 1 + ( 1.5 / ( 1 + adjustBias ) );
515  }
516  small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 );
517  }
518 
519  if ( small )
520  {
521  if ( cell > 10 )
522  {
523  cell = 9 + cell / 10;
524  cell = cell * shrink;
525  }
526  if ( minimumCount > 1 )
527  {
528  cell = cell / minimumCount;
529  }
530  }
531  else
532  {
533  cell = dx;
534  if ( divisions > 1 )
535  {
536  cell = cell / divisions;
537  }
538  }
539  if ( cell < 20 * 1e-07 )
540  {
541  cell = 20 * 1e-07;
542  }
543 
544  double base = pow( 10.0, floor( log10( cell ) ) );
545  double unit = base;
546  if (( 2 * base ) - cell < h *( cell - unit ) )
547  {
548  unit = 2.0 * base;
549  if (( 5 * base ) - cell < adjustBias *( cell - unit ) )
550  {
551  unit = 5.0 * base;
552  if (( 10.0 * base ) - cell < h *( cell - unit ) )
553  {
554  unit = 10.0 * base;
555  }
556  }
557  }
558  // Maybe used to correct for the epsilon here??
559  int start = floor( minimum / unit + 1e-07 );
560  int end = ceil( maximum / unit - 1e-07 );
561 
562  // Extend the range out beyond the data. Does this ever happen??
563  while ( start * unit > minimum + ( 1e-07 * unit ) )
564  {
565  start = start - 1;
566  }
567  while ( end * unit < maximum - ( 1e-07 * unit ) )
568  {
569  end = end + 1;
570  }
571  QgsDebugMsg( QString( "pretty classes: %1" ).arg( end ) );
572 
573  // If we don't have quite enough labels, extend the range out
574  // to make more (these labels are beyond the data :( )
575  int k = floor( 0.5 + end - start );
576  if ( k < minimumCount )
577  {
578  k = minimumCount - k;
579  if ( start >= 0 )
580  {
581  end = end + k / 2;
582  start = start - k / 2 + k % 2;
583  }
584  else
585  {
586  start = start - k / 2;
587  end = end + k / 2 + k % 2;
588  }
589  }
590  double minimumBreak = start * unit;
591  //double maximumBreak = end * unit;
592  int count = end - start;
593 
594  for ( int i = 1; i < count + 1; i++ )
595  {
596  breaks.append( minimumBreak + i * unit );
597  }
598 
599  if ( breaks.isEmpty() )
600  return breaks;
601 
602  if ( breaks.first() < minimum )
603  {
604  breaks[0] = minimum;
605  }
606  if ( breaks.last() > maximum )
607  {
608  breaks[breaks.count()-1] = maximum;
609  }
610 
611  return breaks;
612 } // _calcPrettyBreaks
613 
614 
615 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<int> &labels )
616 {
617 
618  // C++ implementation of the standard deviation class interval algorithm
619  // as implemented in the 'classInt' package available for the R statistical
620  // prgramming language.
621 
622  // Returns breaks based on '_calcPrettyBreaks' of the centred and scaled
623  // values of 'values', and may have a number of classes different from 'classes'.
624 
625  // If there are no values to process: bail out
626  if ( !values.count() )
627  return QList<double>();
628 
629  double mean = 0.0;
630  double stdDev = 0.0;
631  int n = values.count();
632  double minimum = values[0];
633  double maximum = values[0];
634 
635  for ( int i = 0; i < n; i++ )
636  {
637  mean += values[i];
638  minimum = qMin( values[i], minimum ); // could use precomputed max and min
639  maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
640  }
641  mean = mean / ( double ) n;
642 
643  double sd = 0.0;
644  for ( int i = 0; i < n; i++ )
645  {
646  sd = values[i] - mean;
647  stdDev += sd * sd;
648  }
649  stdDev = sqrt( stdDev / n );
650 
651  QList<double> breaks = _calcPrettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
652  for ( int i = 0; i < breaks.count(); i++ )
653  {
654  labels.append(( int ) breaks[i] );
655  breaks[i] = ( breaks[i] * stdDev ) + mean;
656  }
657 
658  return breaks;
659 } // _calcStdDevBreaks
660 
661 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
662  double minimum, double maximum,
663  int maximumSize = 1000 )
664 {
665  // Jenks Optimal (Natural Breaks) algorithm
666  // Based on the Jenks algorithm from the 'classInt' package available for
667  // the R statistical prgramming language, and from Python code from here:
668  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
669  // and is based on a JAVA and Fortran code available here:
670  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
671 
672  // Returns class breaks such that classes are internally homogeneous while
673  // assuring heterogeneity among classes.
674 
675  if ( !values.count() )
676  return QList<double>();
677 
678  if ( classes <= 1 )
679  {
680  return QList<double>() << maximum;
681  }
682 
683  if ( classes >= values.size() )
684  {
685  return values;
686  }
687 
688  QVector<double> sample;
689 
690  // if we have lots of values, we need to take a random sample
691  if ( values.size() > maximumSize )
692  {
693  // for now, sample at least maximumSize values or a 10% sample, whichever
694  // is larger. This will produce a more representative sample for very large
695  // layers, but could end up being computationally intensive...
696 
697  qsrand( time( 0 ) );
698 
699  sample.resize( qMax( maximumSize, values.size() / 10 ) );
700 
701  QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
702  QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
703 
704  sample[ 0 ] = minimum;
705  sample[ 1 ] = maximum;;
706  for ( int i = 2; i < sample.size(); i++ )
707  {
708  // pick a random integer from 0 to n
709  double r = qrand();
710  int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
711  sample[ i ] = values[ j ];
712  }
713  }
714  else
715  {
716  sample = values.toVector();
717  }
718 
719  int n = sample.size();
720 
721  // sort the sample values
722  qSort( sample );
723 
724  QVector< QVector<int> > matrixOne( n + 1 );
725  QVector< QVector<double> > matrixTwo( n + 1 );
726 
727  for ( int i = 0; i <= n; i++ )
728  {
729  matrixOne[i].resize( classes + 1 );
730  matrixTwo[i].resize( classes + 1 );
731  }
732 
733  for ( int i = 1; i <= classes; i++ )
734  {
735  matrixOne[0][i] = 1;
736  matrixOne[1][i] = 1;
737  matrixTwo[0][i] = 0.0;
738  for ( int j = 2; j <= n; j++ )
739  {
740  matrixTwo[j][i] = std::numeric_limits<double>::max();
741  }
742  }
743 
744  for ( int l = 2; l <= n; l++ )
745  {
746  double s1 = 0.0;
747  double s2 = 0.0;
748  int w = 0;
749 
750  double v = 0.0;
751 
752  for ( int m = 1; m <= l; m++ )
753  {
754  int i3 = l - m + 1;
755 
756  double val = sample[ i3 - 1 ];
757 
758  s2 += val * val;
759  s1 += val;
760  w++;
761 
762  v = s2 - ( s1 * s1 ) / ( double ) w;
763  int i4 = i3 - 1;
764  if ( i4 != 0 )
765  {
766  for ( int j = 2; j <= classes; j++ )
767  {
768  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
769  {
770  matrixOne[l][j] = i4;
771  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
772  }
773  }
774  }
775  }
776  matrixOne[l][1] = 1;
777  matrixTwo[l][1] = v;
778  }
779 
780  QVector<double> breaks( classes );
781  breaks[classes-1] = sample[n-1];
782 
783  for ( int j = classes, k = n; j >= 2; j-- )
784  {
785  int id = matrixOne[k][j] - 1;
786  breaks[j - 2] = sample[id];
787  k = matrixOne[k][j] - 1;
788  }
789 
790  return breaks.toList();
791 } //_calcJenksBreaks
792 
793 
795  QgsVectorLayer* vlayer,
796  QString attrName,
797  int classes,
798  Mode mode,
799  QgsSymbolV2* symbol,
800  QgsVectorColorRampV2* ramp,
801  bool inverted )
802 {
803  if ( classes < 1 )
804  return NULL;
805 
806  int attrNum = vlayer->fieldNameIndex( attrName );
807  double minimum;
808  double maximum;
809 
810  QScopedPointer<QgsExpression> expression;
811 
812  if ( attrNum == -1 )
813  {
814  // try to use expression
815  expression.reset( new QgsExpression( attrName ) );
816  if ( expression->hasParserError() || !expression->prepare( vlayer->pendingFields() ) )
817  return 0; // should have a means to report errors
818 
819  QList<double> values;
820  QgsFeatureIterator fit = vlayer->getFeatures();
821  QgsFeature feature;
822  while ( fit.nextFeature( feature ) )
823  {
824  values << expression->evaluate( feature ).toDouble();
825  }
826  qSort( values );
827  minimum = values.first();
828  maximum = values.last();
829  }
830  else
831  {
832  minimum = vlayer->minimumValue( attrNum ).toDouble();
833  maximum = vlayer->maximumValue( attrNum ).toDouble();
834  }
835 
836  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
837  QList<double> breaks;
838  QList<int> labels;
839  if ( mode == EqualInterval )
840  {
841  breaks = _calcEqualIntervalBreaks( minimum, maximum, classes );
842  }
843  else if ( mode == Pretty )
844  {
845  breaks = _calcPrettyBreaks( minimum, maximum, classes );
846  }
847  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
848  {
849  // get values from layer
850  QList<double> values;
851  QgsFeature f;
852  QStringList lst;
853  if ( expression.isNull() )
854  lst.append( attrName );
855  else
856  lst = expression->referencedColumns();
857 
858  QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( lst, vlayer->pendingFields() ) );
859 
860  // create list of non-null attribute values
861  while ( fit.nextFeature( f ) )
862  {
863  QVariant v = expression.isNull() ? f.attribute( attrNum ) : expression->evaluate( f );
864  if ( !v.isNull() )
865  values.append( v.toDouble() );
866  }
867 
868  // calculate the breaks
869  if ( mode == Quantile )
870  {
871  breaks = _calcQuantileBreaks( values, classes );
872  }
873  else if ( mode == Jenks )
874  {
875  breaks = _calcJenksBreaks( values, classes, minimum, maximum );
876  }
877  else if ( mode == StdDev )
878  {
879  breaks = _calcStdDevBreaks( values, classes, labels );
880  }
881  }
882  else
883  {
884  Q_ASSERT( false );
885  }
886 
888  double lower, upper = minimum;
889  QString label;
890 
891  // "breaks" list contains all values at class breaks plus maximum as last break
892  int i = 0;
893  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
894  {
895  lower = upper; // upper border from last interval
896  upper = *it;
897  if ( mode == StdDev )
898  {
899  if ( i == 0 )
900  {
901  label = "< " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
902  }
903  else if ( i == labels.count() - 1 )
904  {
905  label = ">= " + QString::number( labels[i-1], 'i', 0 ) + " Std Dev";
906  }
907  else
908  {
909  label = QString::number( labels[i-1], 'i', 0 ) + " Std Dev" + " - " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
910  }
911  }
912  else
913  {
914  label = QString::number( lower, 'f', 4 ) + " - " + QString::number( upper, 'f', 4 );
915  }
916 
917  QgsSymbolV2* newSymbol = symbol->clone();
918  double colorValue;
919  if ( inverted ) colorValue = ( breaks.count() > 1 ? ( double )( breaks.count() - i - 1 ) / ( breaks.count() - 1 ) : 0 );
920  else colorValue = ( breaks.count() > 1 ? ( double ) i / ( breaks.count() - 1 ) : 0 );
921  newSymbol->setColor( ramp->color( colorValue ) ); // color from (0 / cl-1) to (cl-1 / cl-1)
922 
923  ranges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
924  }
925 
926  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
927  r->setSourceSymbol( symbol->clone() );
928  r->setSourceColorRamp( ramp->clone() );
929  r->setInvertedColorRamp( inverted );
930  r->setMode( mode );
931  return r;
932 }
933 
935 {
936  QDomElement symbolsElem = element.firstChildElement( "symbols" );
937  if ( symbolsElem.isNull() )
938  return NULL;
939 
940  QDomElement rangesElem = element.firstChildElement( "ranges" );
941  if ( rangesElem.isNull() )
942  return NULL;
943 
944  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
946 
947  QDomElement rangeElem = rangesElem.firstChildElement();
948  while ( !rangeElem.isNull() )
949  {
950  if ( rangeElem.tagName() == "range" )
951  {
952  double lowerValue = rangeElem.attribute( "lower" ).toDouble();
953  double upperValue = rangeElem.attribute( "upper" ).toDouble();
954  QString symbolName = rangeElem.attribute( "symbol" );
955  QString label = rangeElem.attribute( "label" );
956  if ( symbolMap.contains( symbolName ) )
957  {
958  QgsSymbolV2* symbol = symbolMap.take( symbolName );
959  ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label ) );
960  }
961  }
962  rangeElem = rangeElem.nextSiblingElement();
963  }
964 
965  QString attrName = element.attribute( "attr" );
966 
967  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
968 
969  // delete symbols if there are any more
971 
972  // try to load source symbol (optional)
973  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
974  if ( !sourceSymbolElem.isNull() )
975  {
976  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
977  if ( sourceSymbolMap.contains( "0" ) )
978  {
979  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
980  }
981  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
982  }
983 
984  // try to load color ramp (optional)
985  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
986  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
987  {
988  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
989  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
990  if ( !invertedColorRampElem.isNull() )
991  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
992  }
993 
994  // try to load mode
995  QDomElement modeElem = element.firstChildElement( "mode" );
996  if ( !modeElem.isNull() )
997  {
998  QString modeString = modeElem.attribute( "name" );
999  if ( modeString == "equal" )
1000  r->setMode( EqualInterval );
1001  else if ( modeString == "quantile" )
1002  r->setMode( Quantile );
1003  else if ( modeString == "jenks" )
1004  r->setMode( Jenks );
1005  else if ( modeString == "stddev" )
1006  r->setMode( StdDev );
1007  else if ( modeString == "pretty" )
1008  r->setMode( Pretty );
1009  }
1010 
1011  QDomElement rotationElem = element.firstChildElement( "rotation" );
1012  if ( !rotationElem.isNull() )
1013  r->setRotationField( rotationElem.attribute( "field" ) );
1014 
1015  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
1016  if ( !sizeScaleElem.isNull() )
1017  r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
1018  r->setScaleMethod( QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ) );
1019 
1020  // TODO: symbol levels
1021  return r;
1022 }
1023 
1024 QDomElement QgsGraduatedSymbolRendererV2::save( QDomDocument& doc )
1025 {
1026  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1027  rendererElem.setAttribute( "type", "graduatedSymbol" );
1028  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
1029  rendererElem.setAttribute( "attr", mAttrName );
1030 
1031  // ranges
1032  int i = 0;
1034  QDomElement rangesElem = doc.createElement( "ranges" );
1035  QgsRangeList::const_iterator it = mRanges.constBegin();
1036  for ( ; it != mRanges.constEnd(); ++it )
1037  {
1038  const QgsRendererRangeV2& range = *it;
1039  QString symbolName = QString::number( i );
1040  symbols.insert( symbolName, range.symbol() );
1041 
1042  QDomElement rangeElem = doc.createElement( "range" );
1043  rangeElem.setAttribute( "lower", QString::number( range.lowerValue(), 'f' ) );
1044  rangeElem.setAttribute( "upper", QString::number( range.upperValue(), 'f' ) );
1045  rangeElem.setAttribute( "symbol", symbolName );
1046  rangeElem.setAttribute( "label", range.label() );
1047  rangeElem.setAttribute( "render", range.renderState() ? "true" : "false" );
1048  rangesElem.appendChild( rangeElem );
1049  i++;
1050  }
1051 
1052  rendererElem.appendChild( rangesElem );
1053 
1054  // save symbols
1055  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
1056  rendererElem.appendChild( symbolsElem );
1057 
1058  // save source symbol
1059  if ( mSourceSymbol.data() )
1060  {
1061  QgsSymbolV2Map sourceSymbols;
1062  sourceSymbols.insert( "0", mSourceSymbol.data() );
1063  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
1064  rendererElem.appendChild( sourceSymbolElem );
1065  }
1066 
1067  // save source color ramp
1068  if ( mSourceColorRamp.data() )
1069  {
1070  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
1071  rendererElem.appendChild( colorRampElem );
1072  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
1073  invertedElem.setAttribute( "value", mInvertedColorRamp );
1074  rendererElem.appendChild( invertedElem );
1075  }
1076 
1077  // save mode
1078  QString modeString;
1079  if ( mMode == EqualInterval )
1080  modeString = "equal";
1081  else if ( mMode == Quantile )
1082  modeString = "quantile";
1083  else if ( mMode == Jenks )
1084  modeString = "jenks";
1085  else if ( mMode == StdDev )
1086  modeString = "stddev";
1087  else if ( mMode == Pretty )
1088  modeString = "pretty";
1089  if ( !modeString.isEmpty() )
1090  {
1091  QDomElement modeElem = doc.createElement( "mode" );
1092  modeElem.setAttribute( "name", modeString );
1093  rendererElem.appendChild( modeElem );
1094  }
1095 
1096  QDomElement rotationElem = doc.createElement( "rotation" );
1097  if ( mRotation.data() )
1098  rotationElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) );
1099  rendererElem.appendChild( rotationElem );
1100 
1101  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
1102  if ( mSizeScale.data() )
1103  sizeScaleElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mSizeScale.data() ) );
1104  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
1105  rendererElem.appendChild( sizeScaleElem );
1106 
1107  return rendererElem;
1108 }
1109 
1111 {
1112  QSettings settings;
1113  bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
1114 
1116  if ( showClassifiers )
1117  {
1118  lst << qMakePair( classAttribute(), QPixmap() );
1119  }
1120 
1121  int count = ranges().count();
1122  for ( int i = 0; i < count; i++ )
1123  {
1124  const QgsRendererRangeV2& range = ranges()[i];
1125  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
1126  lst << qMakePair( range.label(), pix );
1127  }
1128  return lst;
1129 }
1130 
1132 {
1133  Q_UNUSED( scaleDenominator );
1134  QSettings settings;
1135  bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
1136 
1137  QgsLegendSymbolList lst;
1138  if ( showClassifiers )
1139  {
1140  lst << qMakePair( classAttribute(), ( QgsSymbolV2* )0 );
1141  }
1142 
1143  foreach ( const QgsRendererRangeV2& range, mRanges )
1144  {
1145  if ( rule.isEmpty() || range.label() == rule )
1146  {
1147  lst << qMakePair( range.label(), range.symbol() );
1148  }
1149  }
1150  return lst;
1151 }
1152 
1154 {
1155  return mSourceSymbol.data();
1156 }
1158 {
1159  mSourceSymbol.reset( sym );
1160 }
1161 
1163 {
1164  return mSourceColorRamp.data();
1165 }
1166 
1168 {
1169  mSourceColorRamp.reset( ramp );
1170 }
1171 
1173 {
1174  int i = 0;
1175  foreach ( QgsRendererRangeV2 range, mRanges )
1176  {
1177  QgsSymbolV2* symbol = range.symbol()->clone();
1178  double colorValue;
1179  if ( inverted ) colorValue = ( mRanges.count() > 1 ? ( double )( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
1180  else colorValue = ( mRanges.count() > 1 ? ( double ) i / ( mRanges.count() - 1 ) : 0 );
1181  symbol->setColor( ramp->color( colorValue ) );
1182  updateRangeSymbol( i, symbol );
1183  ++i;
1184  }
1185  this->setSourceColorRamp( ramp );
1186  this->setInvertedColorRamp( inverted );
1187 }
1188 
1190 {
1191  int i = 0;
1192  foreach ( QgsRendererRangeV2 range, mRanges )
1193  {
1194  QgsSymbolV2* symbol = sym->clone();
1195  symbol->setColor( range.symbol()->color() );
1196  updateRangeSymbol( i, symbol );
1197  ++i;
1198  }
1199  this->setSourceSymbol( sym->clone() );
1200 }
1201 
1202 void QgsGraduatedSymbolRendererV2::setRotationField( QString fieldOrExpression )
1203 {
1205 }
1206 
1208 {
1209  return mRotation.data() ? QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) : QString();
1210 }
1211 
1212 void QgsGraduatedSymbolRendererV2::setSizeScaleField( QString fieldOrExpression )
1213 {
1215 }
1216 
1218 {
1220 }
1221 
1223 {
1225  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1226  {
1227  setScaleMethodToSymbol( it->symbol(), scaleMethod );
1228  }
1229 }
1230 
1232 {
1233  return true;
1234 }
1235 
1237 {
1238  if ( index >= 0 && index < mRanges.size() )
1239  return mRanges[ index ].renderState();
1240  else
1241  return true;
1242 }
1243 
1245 {
1246  updateRangeRenderState( index, state );
1247 }
1248 
1249 
1251 {
1252  QgsSymbolV2* newSymbol = symbol->clone();
1253  QString label = "0.0 - 0.0";
1254  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1255 
1256 }
1257 
1259 {
1260  mRanges.append( range );
1261 }
1262 
1264 {
1265  mRanges.removeAt( idx );
1266 }
1267 
1269 {
1270  mRanges.clear();
1271 }
1272 
1274 {
1275  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() ) return;
1276  mRanges.move( from, to );
1277 }
1278 
1280 {
1281  return r1.lowerValue() < r2.lowerValue();
1282 }
1283 
1285 {
1286  return !valueLessThan( r1, r2 );
1287 }
1288 
1290 {
1291  QgsDebugMsg( "Entered" );
1292  if ( order == Qt::AscendingOrder )
1293  {
1294  qSort( mRanges.begin(), mRanges.end(), valueLessThan );
1295  }
1296  else
1297  {
1298  qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1299  }
1300 }
1301 
1303 {
1304  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1305 }
1306 
1308 {
1309  return !labelLessThan( r1, r2 );
1310 }
1311 
1313 {
1314  if ( order == Qt::AscendingOrder )
1315  {
1316  qSort( mRanges.begin(), mRanges.end(), labelLessThan );
1317  }
1318  else
1319  {
1320  qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1321  }
1322 }
1323