QGIS API Documentation  2.15.0-Master (af20121)
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  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
1205 
1206  return rendererElem;
1207 }
1208 
1210 {
1212  int count = ranges().count();
1213  lst.reserve( count );
1214  for ( int i = 0; i < count; i++ )
1215  {
1216  const QgsRendererRangeV2& range = ranges()[i];
1217  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
1218  lst << qMakePair( range.label(), pix );
1219  }
1220  return lst;
1221 }
1222 
1224 {
1225  QgsLegendSymbolListV2 list;
1226  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
1227  {
1228  // check that all symbols that have the same size expression
1229  QgsDataDefined ddSize;
1230  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1231  {
1232  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( range.symbol() );
1233  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
1234  {
1235  // no common size expression
1237  }
1238  else
1239  {
1240  ddSize = symbol->dataDefinedSize();
1241  }
1242  }
1243 
1244  if ( !ddSize.isActive() || !ddSize.useExpression() )
1245  {
1247  }
1248 
1249  QgsScaleExpression exp( ddSize.expressionString() );
1250  if ( exp.type() != QgsScaleExpression::Unknown )
1251  {
1252  QgsLegendSymbolItemV2 title( nullptr, exp.baseExpression(), "" );
1253  list << title;
1254  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
1255  {
1257  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
1259  s->setSize( exp.size( v ) );
1260  list << si;
1261  }
1262  // now list the graduated symbols
1264  Q_FOREACH ( const QgsLegendSymbolItemV2& item, list2 )
1265  list << item;
1266  return list;
1267  }
1268  }
1269 
1271 }
1272 
1274 {
1275  QVariant value = valueForFeature( feature, context );
1276 
1277  // Null values should not be categorized
1278  if ( value.isNull() )
1279  return QSet< QString >();
1280 
1281  // find the right category
1282  QString key = legendKeyForValue( value.toDouble() );
1283  if ( !key.isNull() )
1284  return QSet< QString >() << key;
1285  else
1286  return QSet< QString >();
1287 }
1288 
1290 {
1291  Q_UNUSED( scaleDenominator );
1292  QgsLegendSymbolList lst;
1293 
1294  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1295  {
1296  if ( rule.isEmpty() || range.label() == rule )
1297  {
1298  lst << qMakePair( range.label(), range.symbol() );
1299  }
1300  }
1301  return lst;
1302 }
1303 
1305 {
1306  return mSourceSymbol.data();
1307 }
1309 {
1310  mSourceSymbol.reset( sym );
1311 }
1312 
1314 {
1315  return mSourceColorRamp.data();
1316 }
1317 
1319 {
1320  mSourceColorRamp.reset( ramp );
1321 }
1322 
1324 {
1325  double min = DBL_MAX;
1326  for ( int i = 0; i < mRanges.count(); i++ )
1327  {
1328  double sz = 0;
1329  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1330  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1331  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1332  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1333  min = qMin( sz, min );
1334  }
1335  return min;
1336 }
1337 
1339 {
1340  double max = DBL_MIN;
1341  for ( int i = 0; i < mRanges.count(); i++ )
1342  {
1343  double sz = 0;
1344  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1345  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1346  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1347  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1348  max = qMax( sz, max );
1349  }
1350  return max;
1351 }
1352 
1353 void QgsGraduatedSymbolRendererV2::setSymbolSizes( double minSize, double maxSize )
1354 {
1355  for ( int i = 0; i < mRanges.count(); i++ )
1356  {
1357  QScopedPointer<QgsSymbolV2> symbol( mRanges.at( i ).symbol() ? mRanges.at( i ).symbol()->clone() : nullptr );
1358  const double size = mRanges.count() > 1
1359  ? minSize + i * ( maxSize - minSize ) / ( mRanges.count() - 1 )
1360  : .5 * ( maxSize + minSize );
1361  if ( symbol->type() == QgsSymbolV2::Marker )
1362  static_cast< QgsMarkerSymbolV2 * >( symbol.data() )->setSize( size );
1363  if ( symbol->type() == QgsSymbolV2::Line )
1364  static_cast< QgsLineSymbolV2 * >( symbol.data() )->setWidth( size );
1365  updateRangeSymbol( i, symbol.take() );
1366  }
1367 }
1368 
1370 {
1371  int i = 0;
1372  if ( ramp )
1373  {
1374  setSourceColorRamp( ramp );
1375  setInvertedColorRamp( inverted );
1376  }
1377 
1378  if ( mSourceColorRamp )
1379  {
1380  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1381  {
1382  QgsSymbolV2 *symbol = range.symbol() ? range.symbol()->clone() : nullptr;
1383  if ( symbol )
1384  {
1385  double colorValue;
1386  if ( inverted )
1387  colorValue = ( mRanges.count() > 1 ? static_cast< double >( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
1388  else
1389  colorValue = ( mRanges.count() > 1 ? static_cast< double >( i ) / ( mRanges.count() - 1 ) : 0 );
1390  symbol->setColor( mSourceColorRamp->color( colorValue ) );
1391  }
1392  updateRangeSymbol( i, symbol );
1393  ++i;
1394  }
1395  }
1396 
1397 }
1398 
1400 {
1401  if ( !sym )
1402  return;
1403 
1404  int i = 0;
1405  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1406  {
1407  QScopedPointer<QgsSymbolV2> symbol( sym->clone() );
1409  {
1410  symbol->setColor( range.symbol()->color() );
1411  }
1412  else if ( mGraduatedMethod == GraduatedSize )
1413  {
1414  if ( symbol->type() == QgsSymbolV2::Marker )
1415  static_cast<QgsMarkerSymbolV2 *>( symbol.data() )->setSize(
1416  static_cast<QgsMarkerSymbolV2 *>( range.symbol() )->size() );
1417  else if ( symbol->type() == QgsSymbolV2::Line )
1418  static_cast<QgsLineSymbolV2 *>( symbol.data() )->setWidth(
1419  static_cast<QgsLineSymbolV2 *>( range.symbol() )->width() );
1420  }
1421  updateRangeSymbol( i, symbol.take() );
1422  ++i;
1423  }
1424  setSourceSymbol( sym->clone() );
1425 }
1426 
1428 {
1429  if ( mSourceSymbol->type() == QgsSymbolV2::Marker )
1430  {
1431  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
1432  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
1433  }
1434 
1435 }
1436 
1438 {
1439  if ( mSourceSymbol->type() == QgsSymbolV2::Marker )
1440  {
1441  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
1442  QgsDataDefined ddAngle = s->dataDefinedAngle();
1443  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
1444  }
1445 
1446  return QString();
1447 }
1448 
1450 {
1452 }
1453 
1455 {
1457 }
1458 
1460 {
1462  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1463  {
1464  if ( range.symbol() )
1466  }
1467 }
1468 
1470 {
1471  return true;
1472 }
1473 
1475 {
1476  bool ok;
1477  int index = key.toInt( &ok );
1478  if ( ok && index >= 0 && index < mRanges.size() )
1479  return mRanges.at( index ).renderState();
1480  else
1481  return true;
1482 }
1483 
1485 {
1486  bool ok;
1487  int index = key.toInt( &ok );
1488  if ( ok )
1489  updateRangeRenderState( index, state );
1490 }
1491 
1493 {
1494  bool ok;
1495  int index = key.toInt( &ok );
1496  if ( ok )
1497  updateRangeSymbol( index, symbol );
1498  else
1499  delete symbol;
1500 }
1501 
1503 {
1504  QgsSymbolV2* newSymbol = symbol->clone();
1505  QString label = "0.0 - 0.0";
1506  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1507 }
1508 
1509 void QgsGraduatedSymbolRendererV2::addClass( double lower, double upper )
1510 {
1511  QgsSymbolV2* newSymbol = mSourceSymbol->clone();
1512  QString label = mLabelFormat.labelForRange( lower, upper );
1513  mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
1514 }
1515 
1517 {
1519  while ( it.hasNext() )
1520  {
1521  QgsRendererRangeV2 range = it.next();
1522  if ( range.lowerValue() < breakValue && range.upperValue() > breakValue )
1523  {
1525  newRange.setLowerValue( breakValue );
1526  newRange.setUpperValue( range.upperValue() );
1527  newRange.setLabel( mLabelFormat.labelForRange( newRange ) );
1528  newRange.setSymbol( mSourceSymbol->clone() );
1529 
1530  //update old range
1531  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
1532  range.setUpperValue( breakValue );
1533  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range.lowerValue(), breakValue ) );
1534  it.setValue( range );
1535 
1536  it.insert( newRange );
1537  break;
1538  }
1539  }
1540 
1541  if ( updateSymbols )
1542  {
1543  switch ( mGraduatedMethod )
1544  {
1545  case GraduatedColor:
1547  break;
1548  case GraduatedSize:
1550  break;
1551  }
1552  }
1553 }
1554 
1556 {
1557  mRanges.append( range );
1558 }
1559 
1561 {
1562  mRanges.removeAt( idx );
1563 }
1564 
1566 {
1567  mRanges.clear();
1568 }
1569 
1571 {
1572  if ( updateRanges && labelFormat != mLabelFormat )
1573  {
1574  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1575  {
1576  it->setLabel( labelFormat.labelForRange( *it ) );
1577  }
1578  }
1580 }
1581 
1582 
1584 {
1585  // Find the minimum size of a class
1586  double minClassRange = 0.0;
1587  Q_FOREACH ( const QgsRendererRangeV2& rendererRange, mRanges )
1588  {
1589  double range = rendererRange.upperValue() - rendererRange.lowerValue();
1590  if ( range <= 0.0 )
1591  continue;
1592  if ( minClassRange == 0.0 || range < minClassRange )
1593  minClassRange = range;
1594  }
1595  if ( minClassRange <= 0.0 )
1596  return;
1597 
1598  // Now set the number of decimal places to ensure no more than 20% error in
1599  // representing this range (up to 10% at upper and lower end)
1600 
1601  int ndp = 10;
1602  double nextDpMinRange = 0.0000000099;
1603  while ( ndp > 0 && nextDpMinRange < minClassRange )
1604  {
1605  ndp--;
1606  nextDpMinRange *= 10.0;
1607  }
1608  mLabelFormat.setPrecision( ndp );
1609  if ( updateRanges ) setLabelFormat( mLabelFormat, true );
1610 }
1611 
1613 {
1614  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1615  return;
1616  mRanges.move( from, to );
1617 }
1618 
1620 {
1621  return r1 < r2;
1622 }
1623 
1625 {
1626  return !valueLessThan( r1, r2 );
1627 }
1628 
1630 {
1631  QgsDebugMsg( "Entered" );
1632  if ( order == Qt::AscendingOrder )
1633  {
1634  qSort( mRanges.begin(), mRanges.end(), valueLessThan );
1635  }
1636  else
1637  {
1638  qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1639  }
1640 }
1641 
1643 {
1644  QgsRangeList sortedRanges = mRanges;
1645  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1646 
1647  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1648  if ( it == sortedRanges.constEnd() )
1649  return false;
1650 
1651  if (( *it ).upperValue() < ( *it ).lowerValue() )
1652  return true;
1653 
1654  double prevMax = ( *it ).upperValue();
1655  ++it;
1656 
1657  for ( ; it != sortedRanges.constEnd(); ++it )
1658  {
1659  if (( *it ).upperValue() < ( *it ).lowerValue() )
1660  return true;
1661 
1662  if (( *it ).lowerValue() < prevMax )
1663  return true;
1664 
1665  prevMax = ( *it ).upperValue();
1666  }
1667  return false;
1668 }
1669 
1671 {
1672  QgsRangeList sortedRanges = mRanges;
1673  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1674 
1675  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1676  if ( it == sortedRanges.constEnd() )
1677  return false;
1678 
1679  double prevMax = ( *it ).upperValue();
1680  ++it;
1681 
1682  for ( ; it != sortedRanges.constEnd(); ++it )
1683  {
1684  if ( !qgsDoubleNear(( *it ).lowerValue(), prevMax ) )
1685  return true;
1686 
1687  prevMax = ( *it ).upperValue();
1688  }
1689  return false;
1690 }
1691 
1693 {
1694  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1695 }
1696 
1698 {
1699  return !labelLessThan( r1, r2 );
1700 }
1701 
1703 {
1704  if ( order == Qt::AscendingOrder )
1705  {
1706  qSort( mRanges.begin(), mRanges.end(), labelLessThan );
1707  }
1708  else
1709  {
1710  qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1711  }
1712 }
1713 
1715 {
1716  QgsGraduatedSymbolRendererV2* r = nullptr;
1717  if ( renderer->type() == "graduatedSymbol" )
1718  {
1719  r = dynamic_cast<QgsGraduatedSymbolRendererV2*>( renderer->clone() );
1720  }
1721  else if ( renderer->type() == "pointDisplacement" )
1722  {
1723  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1724  if ( pointDisplacementRenderer )
1725  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1726  }
1727  else if ( renderer->type() == "invertedPolygonRenderer" )
1728  {
1729  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1730  if ( invertedPolygonRenderer )
1731  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1732  }
1733 
1734  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1735  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1736 
1737  if ( !r )
1738  {
1739  r = new QgsGraduatedSymbolRendererV2( "", QgsRangeList() );
1740  QgsRenderContext context;
1741  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
1742  if ( !symbols.isEmpty() )
1743  {
1744  r->setSourceSymbol( symbols.at( 0 )->clone() );
1745  }
1746  }
1747 
1748  r->setOrderBy( renderer->orderBy() );
1749  r->setOrderByEnabled( renderer->orderByEnabled() );
1750 
1751  return r;
1752 }
1753 
1755 {
1756  switch ( method )
1757  {
1758  case GraduatedColor:
1759  return "GraduatedColor";
1760  case GraduatedSize:
1761  return "GraduatedSize";
1762  }
1763  return "";
1764 }
1765 
1766 
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:104
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:534
double rendererScale() const
virtual QgsSymbolV2 * clone() const =0
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
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:193
void updateColorRamp(QgsVectorColorRampV2 *ramp=nullptr, bool inverted=false)
Update the color ramp used.
Line symbol.
Definition: qgssymbolv2.h:79
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)
Compare two doubles (but allow some difference)
Definition: qgis.h:352
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:78
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:86
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
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer...
virtual QgsVectorColorRampV2 * clone() const =0
Creates a clone of the color ramp.
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)
Sets the angle for the whole symbol.
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)
Sets the size for the whole symbol.
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:87
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:204
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.
Abstract base class for color ramps.
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)