QGIS API Documentation  2.8.2-Wien
 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 /***************************************************************************
3  qgsgraduatedsymbolrendererv2.cpp
4  ---------------------
5  begin : November 2009
6  copyright : (C) 2009 by Martin Dobias
7  email : wonder dot sk at gmail dot com
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
17 
18 #include "qgssymbolv2.h"
19 #include "qgssymbollayerv2utils.h"
20 #include "qgsvectorcolorrampv2.h"
23 
24 #include "qgsfeature.h"
25 #include "qgsvectorlayer.h"
26 #include "qgslogger.h"
27 #include "qgsvectordataprovider.h"
28 #include "qgsexpression.h"
29 #include <QDomDocument>
30 #include <QDomElement>
31 #include <QSettings> // for legend
32 #include <limits> // for jenks classification
33 #include <cmath> // for pretty classification
34 #include <ctime>
35 
37  : mLowerValue( 0 )
38  , mUpperValue( 0 )
39  , mSymbol( 0 )
40  , mLabel()
41  , mRender( true )
42 {
43 }
44 
45 QgsRendererRangeV2::QgsRendererRangeV2( double lowerValue, double upperValue, QgsSymbolV2* symbol, QString label, bool render )
46  : mLowerValue( lowerValue )
47  , mUpperValue( upperValue )
48  , mSymbol( symbol )
49  , mLabel( label )
50  , mRender( render )
51 {
52 }
53 
55  : mLowerValue( range.mLowerValue )
56  , mUpperValue( range.mUpperValue )
57  , mSymbol( range.mSymbol.data() ? range.mSymbol->clone() : NULL )
58  , mLabel( range.mLabel )
59  , mRender( range.mRender )
60 {
61 }
62 
63 // cpy and swap idiom, note that the cpy is done with 'pass by value'
65 {
66  swap( range );
67  return *this;
68 }
69 
71 {
72  return
73  lowerValue() < other.lowerValue() ||
74  ( lowerValue() == other.lowerValue() && upperValue() < other.upperValue() );
75 }
76 
77 
79 {
80  qSwap( mLowerValue, other.mLowerValue );
81  qSwap( mUpperValue, other.mUpperValue );
82  qSwap( mSymbol, other.mSymbol );
83  std::swap( mLabel, other.mLabel );
84 }
85 
87 {
88  return mLowerValue;
89 }
90 
92 {
93  return mUpperValue;
94 }
95 
97 {
98  return mSymbol.data();
99 }
100 
102 {
103  return mLabel;
104 }
105 
107 {
108  if ( mSymbol.data() != s ) mSymbol.reset( s );
109 }
110 
111 void QgsRendererRangeV2::setLabel( QString label )
112 {
113  mLabel = label;
114 }
115 
116 void QgsRendererRangeV2::setUpperValue( double upperValue )
117 {
119 }
120 
121 void QgsRendererRangeV2::setLowerValue( double lowerValue )
122 {
124 }
125 
127 {
128  return mRender;
129 }
130 
132 {
133  mRender = render;
134 }
135 
137 {
138  return QString( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel ).arg( mSymbol.data() ? mSymbol->dump() : "(no symbol)" );
139 }
140 
141 void QgsRendererRangeV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
142 {
143  if ( !mSymbol.data() || props.value( "attribute", "" ).isEmpty() )
144  return;
145 
146  QString attrName = props[ "attribute" ];
147 
148  QDomElement ruleElem = doc.createElement( "se:Rule" );
149  element.appendChild( ruleElem );
150 
151  QDomElement nameElem = doc.createElement( "se:Name" );
152  nameElem.appendChild( doc.createTextNode( mLabel ) );
153  ruleElem.appendChild( nameElem );
154 
155  QDomElement descrElem = doc.createElement( "se:Description" );
156  QDomElement titleElem = doc.createElement( "se:Title" );
157  QString descrStr = QString( "range: %1 - %2" ).arg( mLowerValue ).arg( mUpperValue );
158  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
159  descrElem.appendChild( titleElem );
160  ruleElem.appendChild( descrElem );
161 
162  // create the ogc:Filter for the range
163  QString filterFunc = QString( "%1 > %2 AND %1 <= %3" )
164  .arg( attrName.replace( "\"", "\"\"" ) )
165  .arg( mLowerValue ).arg( mUpperValue );
166  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, filterFunc );
167 
168  mSymbol->toSld( doc, ruleElem, props );
169 }
170 
172 
175 
177  mFormat( " %1 - %2 " ),
178  mPrecision( 4 ),
179  mTrimTrailingZeroes( false ),
180  mNumberScale( 1.0 ),
181  mNumberSuffix( "" ),
182  mReTrailingZeroes( "[.,]?0*$" ),
183  mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
184 {
185 }
186 
187 QgsRendererRangeV2LabelFormat::QgsRendererRangeV2LabelFormat( QString format, int precision, bool trimTrailingZeroes ):
188  mReTrailingZeroes( "[.,]?0*$" ),
189  mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
190 {
191  setFormat( format );
192  setPrecision( precision );
193  setTrimTrailingZeroes( trimTrailingZeroes );
194 }
195 
196 
198 {
199  return
200  format() == other.format() &&
201  precision() == other.precision() &&
203 }
204 
206 {
207  return !( *this == other );
208 }
209 
211 {
212  // Limit the range of decimal places to a reasonable range
213  precision = qBound( MinPrecision, precision, MaxPrecision );
215  mNumberScale = 1.0;
216  mNumberSuffix = "";
217  while ( precision < 0 )
218  {
219  precision++;
220  mNumberScale /= 10.0;
221  mNumberSuffix.append( '0' );
222  }
223 }
224 
226 {
227  return labelForRange( range.lowerValue(), range.upperValue() );
228 }
229 
230 QString QgsRendererRangeV2LabelFormat::formatNumber( double value ) const
231 {
232  if ( mPrecision > 0 )
233  {
234  QString valueStr = QString::number( value, 'f', mPrecision );
235  if ( mTrimTrailingZeroes )
236  valueStr = valueStr.replace( mReTrailingZeroes, "" );
237  if ( mReNegativeZero.exactMatch( valueStr ) )
238  valueStr = valueStr.mid( 1 );
239  return valueStr;
240  }
241  else
242  {
243  QString valueStr = QString::number( value * mNumberScale, 'f', 0 );
244  if ( valueStr == "-0" )
245  valueStr = "0";
246  if ( valueStr != "0" )
247  valueStr = valueStr + mNumberSuffix;
248  return valueStr;
249  }
250 }
251 
252 QString QgsRendererRangeV2LabelFormat::labelForRange( double lower, double upper ) const
253 {
254  QString lowerStr = formatNumber( lower );
255  QString upperStr = formatNumber( upper );
256 
257  QString legend( mFormat );
258  return legend.replace( "%1", lowerStr ).replace( "%2", upperStr );
259 }
260 
262 {
263  mFormat = element.attribute( "format",
264  element.attribute( "prefix", " " ) + "%1" +
265  element.attribute( "separator", " - " ) + "%2" +
266  element.attribute( "suffix", " " )
267  );
268  setPrecision( element.attribute( "decimalplaces", "4" ).toInt() );
269  mTrimTrailingZeroes = element.attribute( "trimtrailingzeroes", "false" ) == "true";
270 }
271 
273 {
274  element.setAttribute( "format", mFormat );
275  element.setAttribute( "decimalplaces", mPrecision );
276  element.setAttribute( "trimtrailingzeroes", mTrimTrailingZeroes ? "true" : "false" );
277 }
278 
280 
282  : QgsFeatureRendererV2( "graduatedSymbol" )
283  , mAttrName( attrName )
284  , mRanges( ranges )
285  , mMode( Custom )
286  , mInvertedColorRamp( false )
287  , mScaleMethod( DEFAULT_SCALE_METHOD )
288  , mAttrNum( -1 )
289  , mCounting( false )
290 
291 {
292  // TODO: check ranges for sanity (NULL symbols, invalid ranges)
293 }
294 
296 {
297  mRanges.clear(); // should delete all the symbols
298 }
299 
301 {
302  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
303  {
304  if ( it->lowerValue() <= value && it->upperValue() >= value )
305  {
306  if ( it->renderState() || mCounting )
307  return it->symbol();
308  else
309  return NULL;
310  }
311  }
312  // the value is out of the range: return NULL instead of symbol
313  return NULL;
314 }
315 
317 {
318  QgsSymbolV2* symbol = originalSymbolForFeature( feature );
319  if ( symbol == NULL )
320  return NULL;
321 
322  if ( !mRotation.data() && !mSizeScale.data() )
323  return symbol; // no data-defined rotation/scaling - just return the symbol
324 
325  // find out rotation, size scale
326  const double rotation = mRotation.data() ? mRotation->evaluate( feature ).toDouble() : 0;
327  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( feature ).toDouble() : 1.;
328 
329  // take a temporary symbol (or create it if doesn't exist)
330  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
331 
332  // modify the temporary symbol and return it
333  if ( tempSymbol->type() == QgsSymbolV2::Marker )
334  {
335  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
336  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
337  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
338  markerSymbol->setScaleMethod( mScaleMethod );
339  }
340  else if ( tempSymbol->type() == QgsSymbolV2::Line )
341  {
342  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
343  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
344  }
345  return tempSymbol;
346 }
347 
349 {
350  const QgsAttributes& attrs = feature.attributes();
351  QVariant value;
352  if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
353  {
354  value = mExpression->evaluate( &feature );
355  }
356  else
357  {
358  value = attrs[mAttrNum];
359  }
360 
361  // Null values should not be categorized
362  if ( value.isNull() )
363  return NULL;
364 
365  // find the right category
366  return symbolForValue( value.toDouble() );
367 }
368 
370 {
371  mCounting = context.rendererScale() == 0.0;
372 
373  // find out classification attribute index from name
374  mAttrNum = fields.fieldNameIndex( mAttrName );
375 
376  if ( mAttrNum == -1 )
377  {
378  mExpression.reset( new QgsExpression( mAttrName ) );
379  mExpression->prepare( fields );
380  }
381 
382  QgsRangeList::iterator it = mRanges.begin();
383  for ( ; it != mRanges.end(); ++it )
384  {
385  if ( !it->symbol() )
386  continue;
387 
388  it->symbol()->startRender( context, &fields );
389 
390  if ( mRotation.data() || mSizeScale.data() )
391  {
392  QgsSymbolV2* tempSymbol = it->symbol()->clone();
393  tempSymbol->setRenderHints(( mRotation.data() ? QgsSymbolV2::DataDefinedRotation : 0 ) |
395  tempSymbol->startRender( context, &fields );
396  mTempSymbols[ it->symbol()] = tempSymbol;
397  }
398  }
399 }
400 
402 {
403  QgsRangeList::iterator it = mRanges.begin();
404  for ( ; it != mRanges.end(); ++it )
405  {
406  if ( !it->symbol() )
407  continue;
408 
409  it->symbol()->stopRender( context );
410  }
411 
412  // cleanup mTempSymbols
413  QHash<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
414  for ( ; it2 != mTempSymbols.end(); ++it2 )
415  {
416  it2.value()->stopRender( context );
417  delete it2.value();
418  }
419  mTempSymbols.clear();
420 }
421 
423 {
424  QSet<QString> attributes;
425 
426  // mAttrName can contain either attribute name or an expression.
427  // Sometimes it is not possible to distinguish between those two,
428  // e.g. "a - b" can be both a valid attribute name or expression.
429  // Since we do not have access to fields here, try both options.
430  attributes << mAttrName;
431 
432  QgsExpression testExpr( mAttrName );
433  if ( !testExpr.hasParserError() )
434  attributes.unite( testExpr.referencedColumns().toSet() );
435 
436  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
437  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
438 
439  QgsRangeList::const_iterator range_it = mRanges.constBegin();
440  for ( ; range_it != mRanges.constEnd(); ++range_it )
441  {
442  QgsSymbolV2* symbol = range_it->symbol();
443  if ( symbol )
444  {
445  attributes.unite( symbol->usedAttributes() );
446  }
447  }
448  return attributes.toList();
449 }
450 
452 {
453  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
454  return false;
455  mRanges[rangeIndex].setSymbol( symbol );
456  return true;
457 }
458 
459 bool QgsGraduatedSymbolRendererV2::updateRangeLabel( int rangeIndex, QString label )
460 {
461  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
462  return false;
463  mRanges[rangeIndex].setLabel( label );
464  return true;
465 }
466 
467 bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double value )
468 {
469  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
470  return false;
471  QgsRendererRangeV2 &range = mRanges[rangeIndex];
472  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
473  range.setUpperValue( value );
474  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
475  return true;
476 }
477 
478 bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double value )
479 {
480  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
481  return false;
482  QgsRendererRangeV2 &range = mRanges[rangeIndex];
483  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
484  range.setLowerValue( value );
485  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
486  return true;
487 }
488 
490 {
491  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
492  return false;
493  mRanges[rangeIndex].setRenderState( value );
494  return true;
495 }
496 
498 {
499  QString s = QString( "GRADUATED: attr %1\n" ).arg( mAttrName );
500  for ( int i = 0; i < mRanges.count(); i++ )
501  s += mRanges[i].dump();
502  return s;
503 }
504 
506 {
508  r->setMode( mMode );
509  if ( mSourceSymbol.data() )
510  r->setSourceSymbol( mSourceSymbol->clone() );
511  if ( mSourceColorRamp.data() )
512  {
513  r->setSourceColorRamp( mSourceColorRamp->clone() );
515  }
519  r->setScaleMethod( scaleMethod() );
520  r->setLabelFormat( labelFormat() );
521  return r;
522 }
523 
524 void QgsGraduatedSymbolRendererV2::toSld( QDomDocument& doc, QDomElement &element ) const
525 {
526  QgsStringMap props;
527  props[ "attribute" ] = mAttrName;
528  if ( mRotation.data() )
529  props[ "angle" ] = mRotation->expression();
530  if ( mSizeScale.data() )
531  props[ "scale" ] = mSizeScale->expression();
532 
533  // create a Rule for each range
534  for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
535  {
536  QgsStringMap catProps( props );
537  it->toSld( doc, element, catProps );
538  }
539 }
540 
542 {
543  QgsSymbolV2List lst;
544  for ( int i = 0; i < mRanges.count(); i++ )
545  lst.append( mRanges[i].symbol() );
546  return lst;
547 }
548 
549 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
550 {
551 
552  // Equal interval algorithm
553  //
554  // Returns breaks based on dividing the range ('minimum' to 'maximum')
555  // into 'classes' parts.
556 
557  double step = ( maximum - minimum ) / classes;
558 
559  QList<double> breaks;
560  double value = minimum;
561  for ( int i = 0; i < classes; i++ )
562  {
563  value += step;
564  breaks.append( value );
565  }
566 
567  // floating point arithmetics is not precise:
568  // set the last break to be exactly maximum so we do not miss it
569  breaks[classes-1] = maximum;
570 
571  return breaks;
572 }
573 
574 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
575 {
576  // q-th quantile of a data set:
577  // value where q fraction of data is below and (1-q) fraction is above this value
578  // Xq = (1 - r) * X_NI1 + r * X_NI2
579  // NI1 = (int) (q * (n+1))
580  // NI2 = NI1 + 1
581  // r = q * (n+1) - (int) (q * (n+1))
582  // (indices of X: 1...n)
583 
584  // sort the values first
585  qSort( values );
586 
587  QList<double> breaks;
588 
589  // If there are no values to process: bail out
590  if ( !values.count() )
591  return breaks;
592 
593  int n = values.count();
594  double Xq = n > 0 ? values[0] : 0.0;
595 
596  for ( int i = 1; i < classes; i++ )
597  {
598  if ( n > 1 )
599  {
600  double q = i / ( double ) classes;
601  double a = q * ( n - 1 );
602  int aa = ( int )( a );
603 
604  double r = a - aa;
605  Xq = ( 1 - r ) * values[aa] + r * values[aa+1];
606  }
607  breaks.append( Xq );
608  }
609 
610  breaks.append( values[ n-1 ] );
611 
612  return breaks;
613 }
614 
615 static QList<double> _calcPrettyBreaks( double minimum, double maximum, int classes )
616 {
617 
618  // C++ implementation of R's pretty algorithm
619  // Based on code for determining optimal tick placement for statistical graphics
620  // from the R statistical programming language.
621  // Code ported from R implementation from 'labeling' R package
622  //
623  // Computes a sequence of about 'classes' equally spaced round values
624  // which cover the range of values from 'minimum' to 'maximum'.
625  // The values are chosen so that they are 1, 2 or 5 times a power of 10.
626 
627  QList<double> breaks;
628  if ( classes < 1 )
629  {
630  breaks.append( maximum );
631  return breaks;
632  }
633 
634  int minimumCount = ( int ) classes / 3;
635  double shrink = 0.75;
636  double highBias = 1.5;
637  double adjustBias = 0.5 + 1.5 * highBias;
638  int divisions = classes;
639  double h = highBias;
640  double cell;
641  int U;
642  bool small = false;
643  double dx = maximum - minimum;
644 
645  if ( dx == 0 && maximum == 0 )
646  {
647  cell = 1.0;
648  small = true;
649  U = 1;
650  }
651  else
652  {
653  cell = qMax( qAbs( minimum ), qAbs( maximum ) );
654  if ( adjustBias >= 1.5 * h + 0.5 )
655  {
656  U = 1 + ( 1.0 / ( 1 + h ) );
657  }
658  else
659  {
660  U = 1 + ( 1.5 / ( 1 + adjustBias ) );
661  }
662  small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 );
663  }
664 
665  if ( small )
666  {
667  if ( cell > 10 )
668  {
669  cell = 9 + cell / 10;
670  cell = cell * shrink;
671  }
672  if ( minimumCount > 1 )
673  {
674  cell = cell / minimumCount;
675  }
676  }
677  else
678  {
679  cell = dx;
680  if ( divisions > 1 )
681  {
682  cell = cell / divisions;
683  }
684  }
685  if ( cell < 20 * 1e-07 )
686  {
687  cell = 20 * 1e-07;
688  }
689 
690  double base = pow( 10.0, floor( log10( cell ) ) );
691  double unit = base;
692  if (( 2 * base ) - cell < h *( cell - unit ) )
693  {
694  unit = 2.0 * base;
695  if (( 5 * base ) - cell < adjustBias *( cell - unit ) )
696  {
697  unit = 5.0 * base;
698  if (( 10.0 * base ) - cell < h *( cell - unit ) )
699  {
700  unit = 10.0 * base;
701  }
702  }
703  }
704  // Maybe used to correct for the epsilon here??
705  int start = floor( minimum / unit + 1e-07 );
706  int end = ceil( maximum / unit - 1e-07 );
707 
708  // Extend the range out beyond the data. Does this ever happen??
709  while ( start * unit > minimum + ( 1e-07 * unit ) )
710  {
711  start = start - 1;
712  }
713  while ( end * unit < maximum - ( 1e-07 * unit ) )
714  {
715  end = end + 1;
716  }
717  QgsDebugMsg( QString( "pretty classes: %1" ).arg( end ) );
718 
719  // If we don't have quite enough labels, extend the range out
720  // to make more (these labels are beyond the data :( )
721  int k = floor( 0.5 + end - start );
722  if ( k < minimumCount )
723  {
724  k = minimumCount - k;
725  if ( start >= 0 )
726  {
727  end = end + k / 2;
728  start = start - k / 2 + k % 2;
729  }
730  else
731  {
732  start = start - k / 2;
733  end = end + k / 2 + k % 2;
734  }
735  }
736  double minimumBreak = start * unit;
737  //double maximumBreak = end * unit;
738  int count = end - start;
739 
740  for ( int i = 1; i < count + 1; i++ )
741  {
742  breaks.append( minimumBreak + i * unit );
743  }
744 
745  if ( breaks.isEmpty() )
746  return breaks;
747 
748  if ( breaks.first() < minimum )
749  {
750  breaks[0] = minimum;
751  }
752  if ( breaks.last() > maximum )
753  {
754  breaks[breaks.count()-1] = maximum;
755  }
756 
757  return breaks;
758 } // _calcPrettyBreaks
759 
760 
761 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<double> &labels )
762 {
763 
764  // C++ implementation of the standard deviation class interval algorithm
765  // as implemented in the 'classInt' package available for the R statistical
766  // prgramming language.
767 
768  // Returns breaks based on '_calcPrettyBreaks' of the centred and scaled
769  // values of 'values', and may have a number of classes different from 'classes'.
770 
771  // If there are no values to process: bail out
772  if ( !values.count() )
773  return QList<double>();
774 
775  double mean = 0.0;
776  double stdDev = 0.0;
777  int n = values.count();
778  double minimum = values[0];
779  double maximum = values[0];
780 
781  for ( int i = 0; i < n; i++ )
782  {
783  mean += values[i];
784  minimum = qMin( values[i], minimum ); // could use precomputed max and min
785  maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
786  }
787  mean = mean / ( double ) n;
788 
789  double sd = 0.0;
790  for ( int i = 0; i < n; i++ )
791  {
792  sd = values[i] - mean;
793  stdDev += sd * sd;
794  }
795  stdDev = sqrt( stdDev / n );
796 
797  QList<double> breaks = _calcPrettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
798  for ( int i = 0; i < breaks.count(); i++ )
799  {
800  labels.append( breaks[i] );
801  breaks[i] = ( breaks[i] * stdDev ) + mean;
802  }
803 
804  return breaks;
805 } // _calcStdDevBreaks
806 
807 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
808  double minimum, double maximum,
809  int maximumSize = 1000 )
810 {
811  // Jenks Optimal (Natural Breaks) algorithm
812  // Based on the Jenks algorithm from the 'classInt' package available for
813  // the R statistical prgramming language, and from Python code from here:
814  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
815  // and is based on a JAVA and Fortran code available here:
816  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
817 
818  // Returns class breaks such that classes are internally homogeneous while
819  // assuring heterogeneity among classes.
820 
821  if ( !values.count() )
822  return QList<double>();
823 
824  if ( classes <= 1 )
825  {
826  return QList<double>() << maximum;
827  }
828 
829  if ( classes >= values.size() )
830  {
831  return values;
832  }
833 
834  QVector<double> sample;
835 
836  // if we have lots of values, we need to take a random sample
837  if ( values.size() > maximumSize )
838  {
839  // for now, sample at least maximumSize values or a 10% sample, whichever
840  // is larger. This will produce a more representative sample for very large
841  // layers, but could end up being computationally intensive...
842 
843  sample.resize( qMax( maximumSize, values.size() / 10 ) );
844 
845  QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
846  QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
847 
848  sample[ 0 ] = minimum;
849  sample[ 1 ] = maximum;;
850  for ( int i = 2; i < sample.size(); i++ )
851  {
852  // pick a random integer from 0 to n
853  double r = qrand();
854  int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
855  sample[ i ] = values[ j ];
856  }
857  }
858  else
859  {
860  sample = values.toVector();
861  }
862 
863  int n = sample.size();
864 
865  // sort the sample values
866  qSort( sample );
867 
868  QVector< QVector<int> > matrixOne( n + 1 );
869  QVector< QVector<double> > matrixTwo( n + 1 );
870 
871  for ( int i = 0; i <= n; i++ )
872  {
873  matrixOne[i].resize( classes + 1 );
874  matrixTwo[i].resize( classes + 1 );
875  }
876 
877  for ( int i = 1; i <= classes; i++ )
878  {
879  matrixOne[0][i] = 1;
880  matrixOne[1][i] = 1;
881  matrixTwo[0][i] = 0.0;
882  for ( int j = 2; j <= n; j++ )
883  {
884  matrixTwo[j][i] = std::numeric_limits<double>::max();
885  }
886  }
887 
888  for ( int l = 2; l <= n; l++ )
889  {
890  double s1 = 0.0;
891  double s2 = 0.0;
892  int w = 0;
893 
894  double v = 0.0;
895 
896  for ( int m = 1; m <= l; m++ )
897  {
898  int i3 = l - m + 1;
899 
900  double val = sample[ i3 - 1 ];
901 
902  s2 += val * val;
903  s1 += val;
904  w++;
905 
906  v = s2 - ( s1 * s1 ) / ( double ) w;
907  int i4 = i3 - 1;
908  if ( i4 != 0 )
909  {
910  for ( int j = 2; j <= classes; j++ )
911  {
912  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
913  {
914  matrixOne[l][j] = i4;
915  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
916  }
917  }
918  }
919  }
920  matrixOne[l][1] = 1;
921  matrixTwo[l][1] = v;
922  }
923 
924  QVector<double> breaks( classes );
925  breaks[classes-1] = sample[n-1];
926 
927  for ( int j = classes, k = n; j >= 2; j-- )
928  {
929  int id = matrixOne[k][j] - 1;
930  breaks[j - 2] = sample[id];
931  k = matrixOne[k][j] - 1;
932  }
933 
934  return breaks.toList();
935 } //_calcJenksBreaks
936 
937 
939  QgsVectorLayer* vlayer,
940  QString attrName,
941  int classes,
942  Mode mode,
943  QgsSymbolV2* symbol,
944  QgsVectorColorRampV2* ramp,
945  bool inverted,
947 )
948 {
950  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
951  r->setSourceSymbol( symbol->clone() );
952  r->setSourceColorRamp( ramp->clone() );
953  r->setInvertedColorRamp( inverted );
954  r->setMode( mode );
955  r->setLabelFormat( labelFormat );
956  r->updateClasses( vlayer, mode, classes );
957  return r;
958 }
959 
961 {
962  QList<double> values;
963  QScopedPointer<QgsExpression> expression;
964  int attrNum = vlayer->fieldNameIndex( mAttrName );
965 
966  if ( attrNum == -1 )
967  {
968  // try to use expression
969  expression.reset( new QgsExpression( mAttrName ) );
970  if ( expression->hasParserError() || !expression->prepare( vlayer->pendingFields() ) )
971  return values; // should have a means to report errors
972  }
973 
974  QgsFeature f;
975  QStringList lst;
976  if ( expression.isNull() )
977  lst.append( mAttrName );
978  else
979  lst = expression->referencedColumns();
980 
982  .setFlags(( expression && expression->needsGeometry() ) ?
985  .setSubsetOfAttributes( lst, vlayer->pendingFields() ) );
986 
987  // create list of non-null attribute values
988  while ( fit.nextFeature( f ) )
989  {
990  QVariant v = expression ? expression->evaluate( f ) : f.attribute( attrNum );
991  if ( !v.isNull() )
992  values.append( v.toDouble() );
993  }
994  return values;
995 }
996 
998 {
999  if ( mAttrName.isEmpty() )
1000  return;
1001 
1002  setMode( mode );
1003  // Custom classes are not recalculated
1004  if ( mode == Custom )
1005  return;
1006 
1007  if ( nclasses < 1 )
1008  nclasses = 1;
1009 
1010  QList<double> values;
1011  bool valuesLoaded = false;
1012  double minimum;
1013  double maximum;
1014 
1015  int attrNum = vlayer->fieldNameIndex( mAttrName );
1016 
1017  if ( attrNum == -1 )
1018  {
1019  values = getDataValues( vlayer );
1020  if ( values.isEmpty() )
1021  return;
1022 
1023  qSort( values );
1024  minimum = values.first();
1025  maximum = values.last();
1026  valuesLoaded = true;
1027  }
1028  else
1029  {
1030  minimum = vlayer->minimumValue( attrNum ).toDouble();
1031  maximum = vlayer->maximumValue( attrNum ).toDouble();
1032  }
1033 
1034  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
1035  QList<double> breaks;
1036  QList<double> labels;
1037  if ( mode == EqualInterval )
1038  {
1039  breaks = _calcEqualIntervalBreaks( minimum, maximum, nclasses );
1040  }
1041  else if ( mode == Pretty )
1042  {
1043  breaks = _calcPrettyBreaks( minimum, maximum, nclasses );
1044  }
1045  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
1046  {
1047  // get values from layer
1048  if ( !valuesLoaded )
1049  {
1050  values = getDataValues( vlayer );
1051  }
1052 
1053  // calculate the breaks
1054  if ( mode == Quantile )
1055  {
1056  breaks = _calcQuantileBreaks( values, nclasses );
1057  }
1058  else if ( mode == Jenks )
1059  {
1060  breaks = _calcJenksBreaks( values, nclasses, minimum, maximum );
1061  }
1062  else if ( mode == StdDev )
1063  {
1064  breaks = _calcStdDevBreaks( values, nclasses, labels );
1065  }
1066  }
1067  else
1068  {
1069  Q_ASSERT( false );
1070  }
1071 
1072  double lower, upper = minimum;
1073  QString label;
1074  deleteAllClasses();
1075 
1076  // "breaks" list contains all values at class breaks plus maximum as last break
1077 
1078  int i = 0;
1079  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
1080  {
1081  lower = upper; // upper border from last interval
1082  upper = *it;
1083 
1084  // Label - either StdDev label or default label for a range
1085  if ( mode == StdDev )
1086  {
1087  if ( i == 0 )
1088  {
1089  label = "< " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
1090  }
1091  else if ( i == labels.count() - 1 )
1092  {
1093  label = ">= " + QString::number( labels[i-1], 'f', 2 ) + " Std Dev";
1094  }
1095  else
1096  {
1097  label = QString::number( labels[i-1], 'f', 2 ) + " Std Dev" + " - " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
1098  }
1099  }
1100  else
1101  {
1102  label = mLabelFormat.labelForRange( lower, upper );
1103  }
1104  QgsSymbolV2* newSymbol = mSourceSymbol ? mSourceSymbol->clone() : QgsSymbolV2::defaultSymbol( vlayer->geometryType() );
1105  addClass( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
1106  }
1108 }
1109 
1110 
1112 {
1113  QDomElement symbolsElem = element.firstChildElement( "symbols" );
1114  if ( symbolsElem.isNull() )
1115  return NULL;
1116 
1117  QDomElement rangesElem = element.firstChildElement( "ranges" );
1118  if ( rangesElem.isNull() )
1119  return NULL;
1120 
1121  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
1123 
1124  QDomElement rangeElem = rangesElem.firstChildElement();
1125  while ( !rangeElem.isNull() )
1126  {
1127  if ( rangeElem.tagName() == "range" )
1128  {
1129  double lowerValue = rangeElem.attribute( "lower" ).toDouble();
1130  double upperValue = rangeElem.attribute( "upper" ).toDouble();
1131  QString symbolName = rangeElem.attribute( "symbol" );
1132  QString label = rangeElem.attribute( "label" );
1133  bool render = rangeElem.attribute( "render", "true" ) != "false";
1134  if ( symbolMap.contains( symbolName ) )
1135  {
1136  QgsSymbolV2* symbol = symbolMap.take( symbolName );
1137  ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label, render ) );
1138  }
1139  }
1140  rangeElem = rangeElem.nextSiblingElement();
1141  }
1142 
1143  QString attrName = element.attribute( "attr" );
1144 
1145  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
1146 
1147  // delete symbols if there are any more
1149 
1150  // try to load source symbol (optional)
1151  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
1152  if ( !sourceSymbolElem.isNull() )
1153  {
1154  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
1155  if ( sourceSymbolMap.contains( "0" ) )
1156  {
1157  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
1158  }
1159  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
1160  }
1161 
1162  // try to load color ramp (optional)
1163  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
1164  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
1165  {
1166  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
1167  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
1168  if ( !invertedColorRampElem.isNull() )
1169  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
1170  }
1171 
1172  // try to load mode
1173  QDomElement modeElem = element.firstChildElement( "mode" );
1174  if ( !modeElem.isNull() )
1175  {
1176  QString modeString = modeElem.attribute( "name" );
1177  if ( modeString == "equal" )
1178  r->setMode( EqualInterval );
1179  else if ( modeString == "quantile" )
1180  r->setMode( Quantile );
1181  else if ( modeString == "jenks" )
1182  r->setMode( Jenks );
1183  else if ( modeString == "stddev" )
1184  r->setMode( StdDev );
1185  else if ( modeString == "pretty" )
1186  r->setMode( Pretty );
1187  }
1188 
1189  QDomElement rotationElem = element.firstChildElement( "rotation" );
1190  if ( !rotationElem.isNull() )
1191  r->setRotationField( rotationElem.attribute( "field" ) );
1192 
1193  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
1194  if ( !sizeScaleElem.isNull() )
1195  r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
1196  r->setScaleMethod( QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ) );
1197 
1198  QDomElement labelFormatElem = element.firstChildElement( "labelformat" );
1199  if ( ! labelFormatElem.isNull() )
1200  {
1202  labelFormat.setFromDomElement( labelFormatElem );
1203  r->setLabelFormat( labelFormat );
1204  }
1205  // TODO: symbol levels
1206  return r;
1207 }
1208 
1209 QDomElement QgsGraduatedSymbolRendererV2::save( QDomDocument& doc )
1210 {
1211  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1212  rendererElem.setAttribute( "type", "graduatedSymbol" );
1213  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
1214  rendererElem.setAttribute( "attr", mAttrName );
1215 
1216  // ranges
1217  int i = 0;
1219  QDomElement rangesElem = doc.createElement( "ranges" );
1220  QgsRangeList::const_iterator it = mRanges.constBegin();
1221  for ( ; it != mRanges.constEnd(); ++it )
1222  {
1223  const QgsRendererRangeV2& range = *it;
1224  QString symbolName = QString::number( i );
1225  symbols.insert( symbolName, range.symbol() );
1226 
1227  QDomElement rangeElem = doc.createElement( "range" );
1228  rangeElem.setAttribute( "lower", QString::number( range.lowerValue(), 'f' ) );
1229  rangeElem.setAttribute( "upper", QString::number( range.upperValue(), 'f' ) );
1230  rangeElem.setAttribute( "symbol", symbolName );
1231  rangeElem.setAttribute( "label", range.label() );
1232  rangeElem.setAttribute( "render", range.renderState() ? "true" : "false" );
1233  rangesElem.appendChild( rangeElem );
1234  i++;
1235  }
1236 
1237  rendererElem.appendChild( rangesElem );
1238 
1239  // save symbols
1240  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
1241  rendererElem.appendChild( symbolsElem );
1242 
1243  // save source symbol
1244  if ( mSourceSymbol.data() )
1245  {
1246  QgsSymbolV2Map sourceSymbols;
1247  sourceSymbols.insert( "0", mSourceSymbol.data() );
1248  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
1249  rendererElem.appendChild( sourceSymbolElem );
1250  }
1251 
1252  // save source color ramp
1253  if ( mSourceColorRamp.data() )
1254  {
1255  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
1256  rendererElem.appendChild( colorRampElem );
1257  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
1258  invertedElem.setAttribute( "value", mInvertedColorRamp );
1259  rendererElem.appendChild( invertedElem );
1260  }
1261 
1262  // save mode
1263  QString modeString;
1264  if ( mMode == EqualInterval )
1265  modeString = "equal";
1266  else if ( mMode == Quantile )
1267  modeString = "quantile";
1268  else if ( mMode == Jenks )
1269  modeString = "jenks";
1270  else if ( mMode == StdDev )
1271  modeString = "stddev";
1272  else if ( mMode == Pretty )
1273  modeString = "pretty";
1274  if ( !modeString.isEmpty() )
1275  {
1276  QDomElement modeElem = doc.createElement( "mode" );
1277  modeElem.setAttribute( "name", modeString );
1278  rendererElem.appendChild( modeElem );
1279  }
1280 
1281  QDomElement rotationElem = doc.createElement( "rotation" );
1282  if ( mRotation.data() )
1283  rotationElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) );
1284  rendererElem.appendChild( rotationElem );
1285 
1286  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
1287  if ( mSizeScale.data() )
1288  sizeScaleElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mSizeScale.data() ) );
1289  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
1290  rendererElem.appendChild( sizeScaleElem );
1291 
1292  QDomElement labelFormatElem = doc.createElement( "labelformat" );
1293  mLabelFormat.saveToDomElement( labelFormatElem );
1294  rendererElem.appendChild( labelFormatElem );
1295 
1296  return rendererElem;
1297 }
1298 
1300 {
1302  int count = ranges().count();
1303  for ( int i = 0; i < count; i++ )
1304  {
1305  const QgsRendererRangeV2& range = ranges()[i];
1306  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
1307  lst << qMakePair( range.label(), pix );
1308  }
1309  return lst;
1310 }
1311 
1313 {
1314  Q_UNUSED( scaleDenominator );
1315  QgsLegendSymbolList lst;
1316 
1317  foreach ( const QgsRendererRangeV2& range, mRanges )
1318  {
1319  if ( rule.isEmpty() || range.label() == rule )
1320  {
1321  lst << qMakePair( range.label(), range.symbol() );
1322  }
1323  }
1324  return lst;
1325 }
1326 
1328 {
1329  return mSourceSymbol.data();
1330 }
1332 {
1333  mSourceSymbol.reset( sym );
1334 }
1335 
1337 {
1338  return mSourceColorRamp.data();
1339 }
1340 
1342 {
1343  mSourceColorRamp.reset( ramp );
1344 }
1345 
1347 {
1348  int i = 0;
1349  if ( ramp )
1350  {
1351  setSourceColorRamp( ramp );
1352  setInvertedColorRamp( inverted );
1353  }
1354 
1355  if ( mSourceColorRamp )
1356  {
1357  foreach ( QgsRendererRangeV2 range, mRanges )
1358  {
1359  QgsSymbolV2 *symbol = range.symbol() ? range.symbol()->clone() : 0;
1360  if ( symbol )
1361  {
1362  double colorValue;
1363  if ( inverted )
1364  colorValue = ( mRanges.count() > 1 ? ( double )( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
1365  else
1366  colorValue = ( mRanges.count() > 1 ? ( double ) i / ( mRanges.count() - 1 ) : 0 );
1367  symbol->setColor( mSourceColorRamp->color( colorValue ) );
1368  }
1369  updateRangeSymbol( i, symbol );
1370  ++i;
1371  }
1372  }
1373 
1374 }
1375 
1377 {
1378  if ( !sym )
1379  return;
1380 
1381  int i = 0;
1382  foreach ( QgsRendererRangeV2 range, mRanges )
1383  {
1384  QgsSymbolV2 *symbol = sym->clone();
1385  symbol->setColor( range.symbol()->color() );
1386  updateRangeSymbol( i, symbol );
1387  ++i;
1388  }
1389  setSourceSymbol( sym->clone() );
1390 }
1391 
1392 void QgsGraduatedSymbolRendererV2::setRotationField( QString fieldOrExpression )
1393 {
1395 }
1396 
1398 {
1399  return mRotation.data() ? QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) : QString();
1400 }
1401 
1402 void QgsGraduatedSymbolRendererV2::setSizeScaleField( QString fieldOrExpression )
1403 {
1405 }
1406 
1408 {
1410 }
1411 
1413 {
1415  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1416  {
1417  if ( it->symbol() )
1418  setScaleMethodToSymbol( it->symbol(), scaleMethod );
1419  }
1420 }
1421 
1423 {
1424  return true;
1425 }
1426 
1428 {
1429  bool ok;
1430  int index = key.toInt( &ok );
1431  if ( ok && index >= 0 && index < mRanges.size() )
1432  return mRanges[ index ].renderState();
1433  else
1434  return true;
1435 }
1436 
1438 {
1439  bool ok;
1440  int index = key.toInt( &ok );
1441  if ( ok )
1442  updateRangeRenderState( index, state );
1443 }
1444 
1445 
1447 {
1448  QgsSymbolV2* newSymbol = symbol->clone();
1449  QString label = "0.0 - 0.0";
1450  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1451 }
1452 
1453 void QgsGraduatedSymbolRendererV2::addClass( double lower, double upper )
1454 {
1455  QgsSymbolV2* newSymbol = mSourceSymbol->clone();
1456  QString label = mLabelFormat.labelForRange( lower, upper );
1457  mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
1458 }
1459 
1461 {
1462  mRanges.append( range );
1463 }
1464 
1466 {
1467  mRanges.removeAt( idx );
1468 }
1469 
1471 {
1472  mRanges.clear();
1473 }
1474 
1476 {
1477  if ( updateRanges && labelFormat != mLabelFormat )
1478  {
1479  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1480  {
1481  it->setLabel( labelFormat.labelForRange( *it ) );
1482  }
1483  }
1485 }
1486 
1487 
1489 {
1490  // Find the minimum size of a class
1491  double minClassRange = 0.0;
1492  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1493  {
1494  double range = it->upperValue() - it->lowerValue();
1495  if ( range <= 0.0 )
1496  continue;
1497  if ( minClassRange == 0.0 || range < minClassRange )
1498  minClassRange = range;
1499  }
1500  if ( minClassRange <= 0.0 )
1501  return;
1502 
1503  // Now set the number of decimal places to ensure no more than 20% error in
1504  // representing this range (up to 10% at upper and lower end)
1505 
1506  int ndp = 10;
1507  double nextDpMinRange = 0.0000000099;
1508  while ( ndp > 0 && nextDpMinRange < minClassRange )
1509  {
1510  ndp--;
1511  nextDpMinRange *= 10.0;
1512  }
1513  mLabelFormat.setPrecision( ndp );
1514  if ( updateRanges ) setLabelFormat( mLabelFormat, true );
1515 }
1516 
1518 {
1519  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1520  return;
1521  mRanges.move( from, to );
1522 }
1523 
1525 {
1526  return r1 < r2;
1527 }
1528 
1530 {
1531  return !valueLessThan( r1, r2 );
1532 }
1533 
1535 {
1536  QgsDebugMsg( "Entered" );
1537  if ( order == Qt::AscendingOrder )
1538  {
1539  qSort( mRanges.begin(), mRanges.end(), valueLessThan );
1540  }
1541  else
1542  {
1543  qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1544  }
1545 }
1546 
1548 {
1549  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1550 }
1551 
1553 {
1554  return !labelLessThan( r1, r2 );
1555 }
1556 
1558 {
1559  if ( order == Qt::AscendingOrder )
1560  {
1561  qSort( mRanges.begin(), mRanges.end(), labelLessThan );
1562  }
1563  else
1564  {
1565  qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1566  }
1567 }
1568 
1570 {
1571  if ( renderer->type() == "graduatedSymbol" )
1572  {
1573  return dynamic_cast<QgsGraduatedSymbolRendererV2*>( renderer->clone() );
1574  }
1575  if ( renderer->type() == "pointDisplacement" )
1576  {
1577  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1578  if ( pointDisplacementRenderer )
1579  return convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1580  }
1581  if ( renderer->type() == "invertedPolygonRenderer" )
1582  {
1583  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1584  if ( invertedPolygonRenderer )
1585  return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1586  }
1587 
1588  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1589  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1590 
1592  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols();
1593  if ( symbols.size() > 0 )
1594  {
1595  r->setSourceSymbol( symbols.at( 0 )->clone() );
1596  }
1597 
1598  return r;
1599 }