QGIS API Documentation  2.13.0-Master
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 #include "qgspainteffect.h"
24 #include "qgspainteffectregistry.h"
25 #include "qgsscaleexpression.h"
26 #include "qgsdatadefined.h"
27 
28 #include "qgsfeature.h"
29 #include "qgsvectorlayer.h"
30 #include "qgslogger.h"
31 #include "qgsvectordataprovider.h"
32 #include "qgsexpression.h"
33 #include <QDomDocument>
34 #include <QDomElement>
35 #include <QSettings> // for legend
36 #include <limits> // for jenks classification
37 #include <ctime>
38 
40  : mLowerValue( 0 )
41  , mUpperValue( 0 )
42  , mSymbol( nullptr )
43  , mLabel()
44  , mRender( true )
45 {
46 }
47 
49  : mLowerValue( lowerValue )
50  , mUpperValue( upperValue )
51  , mSymbol( symbol )
52  , mLabel( label )
53  , mRender( render )
54 {
55 }
56 
58  : mLowerValue( range.mLowerValue )
59  , mUpperValue( range.mUpperValue )
60  , mSymbol( range.mSymbol.data() ? range.mSymbol->clone() : nullptr )
61  , mLabel( range.mLabel )
62  , mRender( range.mRender )
63 {
64 }
65 
66 // cpy and swap idiom, note that the cpy is done with 'pass by value'
68 {
69  swap( range );
70  return *this;
71 }
72 
74 {
75  return
76  lowerValue() < other.lowerValue() ||
77  ( qgsDoubleNear( lowerValue(), other.lowerValue() ) && upperValue() < other.upperValue() );
78 }
79 
80 
82 {
83  qSwap( mLowerValue, other.mLowerValue );
84  qSwap( mUpperValue, other.mUpperValue );
85  qSwap( mSymbol, other.mSymbol );
86  std::swap( mLabel, other.mLabel );
87 }
88 
90 {
91  return mLowerValue;
92 }
93 
95 {
96  return mUpperValue;
97 }
98 
100 {
101  return mSymbol.data();
102 }
103 
105 {
106  return mLabel;
107 }
108 
110 {
111  if ( mSymbol.data() != s ) mSymbol.reset( s );
112 }
113 
115 {
116  mLabel = label;
117 }
118 
120 {
122 }
123 
125 {
127 }
128 
130 {
131  return mRender;
132 }
133 
135 {
136  mRender = render;
137 }
138 
140 {
141  return QString( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel, mSymbol.data() ? mSymbol->dump() : "(no symbol)" );
142 }
143 
145 {
146  if ( !mSymbol.data() || props.value( "attribute", "" ).isEmpty() )
147  return;
148 
149  QString attrName = props[ "attribute" ];
150 
151  QDomElement ruleElem = doc.createElement( "se:Rule" );
152  element.appendChild( ruleElem );
153 
154  QDomElement nameElem = doc.createElement( "se:Name" );
155  nameElem.appendChild( doc.createTextNode( mLabel ) );
156  ruleElem.appendChild( nameElem );
157 
158  QDomElement descrElem = doc.createElement( "se:Description" );
159  QDomElement titleElem = doc.createElement( "se:Title" );
160  QString descrStr = QString( "range: %1 - %2" ).arg( mLowerValue ).arg( mUpperValue );
161  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
162  descrElem.appendChild( titleElem );
163  ruleElem.appendChild( descrElem );
164 
165  // create the ogc:Filter for the range
166  QString filterFunc = QString( "%1 > %2 AND %1 <= %3" )
167  .arg( attrName.replace( '\"', "\"\"" ) )
168  .arg( mLowerValue ).arg( mUpperValue );
169  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, filterFunc );
170 
171  mSymbol->toSld( doc, ruleElem, props );
172 }
173 
175 
178 
180  mFormat( " %1 - %2 " ),
181  mPrecision( 4 ),
182  mTrimTrailingZeroes( false ),
183  mNumberScale( 1.0 ),
184  mNumberSuffix( "" ),
185  mReTrailingZeroes( "[.,]?0*$" ),
186  mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
187 {
188 }
189 
191  mReTrailingZeroes( "[.,]?0*$" ),
192  mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
193 {
194  setFormat( format );
195  setPrecision( precision );
196  setTrimTrailingZeroes( trimTrailingZeroes );
197 }
198 
199 
201 {
202  return
203  format() == other.format() &&
204  precision() == other.precision() &&
206 }
207 
209 {
210  return !( *this == other );
211 }
212 
214 {
215  // Limit the range of decimal places to a reasonable range
216  precision = qBound( MinPrecision, precision, MaxPrecision );
218  mNumberScale = 1.0;
219  mNumberSuffix = "";
220  while ( precision < 0 )
221  {
222  precision++;
223  mNumberScale /= 10.0;
224  mNumberSuffix.append( '0' );
225  }
226 }
227 
229 {
230  return labelForRange( range.lowerValue(), range.upperValue() );
231 }
232 
234 {
235  if ( mPrecision > 0 )
236  {
237  QString valueStr = QString::number( value, 'f', mPrecision );
238  if ( mTrimTrailingZeroes )
239  valueStr = valueStr.remove( mReTrailingZeroes );
240  if ( mReNegativeZero.exactMatch( valueStr ) )
241  valueStr = valueStr.mid( 1 );
242  return valueStr;
243  }
244  else
245  {
246  QString valueStr = QString::number( value * mNumberScale, 'f', 0 );
247  if ( valueStr == "-0" )
248  valueStr = '0';
249  if ( valueStr != "0" )
250  valueStr = valueStr + mNumberSuffix;
251  return valueStr;
252  }
253 }
254 
256 {
257  QString lowerStr = formatNumber( lower );
258  QString upperStr = formatNumber( upper );
259 
260  QString legend( mFormat );
261  return legend.replace( "%1", lowerStr ).replace( "%2", upperStr );
262 }
263 
265 {
266  mFormat = element.attribute( "format",
267  element.attribute( "prefix", " " ) + "%1" +
268  element.attribute( "separator", " - " ) + "%2" +
269  element.attribute( "suffix", " " )
270  );
271  setPrecision( element.attribute( "decimalplaces", "4" ).toInt() );
272  mTrimTrailingZeroes = element.attribute( "trimtrailingzeroes", "false" ) == "true";
273 }
274 
276 {
277  element.setAttribute( "format", mFormat );
278  element.setAttribute( "decimalplaces", mPrecision );
279  element.setAttribute( "trimtrailingzeroes", mTrimTrailingZeroes ? "true" : "false" );
280 }
281 
283 
285  : QgsFeatureRendererV2( "graduatedSymbol" )
286  , mAttrName( attrName )
287  , mMode( Custom )
288  , mInvertedColorRamp( false )
289  , mScaleMethod( DEFAULT_SCALE_METHOD )
290  , mGraduatedMethod( GraduatedColor )
291  , mAttrNum( -1 )
292  , mCounting( false )
293 
294 {
295  // TODO: check ranges for sanity (NULL symbols, invalid ranges)
296 
297  //important - we need a deep copy of the ranges list, not a shared copy. This is required because
298  //QgsRendererRangeV2::symbol() is marked const, and so retrieving the symbol via this method does not
299  //trigger a detachment and copy of mRanges BUT that same method CAN be used to modify a symbol in place
300  Q_FOREACH ( const QgsRendererRangeV2& range, ranges )
301  {
302  mRanges << range;
303  }
304 
305 }
306 
308 {
309  mRanges.clear(); // should delete all the symbols
310 }
311 
313 {
314  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
315  {
316  if ( range.lowerValue() <= value && range.upperValue() >= value )
317  {
318  if ( range.renderState() || mCounting )
319  return range.symbol();
320  else
321  return nullptr;
322  }
323  }
324  // the value is out of the range: return NULL instead of symbol
325  return nullptr;
326 }
327 
329 {
330  int i = 0;
331  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
332  {
333  if ( range.lowerValue() <= value && range.upperValue() >= value )
334  {
335  if ( range.renderState() || mCounting )
336  return QString::number( i );
337  else
338  return QString::null;
339  }
340  i++;
341  }
342  // the value is out of the range: return NULL
343  return QString::null;
344 }
345 
347 {
348  QgsSymbolV2* symbol = originalSymbolForFeature( feature, context );
349  if ( !symbol )
350  return nullptr;
351 
352  if ( !mRotation.data() && !mSizeScale.data() )
353  return symbol; // no data-defined rotation/scaling - just return the symbol
354 
355  // find out rotation, size scale
356  const double rotation = mRotation.data() ? mRotation->evaluate( &context.expressionContext() ).toDouble() : 0;
357  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( &context.expressionContext() ).toDouble() : 1.;
358 
359  // take a temporary symbol (or create it if doesn't exist)
360  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
361 
362  // modify the temporary symbol and return it
363  if ( tempSymbol->type() == QgsSymbolV2::Marker )
364  {
365  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
366  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
367  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
368  markerSymbol->setScaleMethod( mScaleMethod );
369  }
370  else if ( tempSymbol->type() == QgsSymbolV2::Line )
371  {
372  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
373  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
374  }
375  return tempSymbol;
376 }
377 
378 QVariant QgsGraduatedSymbolRendererV2::valueForFeature( QgsFeature& feature, QgsRenderContext &context ) const
379 {
380  QgsAttributes attrs = feature.attributes();
381  QVariant value;
382  if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
383  {
384  value = mExpression->evaluate( &context.expressionContext() );
385  }
386  else
387  {
388  value = attrs.at( mAttrNum );
389  }
390 
391  return value;
392 }
393 
395 {
396  QVariant value = valueForFeature( feature, context );
397 
398  // Null values should not be categorized
399  if ( value.isNull() )
400  return nullptr;
401 
402  // find the right category
403  return symbolForValue( value.toDouble() );
404 }
405 
407 {
408  mCounting = context.rendererScale() == 0.0;
409 
410  // find out classification attribute index from name
411  mAttrNum = fields.fieldNameIndex( mAttrName );
412 
413  if ( mAttrNum == -1 )
414  {
416  mExpression->prepare( &context.expressionContext() );
417  }
418 
419  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
420  {
421  if ( !range.symbol() )
422  continue;
423 
424  range.symbol()->startRender( context, &fields );
425 
426  if ( mRotation.data() || mSizeScale.data() )
427  {
428  QgsSymbolV2* tempSymbol = range.symbol()->clone();
431  tempSymbol->startRender( context, &fields );
432  mTempSymbols[ range.symbol()] = tempSymbol;
433  }
434  }
435  return;
436 }
437 
439 {
440  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
441  {
442  if ( !range.symbol() )
443  continue;
444 
445  range.symbol()->stopRender( context );
446  }
447 
448  // cleanup mTempSymbols
450  for ( ; it2 != mTempSymbols.constEnd(); ++it2 )
451  {
452  it2.value()->stopRender( context );
453  delete it2.value();
454  }
456 }
457 
459 {
460  QSet<QString> attributes;
461 
462  // mAttrName can contain either attribute name or an expression.
463  // Sometimes it is not possible to distinguish between those two,
464  // e.g. "a - b" can be both a valid attribute name or expression.
465  // Since we do not have access to fields here, try both options.
466  attributes << mAttrName;
467 
468  QgsExpression testExpr( mAttrName );
469  if ( !testExpr.hasParserError() )
470  attributes.unite( testExpr.referencedColumns().toSet() );
471 
472  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
473  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
474 
476  for ( ; range_it != mRanges.constEnd(); ++range_it )
477  {
478  QgsSymbolV2* symbol = range_it->symbol();
479  if ( symbol )
480  {
481  attributes.unite( symbol->usedAttributes() );
482  }
483  }
484  return attributes.toList();
485 }
486 
488 {
489  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
490  return false;
491  mRanges[rangeIndex].setSymbol( symbol );
492  return true;
493 }
494 
495 bool QgsGraduatedSymbolRendererV2::updateRangeLabel( int rangeIndex, const QString& label )
496 {
497  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
498  return false;
499  mRanges[rangeIndex].setLabel( label );
500  return true;
501 }
502 
503 bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double value )
504 {
505  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
506  return false;
507  QgsRendererRangeV2 &range = mRanges[rangeIndex];
508  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
509  range.setUpperValue( value );
510  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
511  return true;
512 }
513 
514 bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double value )
515 {
516  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
517  return false;
518  QgsRendererRangeV2 &range = mRanges[rangeIndex];
519  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
520  range.setLowerValue( value );
521  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
522  return true;
523 }
524 
526 {
527  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
528  return false;
529  mRanges[rangeIndex].setRenderState( value );
530  return true;
531 }
532 
534 {
535  QString s = QString( "GRADUATED: attr %1\n" ).arg( mAttrName );
536  for ( int i = 0; i < mRanges.count(); i++ )
537  s += mRanges[i].dump();
538  return s;
539 }
540 
542 {
544  r->setMode( mMode );
545  if ( mSourceSymbol.data() )
546  r->setSourceSymbol( mSourceSymbol->clone() );
547  if ( mSourceColorRamp.data() )
548  {
549  r->setSourceColorRamp( mSourceColorRamp->clone() );
551  }
554  r->setLabelFormat( labelFormat() );
556  copyRendererData( r );
557  return r;
558 }
559 
561 {
562  QgsStringMap props;
563  props[ "attribute" ] = mAttrName;
564  props[ "method" ] = graduatedMethodStr( mGraduatedMethod );
565  if ( mRotation.data() )
566  props[ "angle" ] = mRotation->expression();
567  if ( mSizeScale.data() )
568  props[ "scale" ] = mSizeScale->expression();
569 
570  // create a Rule for each range
571  for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
572  {
573  QgsStringMap catProps( props );
574  it->toSld( doc, element, catProps );
575  }
576 }
577 
579 {
580  Q_UNUSED( context );
581  QgsSymbolV2List lst;
582  lst.reserve( mRanges.count() );
583  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
584  {
585  lst.append( range.symbol() );
586  }
587  return lst;
588 }
589 
590 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
591 {
592 
593  // Equal interval algorithm
594  //
595  // Returns breaks based on dividing the range ('minimum' to 'maximum')
596  // into 'classes' parts.
597 
598  double step = ( maximum - minimum ) / classes;
599 
600  QList<double> breaks;
601  double value = minimum;
602  breaks.reserve( classes );
603  for ( int i = 0; i < classes; i++ )
604  {
605  value += step;
606  breaks.append( value );
607  }
608 
609  // floating point arithmetics is not precise:
610  // set the last break to be exactly maximum so we do not miss it
611  breaks[classes-1] = maximum;
612 
613  return breaks;
614 }
615 
616 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
617 {
618  // q-th quantile of a data set:
619  // value where q fraction of data is below and (1-q) fraction is above this value
620  // Xq = (1 - r) * X_NI1 + r * X_NI2
621  // NI1 = (int) (q * (n+1))
622  // NI2 = NI1 + 1
623  // r = q * (n+1) - (int) (q * (n+1))
624  // (indices of X: 1...n)
625 
626  // sort the values first
627  qSort( values );
628 
629  QList<double> breaks;
630 
631  // If there are no values to process: bail out
632  if ( values.isEmpty() )
633  return breaks;
634 
635  int n = values.count();
636  double Xq = n > 0 ? values[0] : 0.0;
637 
638  breaks.reserve( classes );
639  for ( int i = 1; i < classes; i++ )
640  {
641  if ( n > 1 )
642  {
643  double q = i / static_cast< double >( classes );
644  double a = q * ( n - 1 );
645  int aa = static_cast< int >( a );
646 
647  double r = a - aa;
648  Xq = ( 1 - r ) * values[aa] + r * values[aa+1];
649  }
650  breaks.append( Xq );
651  }
652 
653  breaks.append( values[ n-1 ] );
654 
655  return breaks;
656 }
657 
658 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<double> &labels )
659 {
660 
661  // C++ implementation of the standard deviation class interval algorithm
662  // as implemented in the 'classInt' package available for the R statistical
663  // prgramming language.
664 
665  // Returns breaks based on 'prettyBreaks' of the centred and scaled
666  // values of 'values', and may have a number of classes different from 'classes'.
667 
668  // If there are no values to process: bail out
669  if ( values.isEmpty() )
670  return QList<double>();
671 
672  double mean = 0.0;
673  double stdDev = 0.0;
674  int n = values.count();
675  double minimum = values[0];
676  double maximum = values[0];
677 
678  for ( int i = 0; i < n; i++ )
679  {
680  mean += values[i];
681  minimum = qMin( values[i], minimum ); // could use precomputed max and min
682  maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
683  }
684  mean = mean / static_cast< double >( n );
685 
686  double sd = 0.0;
687  for ( int i = 0; i < n; i++ )
688  {
689  sd = values[i] - mean;
690  stdDev += sd * sd;
691  }
692  stdDev = sqrt( stdDev / n );
693 
694  QList<double> breaks = QgsSymbolLayerV2Utils::prettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
695  for ( int i = 0; i < breaks.count(); i++ )
696  {
697  labels.append( breaks[i] );
698  breaks[i] = ( breaks[i] * stdDev ) + mean;
699  }
700 
701  return breaks;
702 } // _calcStdDevBreaks
703 
704 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
705  double minimum, double maximum,
706  int maximumSize = 3000 )
707 {
708  // Jenks Optimal (Natural Breaks) algorithm
709  // Based on the Jenks algorithm from the 'classInt' package available for
710  // the R statistical prgramming language, and from Python code from here:
711  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
712  // and is based on a JAVA and Fortran code available here:
713  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
714 
715  // Returns class breaks such that classes are internally homogeneous while
716  // assuring heterogeneity among classes.
717 
718  if ( values.isEmpty() )
719  return QList<double>();
720 
721  if ( classes <= 1 )
722  {
723  return QList<double>() << maximum;
724  }
725 
726  if ( classes >= values.size() )
727  {
728  return values;
729  }
730 
731  QVector<double> sample;
732 
733  // if we have lots of values, we need to take a random sample
734  if ( values.size() > maximumSize )
735  {
736  // for now, sample at least maximumSize values or a 10% sample, whichever
737  // is larger. This will produce a more representative sample for very large
738  // layers, but could end up being computationally intensive...
739 
740  sample.resize( qMax( maximumSize, values.size() / 10 ) );
741 
742  QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
743  QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
744 
745  sample[ 0 ] = minimum;
746  sample[ 1 ] = maximum;
747  for ( int i = 2; i < sample.size(); i++ )
748  {
749  // pick a random integer from 0 to n
750  double r = qrand();
751  int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
752  sample[ i ] = values[ j ];
753  }
754  }
755  else
756  {
757  sample = values.toVector();
758  }
759 
760  int n = sample.size();
761 
762  // sort the sample values
763  qSort( sample );
764 
765  QVector< QVector<int> > matrixOne( n + 1 );
766  QVector< QVector<double> > matrixTwo( n + 1 );
767 
768  for ( int i = 0; i <= n; i++ )
769  {
770  matrixOne[i].resize( classes + 1 );
771  matrixTwo[i].resize( classes + 1 );
772  }
773 
774  for ( int i = 1; i <= classes; i++ )
775  {
776  matrixOne[0][i] = 1;
777  matrixOne[1][i] = 1;
778  matrixTwo[0][i] = 0.0;
779  for ( int j = 2; j <= n; j++ )
780  {
781  matrixTwo[j][i] = std::numeric_limits<double>::max();
782  }
783  }
784 
785  for ( int l = 2; l <= n; l++ )
786  {
787  double s1 = 0.0;
788  double s2 = 0.0;
789  int w = 0;
790 
791  double v = 0.0;
792 
793  for ( int m = 1; m <= l; m++ )
794  {
795  int i3 = l - m + 1;
796 
797  double val = sample[ i3 - 1 ];
798 
799  s2 += val * val;
800  s1 += val;
801  w++;
802 
803  v = s2 - ( s1 * s1 ) / static_cast< double >( w );
804  int i4 = i3 - 1;
805  if ( i4 != 0 )
806  {
807  for ( int j = 2; j <= classes; j++ )
808  {
809  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
810  {
811  matrixOne[l][j] = i4;
812  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
813  }
814  }
815  }
816  }
817  matrixOne[l][1] = 1;
818  matrixTwo[l][1] = v;
819  }
820 
821  QVector<double> breaks( classes );
822  breaks[classes-1] = sample[n-1];
823 
824  for ( int j = classes, k = n; j >= 2; j-- )
825  {
826  int id = matrixOne[k][j] - 1;
827  breaks[j - 2] = sample[id];
828  k = matrixOne[k][j] - 1;
829  }
830 
831  return breaks.toList();
832 } //_calcJenksBreaks
833 
834 
836  QgsVectorLayer* vlayer,
837  const QString& attrName,
838  int classes,
839  Mode mode,
840  QgsSymbolV2* symbol,
841  QgsVectorColorRampV2* ramp,
842  bool inverted,
844 )
845 {
847  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
848  r->setSourceSymbol( symbol->clone() );
849  r->setSourceColorRamp( ramp->clone() );
850  r->setInvertedColorRamp( inverted );
851  r->setMode( mode );
852  r->setLabelFormat( labelFormat );
853  r->updateClasses( vlayer, mode, classes );
854  return r;
855 }
856 
858 {
859  bool ok;
860  return vlayer->getDoubleValues( mAttrName, ok );
861 }
862 
864 {
865  if ( mAttrName.isEmpty() )
866  return;
867 
868  setMode( mode );
869  // Custom classes are not recalculated
870  if ( mode == Custom )
871  return;
872 
873  if ( nclasses < 1 )
874  nclasses = 1;
875 
876  QList<double> values;
877  bool valuesLoaded = false;
878  double minimum;
879  double maximum;
880 
881  int attrNum = vlayer->fieldNameIndex( mAttrName );
882 
883  bool ok;
884  if ( attrNum == -1 )
885  {
886  values = vlayer->getDoubleValues( mAttrName, ok );
887  if ( !ok || values.isEmpty() )
888  return;
889 
890  qSort( values ); // vmora: is wondering if O( n log(n) ) is really necessary here, min and max are O( n )
891  minimum = values.first();
892  maximum = values.last();
893  valuesLoaded = true;
894  }
895  else
896  {
897  minimum = vlayer->minimumValue( attrNum ).toDouble();
898  maximum = vlayer->maximumValue( attrNum ).toDouble();
899  }
900 
901  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
902  QList<double> breaks;
903  QList<double> labels;
904  if ( mode == EqualInterval )
905  {
906  breaks = _calcEqualIntervalBreaks( minimum, maximum, nclasses );
907  }
908  else if ( mode == Pretty )
909  {
910  breaks = QgsSymbolLayerV2Utils::prettyBreaks( minimum, maximum, nclasses );
911  }
912  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
913  {
914  // get values from layer
915  if ( !valuesLoaded )
916  {
917  values = vlayer->getDoubleValues( mAttrName, ok );
918  }
919 
920  // calculate the breaks
921  if ( mode == Quantile )
922  {
923  breaks = _calcQuantileBreaks( values, nclasses );
924  }
925  else if ( mode == Jenks )
926  {
927  breaks = _calcJenksBreaks( values, nclasses, minimum, maximum );
928  }
929  else if ( mode == StdDev )
930  {
931  breaks = _calcStdDevBreaks( values, nclasses, labels );
932  }
933  }
934  else
935  {
936  Q_ASSERT( false );
937  }
938 
939  double lower, upper = minimum;
940  QString label;
942 
943  // "breaks" list contains all values at class breaks plus maximum as last break
944 
945  int i = 0;
946  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
947  {
948  lower = upper; // upper border from last interval
949  upper = *it;
950 
951  // Label - either StdDev label or default label for a range
952  if ( mode == StdDev )
953  {
954  if ( i == 0 )
955  {
956  label = "< " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
957  }
958  else if ( i == labels.count() - 1 )
959  {
960  label = ">= " + QString::number( labels[i-1], 'f', 2 ) + " Std Dev";
961  }
962  else
963  {
964  label = QString::number( labels[i-1], 'f', 2 ) + " Std Dev" + " - " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
965  }
966  }
967  else
968  {
969  label = mLabelFormat.labelForRange( lower, upper );
970  }
971  QgsSymbolV2* newSymbol = mSourceSymbol ? mSourceSymbol->clone() : QgsSymbolV2::defaultSymbol( vlayer->geometryType() );
972  addClass( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
973  }
975 }
976 
978 {
979  QDomElement symbolsElem = element.firstChildElement( "symbols" );
980  if ( symbolsElem.isNull() )
981  return nullptr;
982 
983  QDomElement rangesElem = element.firstChildElement( "ranges" );
984  if ( rangesElem.isNull() )
985  return nullptr;
986 
987  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
989 
990  QDomElement rangeElem = rangesElem.firstChildElement();
991  while ( !rangeElem.isNull() )
992  {
993  if ( rangeElem.tagName() == "range" )
994  {
995  double lowerValue = rangeElem.attribute( "lower" ).toDouble();
996  double upperValue = rangeElem.attribute( "upper" ).toDouble();
997  QString symbolName = rangeElem.attribute( "symbol" );
998  QString label = rangeElem.attribute( "label" );
999  bool render = rangeElem.attribute( "render", "true" ) != "false";
1000  if ( symbolMap.contains( symbolName ) )
1001  {
1002  QgsSymbolV2* symbol = symbolMap.take( symbolName );
1003  ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label, render ) );
1004  }
1005  }
1006  rangeElem = rangeElem.nextSiblingElement();
1007  }
1008 
1009  QString attrName = element.attribute( "attr" );
1010 
1011  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
1012 
1013  QString attrMethod = element.attribute( "graduatedMethod" );
1014  if ( !attrMethod.isEmpty() )
1015  {
1016  if ( attrMethod == graduatedMethodStr( GraduatedColor ) )
1018  else if ( attrMethod == graduatedMethodStr( GraduatedSize ) )
1020  }
1021 
1022 
1023  // delete symbols if there are any more
1025 
1026  // try to load source symbol (optional)
1027  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
1028  if ( !sourceSymbolElem.isNull() )
1029  {
1030  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
1031  if ( sourceSymbolMap.contains( "0" ) )
1032  {
1033  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
1034  }
1035  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
1036  }
1037 
1038  // try to load color ramp (optional)
1039  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
1040  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
1041  {
1042  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
1043  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
1044  if ( !invertedColorRampElem.isNull() )
1045  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
1046  }
1047 
1048  // try to load mode
1049  QDomElement modeElem = element.firstChildElement( "mode" );
1050  if ( !modeElem.isNull() )
1051  {
1052  QString modeString = modeElem.attribute( "name" );
1053  if ( modeString == "equal" )
1054  r->setMode( EqualInterval );
1055  else if ( modeString == "quantile" )
1056  r->setMode( Quantile );
1057  else if ( modeString == "jenks" )
1058  r->setMode( Jenks );
1059  else if ( modeString == "stddev" )
1060  r->setMode( StdDev );
1061  else if ( modeString == "pretty" )
1062  r->setMode( Pretty );
1063  }
1064 
1065  QDomElement rotationElem = element.firstChildElement( "rotation" );
1066  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
1067  {
1068  Q_FOREACH ( const QgsRendererRangeV2& range, r->mRanges )
1069  {
1070  convertSymbolRotation( range.symbol(), rotationElem.attribute( "field" ) );
1071  }
1072  if ( r->mSourceSymbol.data() )
1073  {
1074  convertSymbolRotation( r->mSourceSymbol.data(), rotationElem.attribute( "field" ) );
1075  }
1076  }
1077 
1078  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
1079  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
1080  {
1081  Q_FOREACH ( const QgsRendererRangeV2& range, r->mRanges )
1082  {
1083  convertSymbolSizeScale( range.symbol(),
1084  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
1085  sizeScaleElem.attribute( "field" ) );
1086  }
1087  if ( r->mSourceSymbol.data() && r->mSourceSymbol->type() == QgsSymbolV2::Marker )
1088  {
1090  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
1091  sizeScaleElem.attribute( "field" ) );
1092  }
1093  }
1094 
1095  QDomElement labelFormatElem = element.firstChildElement( "labelformat" );
1096  if ( ! labelFormatElem.isNull() )
1097  {
1099  labelFormat.setFromDomElement( labelFormatElem );
1100  r->setLabelFormat( labelFormat );
1101  }
1102  // TODO: symbol levels
1103  return r;
1104 }
1105 
1107 {
1108  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1109  rendererElem.setAttribute( "type", "graduatedSymbol" );
1110  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
1111  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
1112  rendererElem.setAttribute( "attr", mAttrName );
1113  rendererElem.setAttribute( "graduatedMethod", graduatedMethodStr( mGraduatedMethod ) );
1114 
1115  // ranges
1116  int i = 0;
1118  QDomElement rangesElem = doc.createElement( "ranges" );
1120  for ( ; it != mRanges.constEnd(); ++it )
1121  {
1122  const QgsRendererRangeV2& range = *it;
1123  QString symbolName = QString::number( i );
1124  symbols.insert( symbolName, range.symbol() );
1125 
1126  QDomElement rangeElem = doc.createElement( "range" );
1127  rangeElem.setAttribute( "lower", QString::number( range.lowerValue(), 'f', 15 ) );
1128  rangeElem.setAttribute( "upper", QString::number( range.upperValue(), 'f', 15 ) );
1129  rangeElem.setAttribute( "symbol", symbolName );
1130  rangeElem.setAttribute( "label", range.label() );
1131  rangeElem.setAttribute( "render", range.renderState() ? "true" : "false" );
1132  rangesElem.appendChild( rangeElem );
1133  i++;
1134  }
1135 
1136  rendererElem.appendChild( rangesElem );
1137 
1138  // save symbols
1139  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
1140  rendererElem.appendChild( symbolsElem );
1141 
1142  // save source symbol
1143  if ( mSourceSymbol.data() )
1144  {
1145  QgsSymbolV2Map sourceSymbols;
1146  sourceSymbols.insert( "0", mSourceSymbol.data() );
1147  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
1148  rendererElem.appendChild( sourceSymbolElem );
1149  }
1150 
1151  // save source color ramp
1152  if ( mSourceColorRamp.data() )
1153  {
1154  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
1155  rendererElem.appendChild( colorRampElem );
1156  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
1157  invertedElem.setAttribute( "value", mInvertedColorRamp );
1158  rendererElem.appendChild( invertedElem );
1159  }
1160 
1161  // save mode
1162  QString modeString;
1163  if ( mMode == EqualInterval )
1164  modeString = "equal";
1165  else if ( mMode == Quantile )
1166  modeString = "quantile";
1167  else if ( mMode == Jenks )
1168  modeString = "jenks";
1169  else if ( mMode == StdDev )
1170  modeString = "stddev";
1171  else if ( mMode == Pretty )
1172  modeString = "pretty";
1173  if ( !modeString.isEmpty() )
1174  {
1175  QDomElement modeElem = doc.createElement( "mode" );
1176  modeElem.setAttribute( "name", modeString );
1177  rendererElem.appendChild( modeElem );
1178  }
1179 
1180  QDomElement rotationElem = doc.createElement( "rotation" );
1181  if ( mRotation.data() )
1183  rendererElem.appendChild( rotationElem );
1184 
1185  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
1186  if ( mSizeScale.data() )
1188  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
1189  rendererElem.appendChild( sizeScaleElem );
1190 
1191  QDomElement labelFormatElem = doc.createElement( "labelformat" );
1192  mLabelFormat.saveToDomElement( labelFormatElem );
1193  rendererElem.appendChild( labelFormatElem );
1194 
1196  mPaintEffect->saveProperties( doc, rendererElem );
1197 
1198  if ( !mOrderBy.isEmpty() )
1199  {
1200  QDomElement orderBy = doc.createElement( "orderby" );
1201  mOrderBy.save( orderBy );
1202  rendererElem.appendChild( orderBy );
1203  }
1204 
1205  return rendererElem;
1206 }
1207 
1209 {
1211  int count = ranges().count();
1212  lst.reserve( count );
1213  for ( int i = 0; i < count; i++ )
1214  {
1215  const QgsRendererRangeV2& range = ranges()[i];
1216  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
1217  lst << qMakePair( range.label(), pix );
1218  }
1219  return lst;
1220 }
1221 
1223 {
1224  QgsLegendSymbolListV2 list;
1225  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
1226  {
1227  // check that all symbols that have the same size expression
1228  QgsDataDefined ddSize;
1229  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1230  {
1231  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( range.symbol() );
1232  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
1233  {
1234  // no common size expression
1236  }
1237  else
1238  {
1239  ddSize = symbol->dataDefinedSize();
1240  }
1241  }
1242 
1243  if ( !ddSize.isActive() || !ddSize.useExpression() )
1244  {
1246  }
1247 
1248  QgsScaleExpression exp( ddSize.expressionString() );
1249  if ( exp.type() != QgsScaleExpression::Unknown )
1250  {
1251  QgsLegendSymbolItemV2 title( nullptr, exp.baseExpression(), "" );
1252  list << title;
1253  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
1254  {
1256  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
1258  s->setSize( exp.size( v ) );
1259  list << si;
1260  }
1261  // now list the graduated symbols
1263  Q_FOREACH ( const QgsLegendSymbolItemV2& item, list2 )
1264  list << item;
1265  return list;
1266  }
1267  }
1268 
1270 }
1271 
1273 {
1274  QVariant value = valueForFeature( feature, context );
1275 
1276  // Null values should not be categorized
1277  if ( value.isNull() )
1278  return QSet< QString >();
1279 
1280  // find the right category
1281  QString key = legendKeyForValue( value.toDouble() );
1282  if ( !key.isNull() )
1283  return QSet< QString >() << key;
1284  else
1285  return QSet< QString >();
1286 }
1287 
1289 {
1290  Q_UNUSED( scaleDenominator );
1291  QgsLegendSymbolList lst;
1292 
1293  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1294  {
1295  if ( rule.isEmpty() || range.label() == rule )
1296  {
1297  lst << qMakePair( range.label(), range.symbol() );
1298  }
1299  }
1300  return lst;
1301 }
1302 
1304 {
1305  return mSourceSymbol.data();
1306 }
1308 {
1309  mSourceSymbol.reset( sym );
1310 }
1311 
1313 {
1314  return mSourceColorRamp.data();
1315 }
1316 
1318 {
1319  mSourceColorRamp.reset( ramp );
1320 }
1321 
1323 {
1324  double min = DBL_MAX;
1325  for ( int i = 0; i < mRanges.count(); i++ )
1326  {
1327  double sz = 0;
1328  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1329  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1330  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1331  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1332  min = qMin( sz, min );
1333  }
1334  return min;
1335 }
1336 
1338 {
1339  double max = DBL_MIN;
1340  for ( int i = 0; i < mRanges.count(); i++ )
1341  {
1342  double sz = 0;
1343  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1344  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1345  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1346  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1347  max = qMax( sz, max );
1348  }
1349  return max;
1350 }
1351 
1352 void QgsGraduatedSymbolRendererV2::setSymbolSizes( double minSize, double maxSize )
1353 {
1354  for ( int i = 0; i < mRanges.count(); i++ )
1355  {
1356  QScopedPointer<QgsSymbolV2> symbol( mRanges.at( i ).symbol() ? mRanges.at( i ).symbol()->clone() : nullptr );
1357  const double size = mRanges.count() > 1
1358  ? minSize + i * ( maxSize - minSize ) / ( mRanges.count() - 1 )
1359  : .5 * ( maxSize + minSize );
1360  if ( symbol->type() == QgsSymbolV2::Marker )
1361  static_cast< QgsMarkerSymbolV2 * >( symbol.data() )->setSize( size );
1362  if ( symbol->type() == QgsSymbolV2::Line )
1363  static_cast< QgsLineSymbolV2 * >( symbol.data() )->setWidth( size );
1364  updateRangeSymbol( i, symbol.take() );
1365  }
1366 }
1367 
1369 {
1370  int i = 0;
1371  if ( ramp )
1372  {
1373  setSourceColorRamp( ramp );
1374  setInvertedColorRamp( inverted );
1375  }
1376 
1377  if ( mSourceColorRamp )
1378  {
1379  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1380  {
1381  QgsSymbolV2 *symbol = range.symbol() ? range.symbol()->clone() : nullptr;
1382  if ( symbol )
1383  {
1384  double colorValue;
1385  if ( inverted )
1386  colorValue = ( mRanges.count() > 1 ? static_cast< double >( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
1387  else
1388  colorValue = ( mRanges.count() > 1 ? static_cast< double >( i ) / ( mRanges.count() - 1 ) : 0 );
1389  symbol->setColor( mSourceColorRamp->color( colorValue ) );
1390  }
1391  updateRangeSymbol( i, symbol );
1392  ++i;
1393  }
1394  }
1395 
1396 }
1397 
1399 {
1400  if ( !sym )
1401  return;
1402 
1403  int i = 0;
1404  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1405  {
1406  QScopedPointer<QgsSymbolV2> symbol( sym->clone() );
1408  {
1409  symbol->setColor( range.symbol()->color() );
1410  }
1411  else if ( mGraduatedMethod == GraduatedSize )
1412  {
1413  if ( symbol->type() == QgsSymbolV2::Marker )
1414  static_cast<QgsMarkerSymbolV2 *>( symbol.data() )->setSize(
1415  static_cast<QgsMarkerSymbolV2 *>( range.symbol() )->size() );
1416  else if ( symbol->type() == QgsSymbolV2::Line )
1417  static_cast<QgsLineSymbolV2 *>( symbol.data() )->setWidth(
1418  static_cast<QgsLineSymbolV2 *>( range.symbol() )->width() );
1419  }
1420  updateRangeSymbol( i, symbol.take() );
1421  ++i;
1422  }
1423  setSourceSymbol( sym->clone() );
1424 }
1425 
1427 {
1428  if ( mSourceSymbol->type() == QgsSymbolV2::Marker )
1429  {
1430  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
1431  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
1432  }
1433 
1434 }
1435 
1437 {
1438  if ( mSourceSymbol->type() == QgsSymbolV2::Marker )
1439  {
1440  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
1441  QgsDataDefined ddAngle = s->dataDefinedAngle();
1442  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
1443  }
1444 
1445  return QString();
1446 }
1447 
1449 {
1451 }
1452 
1454 {
1456 }
1457 
1459 {
1461  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1462  {
1463  if ( range.symbol() )
1465  }
1466 }
1467 
1469 {
1470  return true;
1471 }
1472 
1474 {
1475  bool ok;
1476  int index = key.toInt( &ok );
1477  if ( ok && index >= 0 && index < mRanges.size() )
1478  return mRanges.at( index ).renderState();
1479  else
1480  return true;
1481 }
1482 
1484 {
1485  bool ok;
1486  int index = key.toInt( &ok );
1487  if ( ok )
1488  updateRangeRenderState( index, state );
1489 }
1490 
1492 {
1493  bool ok;
1494  int index = key.toInt( &ok );
1495  if ( ok )
1496  updateRangeSymbol( index, symbol );
1497  else
1498  delete symbol;
1499 }
1500 
1502 {
1503  QgsSymbolV2* newSymbol = symbol->clone();
1504  QString label = "0.0 - 0.0";
1505  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1506 }
1507 
1508 void QgsGraduatedSymbolRendererV2::addClass( double lower, double upper )
1509 {
1510  QgsSymbolV2* newSymbol = mSourceSymbol->clone();
1511  QString label = mLabelFormat.labelForRange( lower, upper );
1512  mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
1513 }
1514 
1516 {
1518  while ( it.hasNext() )
1519  {
1520  QgsRendererRangeV2 range = it.next();
1521  if ( range.lowerValue() < breakValue && range.upperValue() > breakValue )
1522  {
1524  newRange.setLowerValue( breakValue );
1525  newRange.setUpperValue( range.upperValue() );
1526  newRange.setLabel( mLabelFormat.labelForRange( newRange ) );
1527  newRange.setSymbol( mSourceSymbol->clone() );
1528 
1529  //update old range
1530  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
1531  range.setUpperValue( breakValue );
1532  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range.lowerValue(), breakValue ) );
1533  it.setValue( range );
1534 
1535  it.insert( newRange );
1536  break;
1537  }
1538  }
1539 
1540  if ( updateSymbols )
1541  {
1542  switch ( mGraduatedMethod )
1543  {
1544  case GraduatedColor:
1546  break;
1547  case GraduatedSize:
1549  break;
1550  }
1551  }
1552 }
1553 
1555 {
1556  mRanges.append( range );
1557 }
1558 
1560 {
1561  mRanges.removeAt( idx );
1562 }
1563 
1565 {
1566  mRanges.clear();
1567 }
1568 
1570 {
1571  if ( updateRanges && labelFormat != mLabelFormat )
1572  {
1573  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1574  {
1575  it->setLabel( labelFormat.labelForRange( *it ) );
1576  }
1577  }
1579 }
1580 
1581 
1583 {
1584  // Find the minimum size of a class
1585  double minClassRange = 0.0;
1586  Q_FOREACH ( const QgsRendererRangeV2& rendererRange, mRanges )
1587  {
1588  double range = rendererRange.upperValue() - rendererRange.lowerValue();
1589  if ( range <= 0.0 )
1590  continue;
1591  if ( minClassRange == 0.0 || range < minClassRange )
1592  minClassRange = range;
1593  }
1594  if ( minClassRange <= 0.0 )
1595  return;
1596 
1597  // Now set the number of decimal places to ensure no more than 20% error in
1598  // representing this range (up to 10% at upper and lower end)
1599 
1600  int ndp = 10;
1601  double nextDpMinRange = 0.0000000099;
1602  while ( ndp > 0 && nextDpMinRange < minClassRange )
1603  {
1604  ndp--;
1605  nextDpMinRange *= 10.0;
1606  }
1607  mLabelFormat.setPrecision( ndp );
1608  if ( updateRanges ) setLabelFormat( mLabelFormat, true );
1609 }
1610 
1612 {
1613  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1614  return;
1615  mRanges.move( from, to );
1616 }
1617 
1619 {
1620  return r1 < r2;
1621 }
1622 
1624 {
1625  return !valueLessThan( r1, r2 );
1626 }
1627 
1629 {
1630  QgsDebugMsg( "Entered" );
1631  if ( order == Qt::AscendingOrder )
1632  {
1633  qSort( mRanges.begin(), mRanges.end(), valueLessThan );
1634  }
1635  else
1636  {
1637  qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1638  }
1639 }
1640 
1642 {
1643  QgsRangeList sortedRanges = mRanges;
1644  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1645 
1646  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1647  if ( it == sortedRanges.constEnd() )
1648  return false;
1649 
1650  if (( *it ).upperValue() < ( *it ).lowerValue() )
1651  return true;
1652 
1653  double prevMax = ( *it ).upperValue();
1654  ++it;
1655 
1656  for ( ; it != sortedRanges.constEnd(); ++it )
1657  {
1658  if (( *it ).upperValue() < ( *it ).lowerValue() )
1659  return true;
1660 
1661  if (( *it ).lowerValue() < prevMax )
1662  return true;
1663 
1664  prevMax = ( *it ).upperValue();
1665  }
1666  return false;
1667 }
1668 
1670 {
1671  QgsRangeList sortedRanges = mRanges;
1672  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1673 
1674  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1675  if ( it == sortedRanges.constEnd() )
1676  return false;
1677 
1678  double prevMax = ( *it ).upperValue();
1679  ++it;
1680 
1681  for ( ; it != sortedRanges.constEnd(); ++it )
1682  {
1683  if ( !qgsDoubleNear(( *it ).lowerValue(), prevMax ) )
1684  return true;
1685 
1686  prevMax = ( *it ).upperValue();
1687  }
1688  return false;
1689 }
1690 
1692 {
1693  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1694 }
1695 
1697 {
1698  return !labelLessThan( r1, r2 );
1699 }
1700 
1702 {
1703  if ( order == Qt::AscendingOrder )
1704  {
1705  qSort( mRanges.begin(), mRanges.end(), labelLessThan );
1706  }
1707  else
1708  {
1709  qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1710  }
1711 }
1712 
1714 {
1715  QgsGraduatedSymbolRendererV2* r = nullptr;
1716  if ( renderer->type() == "graduatedSymbol" )
1717  {
1718  r = dynamic_cast<QgsGraduatedSymbolRendererV2*>( renderer->clone() );
1719  }
1720  else if ( renderer->type() == "pointDisplacement" )
1721  {
1722  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1723  if ( pointDisplacementRenderer )
1724  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1725  }
1726  else if ( renderer->type() == "invertedPolygonRenderer" )
1727  {
1728  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1729  if ( invertedPolygonRenderer )
1730  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1731  }
1732 
1733  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1734  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1735 
1736  if ( !r )
1737  {
1738  r = new QgsGraduatedSymbolRendererV2( "", QgsRangeList() );
1739  QgsRenderContext context;
1740  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
1741  if ( !symbols.isEmpty() )
1742  {
1743  r->setSourceSymbol( symbols.at( 0 )->clone() );
1744  }
1745  }
1746 
1747  r->setOrderBy( renderer->orderBy() );
1748 
1749  return r;
1750 }
1751 
1753 {
1754  switch ( method )
1755  {
1756  case GraduatedColor:
1757  return "GraduatedColor";
1758  case GraduatedSize:
1759  return "GraduatedSize";
1760  }
1761  return "";
1762 }
1763 
1764 
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
Class for parsing and evaluation of expressions (formerly called "search strings").
QList< QgsRendererRangeV2 > QgsRangeList
void clear()
static QgsSymbolV2Map loadSymbols(QDomElement &element)
#define RENDERER_TAG_NAME
Definition: qgsrendererv2.h:49
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
static unsigned index
virtual void toSld(QDomDocument &doc, QDomElement &element) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
void setFormat(const QString &format)
QString & append(QChar ch)
void setLowerValue(double lowerValue)
QgsGraduatedSymbolRendererV2(const QString &attrName=QString(), const QgsRangeList &ranges=QgsRangeList())
void setSymbolSizes(double minSize, double maxSize)
set varying symbol size for classes
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QStringList referencedColumns() const
Get list of columns referenced by the expression.
void addBreak(double breakValue, bool updateSymbols=true)
Add a breakpoint by splitting existing classes so that the specified value becomes a break between tw...
void setDataDefinedAngle(const QgsDataDefined &dd)
Set data defined angle for whole symbol (including all symbol layers).
A container class for data source field mapping or expression.
void setLabelFormat(const QgsRendererRangeV2LabelFormat &labelFormat, bool updateRanges=false)
Set the label format used to generate default classification labels.
static QList< double > prettyBreaks(double minimum, double maximum, int classes)
Computes a sequence of about &#39;classes&#39; equally spaced round values which cover the range of values fr...
bool labelLessThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
bool contains(const Key &key) const
static QgsGraduatedSymbolRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsGraduatedSymbolRendererV2 from an existing renderer.
static QgsVectorColorRampV2 * loadColorRamp(QDomElement &element)
int localeAwareCompare(const QString &other) const
void updateClasses(QgsVectorLayer *vlayer, Mode mode, int nclasses)
Recalculate classes for a layer.
QgsFeatureRequest::OrderBy orderBy() const
Get the order in which features shall be processed by this renderer.
static QList< double > _calcStdDevBreaks(QList< double > values, int classes, QList< double > &labels)
virtual QString dump() const override
for debugging
QDomNode appendChild(const QDomNode &newChild)
SymbolType type() const
Definition: qgssymbolv2.h:101
QScopedPointer< QgsVectorColorRampV2 > mSourceColorRamp
QVariant maximumValue(int index)
Returns maximum value for an attribute column or invalid variant in case of error.
QSet< QString > usedAttributes() const
Return a list of attributes required to render this feature.
QString field() const
Get the field which this QgsDataDefined represents.
QString attribute(const QString &name, const QString &defValue) const
void updateSymbols(QgsSymbolV2 *sym)
Update all the symbols but leave breaks and colors.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setDataDefinedSize(const QgsDataDefined &dd)
Set data defined size for whole symbol (including all symbol layers).
QString formatNumber(double value) const
void reserve(int alloc)
static QDomElement saveColorRamp(const QString &name, QgsVectorColorRampV2 *ramp, QDomDocument &doc)
int fieldNameIndex(const QString &fieldName) const
Look up field&#39;s index from name also looks up case-insensitive if there is no match otherwise...
Definition: qgsfield.cpp:446
double rendererScale() const
virtual QgsSymbolV2 * clone() const =0
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
QgsDataDefined dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
static QgsSymbolV2::ScaleMethod decodeScaleMethod(const QString &str)
virtual bool legendSymbolItemChecked(const QString &key) override
item in symbology was checked
bool updateRangeRenderState(int rangeIndex, bool render)
virtual QgsSymbolV2 * symbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
const T & at(int i) const
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
void removeAt(int i)
void setSizeScaleField(const QString &fieldOrExpression)
QString labelForRange(double lower, double upper) const
QDomElement nextSiblingElement(const QString &tagName) const
double minSymbolSize() const
return the min symbol size when graduated by size
Container of fields for a vector layer.
Definition: qgsfield.h:189
void updateColorRamp(QgsVectorColorRampV2 *ramp=nullptr, bool inverted=false)
Update the color ramp used.
Line symbol.
Definition: qgssymbolv2.h:76
QString expressionString() const
Returns the expression string of this QgsDataDefined.
void move(int from, int to)
QVector< T > toVector() const
QHash< QgsSymbolV2 *, QgsSymbolV2 * > mTempSymbols
temporary symbols, used for data-defined rotation and scaling
QSet< T > toSet() const
void calculateLabelPrecision(bool updateRanges=true)
Reset the label decimal places to a numberbased on the minimum class interval.
QList< double > getDoubleValues(const QString &fieldOrExpression, bool &ok, bool selectedOnly=false, int *nullCount=nullptr)
Fetches all double values from a specified field name or expression.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
QString & remove(int position, int n)
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, const QString &rule=QString()) override
return a list of item text / symbol
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
double toDouble(bool *ok) const
QVariant minimumValue(int index)
Returns minimum value for an attribute column or invalid variant in case of error.
void setValue(const T &value) const
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:285
static const char * graduatedMethodStr(GraduatedMethod method)
QgsVectorColorRampV2 * sourceColorRamp()
Returns the source color ramp, from which each classes&#39; color is derived.
QgsPaintEffect * mPaintEffect
void setWidth(double width)
Marker symbol.
Definition: qgssymbolv2.h:75
int size() const
bool isNull() const
void reset(T *other)
Q_DECL_DEPRECATED QList< double > getDataValues(QgsVectorLayer *vlayer)
Evaluates the data expression and returns the list of values from the layer.
QScopedPointer< QgsSymbolV2 > mSymbol
QString type() const
Definition: qgsrendererv2.h:83
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
virtual void stopRender(QgsRenderContext &context) override
Needs to be called when a render cycle has finished to clean up.
QScopedPointer< QgsExpression > mExpression
void setColor(const QColor &color)
bool rangesHaveGaps() const
Tests whether classes assigned to the renderer have gaps between the ranges.
virtual QgsFeatureRendererV2 * clone() const =0
QString number(int n, int base)
int count(const T &value) const
static QgsGraduatedSymbolRendererV2 * createRenderer(QgsVectorLayer *vlayer, const QString &attrName, int classes, Mode mode, QgsSymbolV2 *symbol, QgsVectorColorRampV2 *ramp, bool inverted=false, const QgsRendererRangeV2LabelFormat &legendFormat=QgsRendererRangeV2LabelFormat())
void setGraduatedMethod(GraduatedMethod method)
set the method used for graduation (either size or color)
void append(const T &value)
QgsSymbolV2::ScaleMethod scaleMethod() const
void resize(int size)
const_iterator constEnd() const
void startRender(QgsRenderContext &context, const QgsFields *fields=nullptr)
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
bool isNull() const
static QList< double > _calcQuantileBreaks(QList< double > values, int classes)
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
bool operator==(const QgsRendererRangeV2LabelFormat &other) const
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
void setAttribute(const QString &name, const QString &value)
#define DEFAULT_SCALE_METHOD
virtual QgsVectorColorRampV2 * clone() const =0
void swap(QgsRendererRangeV2 &other)
int toInt(bool *ok, int base) const
bool isEmpty() const
bool isEmpty() const
QgsSymbolV2 * sourceSymbol()
Returns the renderer&#39;s source symbol, which is the base symbol used for the each classes&#39; symbol befo...
Q_DECL_DEPRECATED QString rotationField() const override
return rotation field name (or empty string if not set or not supported by renderer) ...
QgsSymbolV2 * symbol() const
GraduatedMethod graduatedMethod() const
return the method used for graduation (either size or color)
void setAngle(double angle)
static QList< double > _calcJenksBreaks(QList< double > values, int classes, double minimum, double maximum, int maximumSize=3000)
void setSourceSymbol(QgsSymbolV2 *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each classes&#39; symbol b...
bool updateRangeLowerValue(int rangeIndex, double value)
void setSize(double size)
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
QgsRendererRangeV2LabelFormat mLabelFormat
QGis::GeometryType geometryType() const
Returns point, line or polygon.
T & first()
virtual Q_DECL_DEPRECATED QgsSymbolV2List symbols()
For symbol levels.
void setUpperValue(double upperValue)
virtual QDomElement save(QDomDocument &doc) override
store renderer info to XML element
bool operator<(const QgsRendererRangeV2 &other) const
void setFromDomElement(QDomElement &element)
QString legendKeyForValue(double value) const
Returns the matching legend key for a value.
QgsSymbolV2 * symbolForValue(double value)
static void convertSymbolSizeScale(QgsSymbolV2 *symbol, QgsSymbolV2::ScaleMethod method, const QString &field)
bool labelGreaterThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
QgsFeatureRendererV2 * embeddedRenderer() const
bool useExpression() const
Returns if the field or the expression part is active.
QDomText createTextNode(const QString &value)
T * data() const
void clear()
bool valueLessThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
iterator end()
const T value(const Key &key) const
virtual QgsGraduatedSymbolRendererV2 * clone() const override
const QgsRendererRangeV2LabelFormat & labelFormat() const
Return the label format used to generate default classification labels.
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
bool hasNext() const
QgsExpressionContext & expressionContext()
Gets the expression context.
void copyRendererData(QgsFeatureRendererV2 *destRenderer) const
Clones generic renderer data to another renderer.
A renderer that automatically displaces points with the same position.
virtual bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
bool isNull() const
void setUsingSymbolLevels(bool usingSymbolLevels)
QString & replace(int position, int n, QChar after)
bool rangesOverlap() const
Tests whether classes assigned to the renderer have ranges which overlap.
static QList< double > _calcEqualIntervalBreaks(double minimum, double maximum, int classes)
const T & at(int i) const
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
const_iterator constBegin() const
int ANALYSIS_EXPORT lower(int n, int i)
Lower function.
void moveClass(int from, int to)
Moves the category at index position from to index position to.
Contains information about the context of a rendering operation.
const QgsFeatureRendererV2 * embeddedRenderer() const
void setOrderBy(const QgsFeatureRequest::OrderBy &orderBy)
Define the order in which features shall be processed by this renderer.
int mAttrNum
attribute index (derived from attribute name in startRender)
QList< T > toList() const
QgsDataDefined dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
void stopRender(QgsRenderContext &context)
QString mid(int position, int n) const
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
QSet< T > & unite(const QSet< T > &other)
static QgsSymbolV2 * defaultSymbol(QGis::GeometryType geomType)
return new default symbol for specified geometry type
static QgsExpression * fieldOrExpressionToExpression(const QString &fieldOrExpression)
Return a new valid expression instance for given field or expression string.
ScaleMethod
Scale method.
Definition: qgssymbolv2.h:84
Q_DECL_DEPRECATED void setRotationField(const QString &fieldOrExpression) override
sets rotation field of renderer (if supported by the renderer)
void insert(int i, const T &value)
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=nullptr)
double maxSymbolSize() const
return the max symbol size when graduated by size
const QgsRangeList & ranges() const
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const
Return a list of symbology items for the legend.
QgsFeatureRequest::OrderBy mOrderBy
bool operator!=(const QgsRendererRangeV2LabelFormat &other) const
static QString encodeScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
QDomElement firstChildElement(const QString &tagName) const
static QString fieldOrExpressionFromExpression(QgsExpression *expression)
Return a field name if the whole expression is just a name of the field .
bool usingSymbolLevels() const
T & last()
void setScaleMethodToSymbol(QgsSymbolV2 *symbol, int scaleMethod)
virtual void setLegendSymbolItem(const QString &key, QgsSymbolV2 *symbol) override
Sets the symbol to be used for a legend symbol item.
int count(const T &value) const
bool updateRangeSymbol(int rangeIndex, QgsSymbolV2 *symbol)
virtual void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
Sets the source color ramp.
QScopedPointer< QgsSymbolV2 > mSourceSymbol
QList< T > toList() const
void setRenderHints(int hints)
Definition: qgssymbolv2.h:201
static void clearSymbolMap(QgsSymbolV2Map &symbols)
void setTrimTrailingZeroes(bool trimTrailingZeroes)
bool updateRangeUpperValue(int rangeIndex, double value)
QScopedPointer< QgsExpression > mRotation
virtual QSet< QString > legendKeysForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return legend keys matching a specified feature.
double toDouble(bool *ok) const
virtual QgsSymbolV2 * originalSymbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
iterator insert(const Key &key, const T &value)
static QgsFeatureRendererV2 * create(QDomElement &element)
create renderer from XML element
virtual QgsLegendSymbologyList legendSymbologyItems(QSize iconSize) override
return a list of symbology items for the legend
QString tagName() const
void setLabel(const QString &label)
bool hasDefaultValues() const
Returns whether the data defined container is set to all the default values, ie, disabled, with empty expression and no assigned field.
virtual QList< QString > usedAttributes() override
Returns a set of attributes required for this renderer.
double ANALYSIS_EXPORT min(double x, double y)
Returns the minimum of two doubles or the first argument if both are equal.
const_iterator constEnd() const
QScopedPointer< QgsExpression > mSizeScale
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
QgsRendererRangeV2 & operator=(QgsRendererRangeV2 range)
A vector of attributes.
Definition: qgsfeature.h:115
void insert(const T &value)
int size() const
Represents a vector layer which manages a vector based data sets.
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
bool exactMatch(const QString &str) const
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
bool isActive() const
iterator begin()
T take(const Key &key)
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
QgsLegendSymbolListV2 legendSymbolItemsV2() const override
bool valueGreaterThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
const T value(const Key &key) const
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
Needs to be called when a new render cycle is started.
static void convertSymbolRotation(QgsSymbolV2 *symbol, const QString &field)
QColor color() const
bool updateRangeLabel(int rangeIndex, const QString &label)