QGIS API Documentation  2.99.0-Master (0cba29c)
qgsgraduatedsymbolrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsgraduatedsymbolrenderer.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 
18 
19 #include "qgsattributes.h"
20 #include "qgscolorramp.h"
22 #include "qgsexpression.h"
23 #include "qgsfeature.h"
25 #include "qgslogger.h"
26 #include "qgspainteffect.h"
27 #include "qgspainteffectregistry.h"
29 #include "qgsproperty.h"
30 #include "qgssymbol.h"
31 #include "qgssymbollayer.h"
32 #include "qgssymbollayerutils.h"
33 #include "qgsvectordataprovider.h"
34 #include "qgsvectorlayer.h"
35 
36 #include <QDomDocument>
37 #include <QDomElement>
38 #include <QSettings> // for legend
39 #include <limits> // for jenks classification
40 #include <ctime>
41 
42 
44  : mLowerValue( 0 )
45  , mUpperValue( 0 )
46  , mSymbol( nullptr )
47  , mLabel()
48  , mRender( true )
49 {
50 }
51 
52 QgsRendererRange::QgsRendererRange( double lowerValue, double upperValue, QgsSymbol *symbol, const QString &label, bool render )
53  : mLowerValue( lowerValue )
54  , mUpperValue( upperValue )
55  , mSymbol( symbol )
56  , mLabel( label )
57  , mRender( render )
58 {
59 }
60 
62  : mLowerValue( range.mLowerValue )
63  , mUpperValue( range.mUpperValue )
64  , mSymbol( range.mSymbol ? range.mSymbol->clone() : nullptr )
65  , mLabel( range.mLabel )
66  , mRender( range.mRender )
67 {
68 }
69 
70 // cpy and swap idiom, note that the cpy is done with 'pass by value'
72 {
73  swap( range );
74  return *this;
75 }
76 
78 {
79  return
80  lowerValue() < other.lowerValue() ||
81  ( qgsDoubleNear( lowerValue(), other.lowerValue() ) && upperValue() < other.upperValue() );
82 }
83 
84 
86 {
87  std::swap( mLowerValue, other.mLowerValue );
88  std::swap( mUpperValue, other.mUpperValue );
89  std::swap( mSymbol, other.mSymbol );
90  std::swap( mLabel, other.mLabel );
91 }
92 
94 {
95  return mLowerValue;
96 }
97 
99 {
100  return mUpperValue;
101 }
102 
104 {
105  return mSymbol.get();
106 }
107 
108 QString QgsRendererRange::label() const
109 {
110  return mLabel;
111 }
112 
114 {
115  if ( mSymbol.get() != s ) mSymbol.reset( s );
116 }
117 
118 void QgsRendererRange::setLabel( const QString &label )
119 {
120  mLabel = label;
121 }
122 
124 {
126 }
127 
129 {
131 }
132 
134 {
135  return mRender;
136 }
137 
139 {
140  mRender = render;
141 }
142 
143 QString QgsRendererRange::dump() const
144 {
145  return QStringLiteral( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel, mSymbol ? mSymbol->dump() : QStringLiteral( "(no symbol)" ) );
146 }
147 
148 void QgsRendererRange::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props, bool firstRange ) const
149 {
150  if ( !mSymbol || props.value( QStringLiteral( "attribute" ), QLatin1String( "" ) ).isEmpty() )
151  return;
152 
153  QString attrName = props[ QStringLiteral( "attribute" )];
154 
155  QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
156  element.appendChild( ruleElem );
157 
158  QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
159  nameElem.appendChild( doc.createTextNode( mLabel ) );
160  ruleElem.appendChild( nameElem );
161 
162  QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
163  QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
164  QString descrStr = QStringLiteral( "range: %1 - %2" ).arg( qgsDoubleToString( mLowerValue ), qgsDoubleToString( mUpperValue ) );
165  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
166  descrElem.appendChild( titleElem );
167  ruleElem.appendChild( descrElem );
168 
169  // create the ogc:Filter for the range
170  QString filterFunc = QStringLiteral( "\"%1\" %2 %3 AND \"%1\" <= %4" )
171  .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
172  firstRange ? ">=" : ">",
175  QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc );
176 
177  mSymbol->toSld( doc, ruleElem, props );
178 }
179 
181 
184 
186  : mFormat( QStringLiteral( " %1 - %2 " ) )
187  , mPrecision( 4 )
188  , mTrimTrailingZeroes( false )
189  , mNumberScale( 1.0 )
190  , mNumberSuffix( QLatin1String( "" ) )
191  , mReTrailingZeroes( "[.,]?0*$" )
192  , mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
193 {
194 }
195 
197  : mReTrailingZeroes( "[.,]?0*$" )
198  , mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
199 {
200  setFormat( format );
201  setPrecision( precision );
202  setTrimTrailingZeroes( trimTrailingZeroes );
203 }
204 
205 
207 {
208  return
209  format() == other.format() &&
210  precision() == other.precision() &&
212 }
213 
215 {
216  return !( *this == other );
217 }
218 
220 {
221  // Limit the range of decimal places to a reasonable range
222  precision = qBound( MIN_PRECISION, precision, MAX_PRECISION );
224  mNumberScale = 1.0;
225  mNumberSuffix = QLatin1String( "" );
226  while ( precision < 0 )
227  {
228  precision++;
229  mNumberScale /= 10.0;
230  mNumberSuffix.append( '0' );
231  }
232 }
233 
235 {
236  return labelForRange( range.lowerValue(), range.upperValue() );
237 }
238 
239 QString QgsRendererRangeLabelFormat::formatNumber( double value ) const
240 {
241  if ( mPrecision > 0 )
242  {
243  QString valueStr = QString::number( value, 'f', mPrecision );
244  if ( mTrimTrailingZeroes )
245  valueStr = valueStr.remove( mReTrailingZeroes );
246  if ( mReNegativeZero.exactMatch( valueStr ) )
247  valueStr = valueStr.mid( 1 );
248  return valueStr;
249  }
250  else
251  {
252  QString valueStr = QString::number( value * mNumberScale, 'f', 0 );
253  if ( valueStr == QLatin1String( "-0" ) )
254  valueStr = '0';
255  if ( valueStr != QLatin1String( "0" ) )
256  valueStr = valueStr + mNumberSuffix;
257  return valueStr;
258  }
259 }
260 
261 QString QgsRendererRangeLabelFormat::labelForRange( double lower, double upper ) const
262 {
263  QString lowerStr = formatNumber( lower );
264  QString upperStr = formatNumber( upper );
265 
266  QString legend( mFormat );
267  return legend.replace( QLatin1String( "%1" ), lowerStr ).replace( QLatin1String( "%2" ), upperStr );
268 }
269 
271 {
272  mFormat = element.attribute( QStringLiteral( "format" ),
273  element.attribute( QStringLiteral( "prefix" ), QStringLiteral( " " ) ) + "%1" +
274  element.attribute( QStringLiteral( "separator" ), QStringLiteral( " - " ) ) + "%2" +
275  element.attribute( QStringLiteral( "suffix" ), QStringLiteral( " " ) )
276  );
277  setPrecision( element.attribute( QStringLiteral( "decimalplaces" ), QStringLiteral( "4" ) ).toInt() );
278  mTrimTrailingZeroes = element.attribute( QStringLiteral( "trimtrailingzeroes" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
279 }
280 
282 {
283  element.setAttribute( QStringLiteral( "format" ), mFormat );
284  element.setAttribute( QStringLiteral( "decimalplaces" ), mPrecision );
285  element.setAttribute( QStringLiteral( "trimtrailingzeroes" ), mTrimTrailingZeroes ? "true" : "false" );
286 }
287 
289 
291  : QgsFeatureRenderer( QStringLiteral( "graduatedSymbol" ) )
292  , mAttrName( attrName )
293  , mMode( Custom )
294  , mGraduatedMethod( GraduatedColor )
295  , mAttrNum( -1 )
296  , mCounting( false )
297 
298 {
299  // TODO: check ranges for sanity (NULL symbols, invalid ranges)
300 
301  //important - we need a deep copy of the ranges list, not a shared copy. This is required because
302  //QgsRendererRange::symbol() is marked const, and so retrieving the symbol via this method does not
303  //trigger a detachment and copy of mRanges BUT that same method CAN be used to modify a symbol in place
304  Q_FOREACH ( const QgsRendererRange &range, ranges )
305  {
306  mRanges << range;
307  }
308 
309 }
310 
312 {
313  mRanges.clear(); // should delete all the symbols
314 }
315 
317 {
318  Q_FOREACH ( const QgsRendererRange &range, mRanges )
319  {
320  if ( range.lowerValue() <= value && range.upperValue() >= value )
321  {
322  if ( range.renderState() || mCounting )
323  return range.symbol();
324  else
325  return nullptr;
326  }
327  }
328  // the value is out of the range: return NULL instead of symbol
329  return nullptr;
330 }
331 
333 {
334  int i = 0;
335  Q_FOREACH ( const QgsRendererRange &range, mRanges )
336  {
337  if ( range.lowerValue() <= value && range.upperValue() >= value )
338  {
339  if ( range.renderState() || mCounting )
340  return QString::number( i );
341  else
342  return QString();
343  }
344  i++;
345  }
346  // the value is out of the range: return NULL
347  return QString();
348 }
349 
351 {
352  return originalSymbolForFeature( feature, context );
353 }
354 
355 QVariant QgsGraduatedSymbolRenderer::valueForFeature( QgsFeature &feature, QgsRenderContext &context ) const
356 {
357  QgsAttributes attrs = feature.attributes();
358  QVariant value;
359  if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
360  {
361  value = mExpression->evaluate( &context.expressionContext() );
362  }
363  else
364  {
365  value = attrs.at( mAttrNum );
366  }
367 
368  return value;
369 }
370 
372 {
373  QVariant value = valueForFeature( feature, context );
374 
375  // Null values should not be categorized
376  if ( value.isNull() )
377  return nullptr;
378 
379  // find the right category
380  return symbolForValue( value.toDouble() );
381 }
382 
384 {
385  mCounting = context.rendererScale() == 0.0;
386 
387  // find out classification attribute index from name
388  mAttrNum = fields.lookupField( mAttrName );
389 
390  if ( mAttrNum == -1 )
391  {
392  mExpression.reset( new QgsExpression( mAttrName ) );
393  mExpression->prepare( &context.expressionContext() );
394  }
395 
396  Q_FOREACH ( const QgsRendererRange &range, mRanges )
397  {
398  if ( !range.symbol() )
399  continue;
400 
401  range.symbol()->startRender( context, fields );
402  }
403 }
404 
406 {
407  Q_FOREACH ( const QgsRendererRange &range, mRanges )
408  {
409  if ( !range.symbol() )
410  continue;
411 
412  range.symbol()->stopRender( context );
413  }
414 }
415 
417 {
418  QSet<QString> attributes;
419 
420  // mAttrName can contain either attribute name or an expression.
421  // Sometimes it is not possible to distinguish between those two,
422  // e.g. "a - b" can be both a valid attribute name or expression.
423  // Since we do not have access to fields here, try both options.
424  attributes << mAttrName;
425 
426  QgsExpression testExpr( mAttrName );
427  if ( !testExpr.hasParserError() )
428  attributes.unite( testExpr.referencedColumns() );
429 
430  QgsRangeList::const_iterator range_it = mRanges.constBegin();
431  for ( ; range_it != mRanges.constEnd(); ++range_it )
432  {
433  QgsSymbol *symbol = range_it->symbol();
434  if ( symbol )
435  {
436  attributes.unite( symbol->usedAttributes( context ) );
437  }
438  }
439  return attributes;
440 }
441 
443 {
444  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
445  return false;
446  mRanges[rangeIndex].setSymbol( symbol );
447  return true;
448 }
449 
450 bool QgsGraduatedSymbolRenderer::updateRangeLabel( int rangeIndex, const QString &label )
451 {
452  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
453  return false;
454  mRanges[rangeIndex].setLabel( label );
455  return true;
456 }
457 
458 bool QgsGraduatedSymbolRenderer::updateRangeUpperValue( int rangeIndex, double value )
459 {
460  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
461  return false;
462  QgsRendererRange &range = mRanges[rangeIndex];
463  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
464  range.setUpperValue( value );
465  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
466  return true;
467 }
468 
469 bool QgsGraduatedSymbolRenderer::updateRangeLowerValue( int rangeIndex, double value )
470 {
471  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
472  return false;
473  QgsRendererRange &range = mRanges[rangeIndex];
474  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
475  range.setLowerValue( value );
476  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
477  return true;
478 }
479 
480 bool QgsGraduatedSymbolRenderer::updateRangeRenderState( int rangeIndex, bool value )
481 {
482  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
483  return false;
484  mRanges[rangeIndex].setRenderState( value );
485  return true;
486 }
487 
489 {
490  QString s = QStringLiteral( "GRADUATED: attr %1\n" ).arg( mAttrName );
491  for ( int i = 0; i < mRanges.count(); i++ )
492  s += mRanges[i].dump();
493  return s;
494 }
495 
497 {
499  r->setMode( mMode );
500  if ( mSourceSymbol )
501  r->setSourceSymbol( mSourceSymbol->clone() );
502  if ( mSourceColorRamp )
503  {
504  r->setSourceColorRamp( mSourceColorRamp->clone() );
505  }
508  r->setLabelFormat( labelFormat() );
510  copyRendererData( r );
511  return r;
512 }
513 
514 void QgsGraduatedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
515 {
516  QgsStringMap newProps = props;
517  newProps[ QStringLiteral( "attribute" )] = mAttrName;
518  newProps[ QStringLiteral( "method" )] = graduatedMethodStr( mGraduatedMethod );
519 
520  // create a Rule for each range
521  bool first = true;
522  for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
523  {
524  it->toSld( doc, element, newProps, first );
525  first = false;
526  }
527 }
528 
530 {
531  Q_UNUSED( context );
532  QgsSymbolList lst;
533  lst.reserve( mRanges.count() );
534  Q_FOREACH ( const QgsRendererRange &range, mRanges )
535  {
536  lst.append( range.symbol() );
537  }
538  return lst;
539 }
540 
541 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
542 {
543 
544  // Equal interval algorithm
545  //
546  // Returns breaks based on dividing the range ('minimum' to 'maximum')
547  // into 'classes' parts.
548 
549  double step = ( maximum - minimum ) / classes;
550 
551  QList<double> breaks;
552  double value = minimum;
553  breaks.reserve( classes );
554  for ( int i = 0; i < classes; i++ )
555  {
556  value += step;
557  breaks.append( value );
558  }
559 
560  // floating point arithmetics is not precise:
561  // set the last break to be exactly maximum so we do not miss it
562  breaks[classes - 1] = maximum;
563 
564  return breaks;
565 }
566 
567 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
568 {
569  // q-th quantile of a data set:
570  // value where q fraction of data is below and (1-q) fraction is above this value
571  // Xq = (1 - r) * X_NI1 + r * X_NI2
572  // NI1 = (int) (q * (n+1))
573  // NI2 = NI1 + 1
574  // r = q * (n+1) - (int) (q * (n+1))
575  // (indices of X: 1...n)
576 
577  // sort the values first
578  std::sort( values.begin(), values.end() );
579 
580  QList<double> breaks;
581 
582  // If there are no values to process: bail out
583  if ( values.isEmpty() )
584  return breaks;
585 
586  int n = values.count();
587  double Xq = n > 0 ? values[0] : 0.0;
588 
589  breaks.reserve( classes );
590  for ( int i = 1; i < classes; i++ )
591  {
592  if ( n > 1 )
593  {
594  double q = i / static_cast< double >( classes );
595  double a = q * ( n - 1 );
596  int aa = static_cast< int >( a );
597 
598  double r = a - aa;
599  Xq = ( 1 - r ) * values[aa] + r * values[aa + 1];
600  }
601  breaks.append( Xq );
602  }
603 
604  breaks.append( values[ n - 1 ] );
605 
606  return breaks;
607 }
608 
609 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<double> &labels )
610 {
611 
612  // C++ implementation of the standard deviation class interval algorithm
613  // as implemented in the 'classInt' package available for the R statistical
614  // prgramming language.
615 
616  // Returns breaks based on 'prettyBreaks' of the centred and scaled
617  // values of 'values', and may have a number of classes different from 'classes'.
618 
619  // If there are no values to process: bail out
620  if ( values.isEmpty() )
621  return QList<double>();
622 
623  double mean = 0.0;
624  double stdDev = 0.0;
625  int n = values.count();
626  double minimum = values[0];
627  double maximum = values[0];
628 
629  for ( int i = 0; i < n; i++ )
630  {
631  mean += values[i];
632  minimum = qMin( values[i], minimum ); // could use precomputed max and min
633  maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
634  }
635  mean = mean / static_cast< double >( n );
636 
637  double sd = 0.0;
638  for ( int i = 0; i < n; i++ )
639  {
640  sd = values[i] - mean;
641  stdDev += sd * sd;
642  }
643  stdDev = sqrt( stdDev / n );
644 
645  QList<double> breaks = QgsSymbolLayerUtils::prettyBreaks( ( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
646  for ( int i = 0; i < breaks.count(); i++ )
647  {
648  labels.append( breaks[i] );
649  breaks[i] = ( breaks[i] * stdDev ) + mean;
650  }
651 
652  return breaks;
653 } // _calcStdDevBreaks
654 
655 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
656  double minimum, double maximum,
657  int maximumSize = 3000 )
658 {
659  // Jenks Optimal (Natural Breaks) algorithm
660  // Based on the Jenks algorithm from the 'classInt' package available for
661  // the R statistical prgramming language, and from Python code from here:
662  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
663  // and is based on a JAVA and Fortran code available here:
664  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
665 
666  // Returns class breaks such that classes are internally homogeneous while
667  // assuring heterogeneity among classes.
668 
669  if ( values.isEmpty() )
670  return QList<double>();
671 
672  if ( classes <= 1 )
673  {
674  return QList<double>() << maximum;
675  }
676 
677  if ( classes >= values.size() )
678  {
679  return values;
680  }
681 
682  QVector<double> sample;
683 
684  // if we have lots of values, we need to take a random sample
685  if ( values.size() > maximumSize )
686  {
687  // for now, sample at least maximumSize values or a 10% sample, whichever
688  // is larger. This will produce a more representative sample for very large
689  // layers, but could end up being computationally intensive...
690 
691  sample.resize( qMax( maximumSize, values.size() / 10 ) );
692 
693  QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
694  QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
695 
696  sample[ 0 ] = minimum;
697  sample[ 1 ] = maximum;
698  for ( int i = 2; i < sample.size(); i++ )
699  {
700  // pick a random integer from 0 to n
701  double r = qrand();
702  int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
703  sample[ i ] = values[ j ];
704  }
705  }
706  else
707  {
708  sample = values.toVector();
709  }
710 
711  int n = sample.size();
712 
713  // sort the sample values
714  std::sort( sample.begin(), sample.end() );
715 
716  QVector< QVector<int> > matrixOne( n + 1 );
717  QVector< QVector<double> > matrixTwo( n + 1 );
718 
719  for ( int i = 0; i <= n; i++ )
720  {
721  matrixOne[i].resize( classes + 1 );
722  matrixTwo[i].resize( classes + 1 );
723  }
724 
725  for ( int i = 1; i <= classes; i++ )
726  {
727  matrixOne[0][i] = 1;
728  matrixOne[1][i] = 1;
729  matrixTwo[0][i] = 0.0;
730  for ( int j = 2; j <= n; j++ )
731  {
732  matrixTwo[j][i] = std::numeric_limits<double>::max();
733  }
734  }
735 
736  for ( int l = 2; l <= n; l++ )
737  {
738  double s1 = 0.0;
739  double s2 = 0.0;
740  int w = 0;
741 
742  double v = 0.0;
743 
744  for ( int m = 1; m <= l; m++ )
745  {
746  int i3 = l - m + 1;
747 
748  double val = sample[ i3 - 1 ];
749 
750  s2 += val * val;
751  s1 += val;
752  w++;
753 
754  v = s2 - ( s1 * s1 ) / static_cast< double >( w );
755  int i4 = i3 - 1;
756  if ( i4 != 0 )
757  {
758  for ( int j = 2; j <= classes; j++ )
759  {
760  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
761  {
762  matrixOne[l][j] = i4;
763  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
764  }
765  }
766  }
767  }
768  matrixOne[l][1] = 1;
769  matrixTwo[l][1] = v;
770  }
771 
772  QVector<double> breaks( classes );
773  breaks[classes - 1] = sample[n - 1];
774 
775  for ( int j = classes, k = n; j >= 2; j-- )
776  {
777  int id = matrixOne[k][j] - 1;
778  breaks[j - 2] = sample[id];
779  k = matrixOne[k][j] - 1;
780  }
781 
782  return breaks.toList();
783 } //_calcJenksBreaks
784 
785 
787  QgsVectorLayer *vlayer,
788  const QString &attrName,
789  int classes,
790  Mode mode,
791  QgsSymbol *symbol,
792  QgsColorRamp *ramp,
794 )
795 {
797  QgsGraduatedSymbolRenderer *r = new QgsGraduatedSymbolRenderer( attrName, ranges );
798  r->setSourceSymbol( symbol->clone() );
799  r->setSourceColorRamp( ramp->clone() );
800  r->setMode( mode );
801  r->setLabelFormat( labelFormat );
802  r->updateClasses( vlayer, mode, classes );
803  return r;
804 }
805 
807 {
808  if ( mAttrName.isEmpty() )
809  return;
810 
811  setMode( mode );
812  // Custom classes are not recalculated
813  if ( mode == Custom )
814  return;
815 
816  if ( nclasses < 1 )
817  nclasses = 1;
818 
819  QList<double> values;
820  bool valuesLoaded = false;
821  double minimum;
822  double maximum;
823 
824  int attrNum = vlayer->fields().lookupField( mAttrName );
825 
826  bool ok;
827  if ( attrNum == -1 )
828  {
829  values = vlayer->getDoubleValues( mAttrName, ok );
830  if ( !ok || values.isEmpty() )
831  return;
832 
833  auto result = std::minmax_element( values.begin(), values.end() );
834  minimum = *result.first;
835  maximum = *result.second;
836  valuesLoaded = true;
837  }
838  else
839  {
840  minimum = vlayer->minimumValue( attrNum ).toDouble();
841  maximum = vlayer->maximumValue( attrNum ).toDouble();
842  }
843 
844  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
845  QList<double> breaks;
846  QList<double> labels;
847  if ( mode == EqualInterval )
848  {
849  breaks = _calcEqualIntervalBreaks( minimum, maximum, nclasses );
850  }
851  else if ( mode == Pretty )
852  {
853  breaks = QgsSymbolLayerUtils::prettyBreaks( minimum, maximum, nclasses );
854  }
855  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
856  {
857  // get values from layer
858  if ( !valuesLoaded )
859  {
860  values = vlayer->getDoubleValues( mAttrName, ok );
861  }
862 
863  // calculate the breaks
864  if ( mode == Quantile )
865  {
866  breaks = _calcQuantileBreaks( values, nclasses );
867  }
868  else if ( mode == Jenks )
869  {
870  breaks = _calcJenksBreaks( values, nclasses, minimum, maximum );
871  }
872  else if ( mode == StdDev )
873  {
874  breaks = _calcStdDevBreaks( values, nclasses, labels );
875  }
876  }
877  else
878  {
879  Q_ASSERT( false );
880  }
881 
882  double lower, upper = minimum;
883  QString label;
885 
886  // "breaks" list contains all values at class breaks plus maximum as last break
887 
888  int i = 0;
889  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
890  {
891  lower = upper; // upper border from last interval
892  upper = *it;
893 
894  // Label - either StdDev label or default label for a range
895  if ( mode == StdDev )
896  {
897  if ( i == 0 )
898  {
899  label = "< " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
900  }
901  else if ( i == labels.count() - 1 )
902  {
903  label = ">= " + QString::number( labels[i - 1], 'f', 2 ) + " Std Dev";
904  }
905  else
906  {
907  label = QString::number( labels[i - 1], 'f', 2 ) + " Std Dev" + " - " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
908  }
909  }
910  else
911  {
912  label = mLabelFormat.labelForRange( lower, upper );
913  }
914  QgsSymbol *newSymbol = mSourceSymbol ? mSourceSymbol->clone() : QgsSymbol::defaultSymbol( vlayer->geometryType() );
915  addClass( QgsRendererRange( lower, upper, newSymbol, label ) );
916  }
917  updateColorRamp( nullptr );
918 }
919 
921 {
922  QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
923  if ( symbolsElem.isNull() )
924  return nullptr;
925 
926  QDomElement rangesElem = element.firstChildElement( QStringLiteral( "ranges" ) );
927  if ( rangesElem.isNull() )
928  return nullptr;
929 
930  QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
932 
933  QDomElement rangeElem = rangesElem.firstChildElement();
934  while ( !rangeElem.isNull() )
935  {
936  if ( rangeElem.tagName() == QLatin1String( "range" ) )
937  {
938  double lowerValue = rangeElem.attribute( QStringLiteral( "lower" ) ).toDouble();
939  double upperValue = rangeElem.attribute( QStringLiteral( "upper" ) ).toDouble();
940  QString symbolName = rangeElem.attribute( QStringLiteral( "symbol" ) );
941  QString label = rangeElem.attribute( QStringLiteral( "label" ) );
942  bool render = rangeElem.attribute( QStringLiteral( "render" ), QStringLiteral( "true" ) ) != QLatin1String( "false" );
943  if ( symbolMap.contains( symbolName ) )
944  {
945  QgsSymbol *symbol = symbolMap.take( symbolName );
946  ranges.append( QgsRendererRange( lowerValue, upperValue, symbol, label, render ) );
947  }
948  }
949  rangeElem = rangeElem.nextSiblingElement();
950  }
951 
952  QString attrName = element.attribute( QStringLiteral( "attr" ) );
953 
954  QgsGraduatedSymbolRenderer *r = new QgsGraduatedSymbolRenderer( attrName, ranges );
955 
956  QString attrMethod = element.attribute( QStringLiteral( "graduatedMethod" ) );
957  if ( !attrMethod.isEmpty() )
958  {
959  if ( attrMethod == graduatedMethodStr( GraduatedColor ) )
961  else if ( attrMethod == graduatedMethodStr( GraduatedSize ) )
963  }
964 
965 
966  // delete symbols if there are any more
968 
969  // try to load source symbol (optional)
970  QDomElement sourceSymbolElem = element.firstChildElement( QStringLiteral( "source-symbol" ) );
971  if ( !sourceSymbolElem.isNull() )
972  {
973  QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
974  if ( sourceSymbolMap.contains( QStringLiteral( "0" ) ) )
975  {
976  r->setSourceSymbol( sourceSymbolMap.take( QStringLiteral( "0" ) ) );
977  }
978  QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
979  }
980 
981  // try to load color ramp (optional)
982  QDomElement sourceColorRampElem = element.firstChildElement( QStringLiteral( "colorramp" ) );
983  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
984  {
985  r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
986  }
987 
988  // try to load mode
989  QDomElement modeElem = element.firstChildElement( QStringLiteral( "mode" ) );
990  if ( !modeElem.isNull() )
991  {
992  QString modeString = modeElem.attribute( QStringLiteral( "name" ) );
993  if ( modeString == QLatin1String( "equal" ) )
994  r->setMode( EqualInterval );
995  else if ( modeString == QLatin1String( "quantile" ) )
996  r->setMode( Quantile );
997  else if ( modeString == QLatin1String( "jenks" ) )
998  r->setMode( Jenks );
999  else if ( modeString == QLatin1String( "stddev" ) )
1000  r->setMode( StdDev );
1001  else if ( modeString == QLatin1String( "pretty" ) )
1002  r->setMode( Pretty );
1003  }
1004 
1005  QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
1006  if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
1007  {
1008  Q_FOREACH ( const QgsRendererRange &range, r->mRanges )
1009  {
1010  convertSymbolRotation( range.symbol(), rotationElem.attribute( QStringLiteral( "field" ) ) );
1011  }
1012  if ( r->mSourceSymbol )
1013  {
1014  convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
1015  }
1016  }
1017 
1018  QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
1019  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
1020  {
1021  Q_FOREACH ( const QgsRendererRange &range, r->mRanges )
1022  {
1023  convertSymbolSizeScale( range.symbol(),
1024  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
1025  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
1026  }
1027  if ( r->mSourceSymbol && r->mSourceSymbol->type() == QgsSymbol::Marker )
1028  {
1030  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
1031  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
1032  }
1033  }
1034 
1035  QDomElement labelFormatElem = element.firstChildElement( QStringLiteral( "labelformat" ) );
1036  if ( ! labelFormatElem.isNull() )
1037  {
1039  labelFormat.setFromDomElement( labelFormatElem );
1040  r->setLabelFormat( labelFormat );
1041  }
1042 
1043  QDomElement ddsLegendSizeElem = element.firstChildElement( "data-defined-size-legend" );
1044  if ( !ddsLegendSizeElem.isNull() )
1045  {
1046  r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
1047  }
1048 
1049  // TODO: symbol levels
1050  return r;
1051 }
1052 
1053 QDomElement QgsGraduatedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
1054 {
1055  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1056  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "graduatedSymbol" ) );
1057  rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? "1" : "0" ) );
1058  rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? "1" : "0" ) );
1059  rendererElem.setAttribute( QStringLiteral( "attr" ), mAttrName );
1060  rendererElem.setAttribute( QStringLiteral( "graduatedMethod" ), graduatedMethodStr( mGraduatedMethod ) );
1061 
1062  // ranges
1063  int i = 0;
1065  QDomElement rangesElem = doc.createElement( QStringLiteral( "ranges" ) );
1066  QgsRangeList::const_iterator it = mRanges.constBegin();
1067  for ( ; it != mRanges.constEnd(); ++it )
1068  {
1069  const QgsRendererRange &range = *it;
1070  QString symbolName = QString::number( i );
1071  symbols.insert( symbolName, range.symbol() );
1072 
1073  QDomElement rangeElem = doc.createElement( QStringLiteral( "range" ) );
1074  rangeElem.setAttribute( QStringLiteral( "lower" ), QString::number( range.lowerValue(), 'f', 15 ) );
1075  rangeElem.setAttribute( QStringLiteral( "upper" ), QString::number( range.upperValue(), 'f', 15 ) );
1076  rangeElem.setAttribute( QStringLiteral( "symbol" ), symbolName );
1077  rangeElem.setAttribute( QStringLiteral( "label" ), range.label() );
1078  rangeElem.setAttribute( QStringLiteral( "render" ), range.renderState() ? "true" : "false" );
1079  rangesElem.appendChild( rangeElem );
1080  i++;
1081  }
1082 
1083  rendererElem.appendChild( rangesElem );
1084 
1085  // save symbols
1086  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
1087  rendererElem.appendChild( symbolsElem );
1088 
1089  // save source symbol
1090  if ( mSourceSymbol )
1091  {
1092  QgsSymbolMap sourceSymbols;
1093  sourceSymbols.insert( QStringLiteral( "0" ), mSourceSymbol.get() );
1094  QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, QStringLiteral( "source-symbol" ), doc, context );
1095  rendererElem.appendChild( sourceSymbolElem );
1096  }
1097 
1098  // save source color ramp
1099  if ( mSourceColorRamp )
1100  {
1101  QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), mSourceColorRamp.get(), doc );
1102  rendererElem.appendChild( colorRampElem );
1103  }
1104 
1105  // save mode
1106  QString modeString;
1107  if ( mMode == EqualInterval )
1108  modeString = QStringLiteral( "equal" );
1109  else if ( mMode == Quantile )
1110  modeString = QStringLiteral( "quantile" );
1111  else if ( mMode == Jenks )
1112  modeString = QStringLiteral( "jenks" );
1113  else if ( mMode == StdDev )
1114  modeString = QStringLiteral( "stddev" );
1115  else if ( mMode == Pretty )
1116  modeString = QStringLiteral( "pretty" );
1117  if ( !modeString.isEmpty() )
1118  {
1119  QDomElement modeElem = doc.createElement( QStringLiteral( "mode" ) );
1120  modeElem.setAttribute( QStringLiteral( "name" ), modeString );
1121  rendererElem.appendChild( modeElem );
1122  }
1123 
1124  QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
1125  rendererElem.appendChild( rotationElem );
1126 
1127  QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
1128  rendererElem.appendChild( sizeScaleElem );
1129 
1130  QDomElement labelFormatElem = doc.createElement( QStringLiteral( "labelformat" ) );
1131  mLabelFormat.saveToDomElement( labelFormatElem );
1132  rendererElem.appendChild( labelFormatElem );
1133 
1135  mPaintEffect->saveProperties( doc, rendererElem );
1136 
1137  if ( !mOrderBy.isEmpty() )
1138  {
1139  QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
1140  mOrderBy.save( orderBy );
1141  rendererElem.appendChild( orderBy );
1142  }
1143  rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? "1" : "0" ) );
1144 
1145  if ( mDataDefinedSizeLegend )
1146  {
1147  QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
1148  mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
1149  rendererElem.appendChild( ddsLegendElem );
1150  }
1151 
1152  return rendererElem;
1153 }
1154 
1155 QgsLegendSymbolList QgsGraduatedSymbolRenderer::baseLegendSymbolItems() const
1156 {
1157  QgsLegendSymbolList lst;
1158  int i = 0;
1159  Q_FOREACH ( const QgsRendererRange &range, mRanges )
1160  {
1161  lst << QgsLegendSymbolItem( range.symbol(), range.label(), QString::number( i++ ), true );
1162  }
1163  return lst;
1164 }
1165 
1167 {
1169  {
1170  // check that all symbols that have the same size expression
1171  QgsProperty ddSize;
1172  Q_FOREACH ( const QgsRendererRange &range, mRanges )
1173  {
1174  const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( range.symbol() );
1175  if ( ddSize )
1176  {
1177  QgsProperty sSize( symbol->dataDefinedSize() );
1178  if ( sSize && sSize != ddSize )
1179  {
1180  // no common size expression
1181  return baseLegendSymbolItems();
1182  }
1183  }
1184  else
1185  {
1186  ddSize = symbol->dataDefinedSize();
1187  }
1188  }
1189 
1190  if ( ddSize && ddSize.isActive() )
1191  {
1192  QgsLegendSymbolList lst;
1193 
1195  ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
1196  lst += ddSizeLegend.legendSymbolList();
1197 
1198  lst += baseLegendSymbolItems();
1199  return lst;
1200  }
1201  }
1202 
1203  return baseLegendSymbolItems();
1204 }
1205 
1207 {
1208  QVariant value = valueForFeature( feature, context );
1209 
1210  // Null values should not be categorized
1211  if ( value.isNull() )
1212  return QSet< QString >();
1213 
1214  // find the right category
1215  QString key = legendKeyForValue( value.toDouble() );
1216  if ( !key.isNull() )
1217  return QSet< QString >() << key;
1218  else
1219  return QSet< QString >();
1220 }
1221 
1223 {
1224  return mSourceSymbol.get();
1225 }
1227 {
1228  mSourceSymbol.reset( sym );
1229 }
1230 
1232 {
1233  return mSourceColorRamp.get();
1234 }
1235 
1237 {
1238  if ( ramp == mSourceColorRamp.get() )
1239  return;
1240 
1241  mSourceColorRamp.reset( ramp );
1242 }
1243 
1245 {
1246  double min = DBL_MAX;
1247  for ( int i = 0; i < mRanges.count(); i++ )
1248  {
1249  double sz = 0;
1250  if ( mRanges[i].symbol()->type() == QgsSymbol::Marker )
1251  sz = static_cast< QgsMarkerSymbol * >( mRanges[i].symbol() )->size();
1252  else if ( mRanges[i].symbol()->type() == QgsSymbol::Line )
1253  sz = static_cast< QgsLineSymbol * >( mRanges[i].symbol() )->width();
1254  min = qMin( sz, min );
1255  }
1256  return min;
1257 }
1258 
1260 {
1261  double max = DBL_MIN;
1262  for ( int i = 0; i < mRanges.count(); i++ )
1263  {
1264  double sz = 0;
1265  if ( mRanges[i].symbol()->type() == QgsSymbol::Marker )
1266  sz = static_cast< QgsMarkerSymbol * >( mRanges[i].symbol() )->size();
1267  else if ( mRanges[i].symbol()->type() == QgsSymbol::Line )
1268  sz = static_cast< QgsLineSymbol * >( mRanges[i].symbol() )->width();
1269  max = qMax( sz, max );
1270  }
1271  return max;
1272 }
1273 
1274 void QgsGraduatedSymbolRenderer::setSymbolSizes( double minSize, double maxSize )
1275 {
1276  for ( int i = 0; i < mRanges.count(); i++ )
1277  {
1278  std::unique_ptr<QgsSymbol> symbol( mRanges.at( i ).symbol() ? mRanges.at( i ).symbol()->clone() : nullptr );
1279  const double size = mRanges.count() > 1
1280  ? minSize + i * ( maxSize - minSize ) / ( mRanges.count() - 1 )
1281  : .5 * ( maxSize + minSize );
1282  if ( symbol->type() == QgsSymbol::Marker )
1283  static_cast< QgsMarkerSymbol * >( symbol.get() )->setSize( size );
1284  if ( symbol->type() == QgsSymbol::Line )
1285  static_cast< QgsLineSymbol * >( symbol.get() )->setWidth( size );
1286  updateRangeSymbol( i, symbol.release() );
1287  }
1288 }
1289 
1291 {
1292  int i = 0;
1293  if ( ramp )
1294  {
1295  setSourceColorRamp( ramp );
1296  }
1297 
1298  if ( mSourceColorRamp )
1299  {
1300  Q_FOREACH ( const QgsRendererRange &range, mRanges )
1301  {
1302  QgsSymbol *symbol = range.symbol() ? range.symbol()->clone() : nullptr;
1303  if ( symbol )
1304  {
1305  double colorValue;
1306  colorValue = ( mRanges.count() > 1 ? static_cast< double >( i ) / ( mRanges.count() - 1 ) : 0 );
1307  symbol->setColor( mSourceColorRamp->color( colorValue ) );
1308  }
1309  updateRangeSymbol( i, symbol );
1310  ++i;
1311  }
1312  }
1313 
1314 }
1315 
1317 {
1318  if ( !sym )
1319  return;
1320 
1321  int i = 0;
1322  Q_FOREACH ( const QgsRendererRange &range, mRanges )
1323  {
1324  std::unique_ptr<QgsSymbol> symbol( sym->clone() );
1326  {
1327  symbol->setColor( range.symbol()->color() );
1328  }
1329  else if ( mGraduatedMethod == GraduatedSize )
1330  {
1331  if ( symbol->type() == QgsSymbol::Marker )
1332  static_cast<QgsMarkerSymbol *>( symbol.get() )->setSize(
1333  static_cast<QgsMarkerSymbol *>( range.symbol() )->size() );
1334  else if ( symbol->type() == QgsSymbol::Line )
1335  static_cast<QgsLineSymbol *>( symbol.get() )->setWidth(
1336  static_cast<QgsLineSymbol *>( range.symbol() )->width() );
1337  }
1338  updateRangeSymbol( i, symbol.release() );
1339  ++i;
1340  }
1341  setSourceSymbol( sym->clone() );
1342 }
1343 
1345 {
1346  return true;
1347 }
1348 
1350 {
1351  bool ok;
1352  int index = key.toInt( &ok );
1353  if ( ok && index >= 0 && index < mRanges.size() )
1354  return mRanges.at( index ).renderState();
1355  else
1356  return true;
1357 }
1358 
1359 void QgsGraduatedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
1360 {
1361  bool ok;
1362  int index = key.toInt( &ok );
1363  if ( ok )
1364  updateRangeRenderState( index, state );
1365 }
1366 
1368 {
1369  bool ok;
1370  int index = key.toInt( &ok );
1371  if ( ok )
1372  updateRangeSymbol( index, symbol );
1373  else
1374  delete symbol;
1375 }
1376 
1378 {
1379  QgsSymbol *newSymbol = symbol->clone();
1380  QString label = QStringLiteral( "0.0 - 0.0" );
1381  mRanges.insert( 0, QgsRendererRange( 0.0, 0.0, newSymbol, label ) );
1382 }
1383 
1384 void QgsGraduatedSymbolRenderer::addClass( double lower, double upper )
1385 {
1386  QgsSymbol *newSymbol = mSourceSymbol->clone();
1387  QString label = mLabelFormat.labelForRange( lower, upper );
1388  mRanges.append( QgsRendererRange( lower, upper, newSymbol, label ) );
1389 }
1390 
1392 {
1393  QMutableListIterator< QgsRendererRange > it( mRanges );
1394  while ( it.hasNext() )
1395  {
1396  QgsRendererRange range = it.next();
1397  if ( range.lowerValue() < breakValue && range.upperValue() > breakValue )
1398  {
1399  QgsRendererRange newRange = QgsRendererRange();
1400  newRange.setLowerValue( breakValue );
1401  newRange.setUpperValue( range.upperValue() );
1402  newRange.setLabel( mLabelFormat.labelForRange( newRange ) );
1403  newRange.setSymbol( mSourceSymbol->clone() );
1404 
1405  //update old range
1406  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
1407  range.setUpperValue( breakValue );
1408  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range.lowerValue(), breakValue ) );
1409  it.setValue( range );
1410 
1411  it.insert( newRange );
1412  break;
1413  }
1414  }
1415 
1416  if ( updateSymbols )
1417  {
1418  switch ( mGraduatedMethod )
1419  {
1420  case GraduatedColor:
1422  break;
1423  case GraduatedSize:
1425  break;
1426  }
1427  }
1428 }
1429 
1431 {
1432  mRanges.append( range );
1433 }
1434 
1436 {
1437  mRanges.removeAt( idx );
1438 }
1439 
1441 {
1442  mRanges.clear();
1443 }
1444 
1446 {
1447  if ( updateRanges && labelFormat != mLabelFormat )
1448  {
1449  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1450  {
1451  it->setLabel( labelFormat.labelForRange( *it ) );
1452  }
1453  }
1455 }
1456 
1457 
1459 {
1460  // Find the minimum size of a class
1461  double minClassRange = 0.0;
1462  Q_FOREACH ( const QgsRendererRange &rendererRange, mRanges )
1463  {
1464  double range = rendererRange.upperValue() - rendererRange.lowerValue();
1465  if ( range <= 0.0 )
1466  continue;
1467  if ( minClassRange == 0.0 || range < minClassRange )
1468  minClassRange = range;
1469  }
1470  if ( minClassRange <= 0.0 )
1471  return;
1472 
1473  // Now set the number of decimal places to ensure no more than 20% error in
1474  // representing this range (up to 10% at upper and lower end)
1475 
1476  int ndp = 10;
1477  double nextDpMinRange = 0.0000000099;
1478  while ( ndp > 0 && nextDpMinRange < minClassRange )
1479  {
1480  ndp--;
1481  nextDpMinRange *= 10.0;
1482  }
1483  mLabelFormat.setPrecision( ndp );
1484  if ( updateRanges ) setLabelFormat( mLabelFormat, true );
1485 }
1486 
1488 {
1489  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1490  return;
1491  mRanges.move( from, to );
1492 }
1493 
1495 {
1496  return r1 < r2;
1497 }
1498 
1500 {
1501  return !valueLessThan( r1, r2 );
1502 }
1503 
1504 void QgsGraduatedSymbolRenderer::sortByValue( Qt::SortOrder order )
1505 {
1506  if ( order == Qt::AscendingOrder )
1507  {
1508  std::sort( mRanges.begin(), mRanges.end(), valueLessThan );
1509  }
1510  else
1511  {
1512  std::sort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1513  }
1514 }
1515 
1517 {
1518  QgsRangeList sortedRanges = mRanges;
1519  std::sort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1520 
1521  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1522  if ( it == sortedRanges.constEnd() )
1523  return false;
1524 
1525  if ( ( *it ).upperValue() < ( *it ).lowerValue() )
1526  return true;
1527 
1528  double prevMax = ( *it ).upperValue();
1529  ++it;
1530 
1531  for ( ; it != sortedRanges.constEnd(); ++it )
1532  {
1533  if ( ( *it ).upperValue() < ( *it ).lowerValue() )
1534  return true;
1535 
1536  if ( ( *it ).lowerValue() < prevMax )
1537  return true;
1538 
1539  prevMax = ( *it ).upperValue();
1540  }
1541  return false;
1542 }
1543 
1545 {
1546  QgsRangeList sortedRanges = mRanges;
1547  std::sort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1548 
1549  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1550  if ( it == sortedRanges.constEnd() )
1551  return false;
1552 
1553  double prevMax = ( *it ).upperValue();
1554  ++it;
1555 
1556  for ( ; it != sortedRanges.constEnd(); ++it )
1557  {
1558  if ( !qgsDoubleNear( ( *it ).lowerValue(), prevMax ) )
1559  return true;
1560 
1561  prevMax = ( *it ).upperValue();
1562  }
1563  return false;
1564 }
1565 
1567 {
1568  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1569 }
1570 
1572 {
1573  return !labelLessThan( r1, r2 );
1574 }
1575 
1576 void QgsGraduatedSymbolRenderer::sortByLabel( Qt::SortOrder order )
1577 {
1578  if ( order == Qt::AscendingOrder )
1579  {
1580  std::sort( mRanges.begin(), mRanges.end(), labelLessThan );
1581  }
1582  else
1583  {
1584  std::sort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1585  }
1586 }
1587 
1589 {
1590  QgsGraduatedSymbolRenderer *r = nullptr;
1591  if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1592  {
1593  r = dynamic_cast<QgsGraduatedSymbolRenderer *>( renderer->clone() );
1594  }
1595  else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1596  {
1597  const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
1598  if ( pointDistanceRenderer )
1599  r = convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1600  }
1601  else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1602  {
1603  const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
1604  if ( invertedPolygonRenderer )
1605  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1606  }
1607 
1608  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1609  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
1610 
1611  if ( !r )
1612  {
1613  r = new QgsGraduatedSymbolRenderer( QLatin1String( "" ), QgsRangeList() );
1614  QgsRenderContext context;
1615  QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
1616  if ( !symbols.isEmpty() )
1617  {
1618  r->setSourceSymbol( symbols.at( 0 )->clone() );
1619  }
1620  }
1621 
1622  r->setOrderBy( renderer->orderBy() );
1623  r->setOrderByEnabled( renderer->orderByEnabled() );
1624 
1625  return r;
1626 }
1627 
1629 {
1630  mDataDefinedSizeLegend.reset( settings );
1631 }
1632 
1634 {
1635  return mDataDefinedSizeLegend.get();
1636 }
1637 
1639 {
1640  switch ( method )
1641  {
1642  case GraduatedColor:
1643  return "GraduatedColor";
1644  case GraduatedSize:
1645  return "GraduatedSize";
1646  }
1647  return "";
1648 }
1649 
1650 
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
int lookupField(const QString &fieldName) const
Look up field&#39;s index from the field name.
Definition: qgsfields.cpp:289
const QgsRendererRangeLabelFormat & labelFormat() const
Return the label format used to generate default classification labels.
static QgsGraduatedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
creates a QgsGraduatedSymbolRenderer from an existing renderer.
The class is used as a container of context for various read/write operations on other objects...
virtual void stopRender(QgsRenderContext &context) override
Needs to be called when a render cycle has finished to clean up.
double rendererScale() const
Returns the renderer map scale.
static QgsSymbol::ScaleMethod decodeScaleMethod(const QString &str)
std::unique_ptr< QgsSymbol > mSourceSymbol
QList< QgsLegendSymbolItem > QgsLegendSymbolList
QgsFeatureRequest::OrderBy mOrderBy
Definition: qgsrenderer.h:472
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
bool labelLessThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
create renderer from XML element
bool updateRangeUpperValue(int rangeIndex, double value)
QList< QgsRendererRange > QgsRangeList
bool rangesOverlap() const
Tests whether classes assigned to the renderer have ranges which overlap.
void saveToDomElement(QDomElement &element)
void setLabel(const QString &label)
virtual QgsColorRamp * clone() const =0
Creates a clone of the color ramp.
#define QgsDebugMsg(str)
Definition: qgslogger.h:37
QVariant minimumValue(int index) const override
Returns the minimum value for an attribute column or an invalid variant in case of error...
QgsFeatureRequest::OrderBy orderBy() const
Get the order in which features shall be processed by this renderer.
Abstract base class for color ramps.
Definition: qgscolorramp.h:30
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
void addBreak(double breakValue, bool updateSymbols=true)
Add a breakpoint by splitting existing classes so that the specified value becomes a break between tw...
static QDomElement saveColorRamp(const QString &name, QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp&#39;s settings to an XML element.
void setGraduatedMethod(GraduatedMethod method)
set the method used for graduation (either size or color)
QgsSymbol * symbolForValue(double value)
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
Needs to be called when a new render cycle is started.
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
Container of fields for a vector layer.
Definition: qgsfields.h:41
void setRenderState(bool render)
#define RENDERER_TAG_NAME
Definition: qgsrenderer.h:49
void setUsingSymbolLevels(bool usingSymbolLevels)
Definition: qgsrenderer.h:250
static void clearSymbolMap(QgsSymbolMap &symbols)
QList< double > getDoubleValues(const QString &fieldOrExpression, bool &ok, bool selectedOnly=false, int *nullCount=nullptr, QgsFeedback *feedback=nullptr) const
Fetches all double values from a specified field name or expression.
QString formatNumber(double value) const
QgsRendererRange & operator=(QgsRendererRange range)
QgsPaintEffect * mPaintEffect
Definition: qgsrenderer.h:458
Line symbol.
Definition: qgssymbol.h:85
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:61
void setSymbolSizes(double minSize, double maxSize)
set varying symbol size for classes
bool operator<(const QgsRendererRange &other) const
QgsLegendSymbolList legendSymbolList() const
Generates legend symbol items according to the configuration.
static const char * graduatedMethodStr(GraduatedMethod method)
std::unique_ptr< QgsExpression > mExpression
void setUpperValue(double upperValue)
QMap< QString, QString > QgsStringMap
Definition: qgis.h:340
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:203
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:384
void setTrimTrailingZeroes(bool trimTrailingZeroes)
virtual void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
bool rangesHaveGaps() const
Tests whether classes assigned to the renderer have gaps between the ranges.
virtual void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
bool updateRangeSymbol(int rangeIndex, QgsSymbol *symbol)
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:43
static QgsSymbol * defaultSymbol(QgsWkbTypes::GeometryType geomType)
return new default symbol for specified geometry type
Definition: qgssymbol.cpp:254
QString type() const
Definition: qgsrenderer.h:124
QgsFields fields() const override
Returns the list of fields of this layer.
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
bool updateRangeLowerValue(int rangeIndex, double value)
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
void setLowerValue(double lowerValue)
bool labelGreaterThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:191
QgsColorRamp * sourceColorRamp()
Returns the source color ramp, from which each classes&#39; color is derived.
void updateFromSymbolAndProperty(const QgsMarkerSymbol *symbol, const QgsProperty &ddSize)
Updates the list of classes, source symbol and title label from given symbol and property.
static QgsColorRamp * loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
QColor color() const
Definition: qgssymbol.cpp:437
static void convertSymbolSizeScale(QgsSymbol *symbol, QgsSymbol::ScaleMethod method, const QString &field)
double ANALYSIS_EXPORT min(double x, double y)
Returns the minimum of two doubles or the first argument if both are equal.
Definition: MathUtils.cc:452
A store for object properties.
Definition: qgsproperty.h:189
bool valueLessThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
static QgsGraduatedSymbolRenderer * createRenderer(QgsVectorLayer *vlayer, const QString &attrName, int classes, Mode mode, QgsSymbol *symbol, QgsColorRamp *ramp, const QgsRendererRangeLabelFormat &legendFormat=QgsRendererRangeLabelFormat())
Creates a new graduated renderer.
void swap(QgsRendererRange &other)
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
Definition: MathUtils.cc:437
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend when using data-defined size for marker symbols...
bool operator==(const QgsRendererRangeLabelFormat &other) const
virtual QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Return a list of attributes required by this renderer.
void setOrderBy(const QgsFeatureRequest::OrderBy &orderBy)
Define the order in which features shall be processed by this renderer.
void moveClass(int from, int to)
Moves the category at index position from to index position to.
QgsExpressionContext & expressionContext()
Gets the expression context.
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1346
void setFormat(const QString &format)
void calculateLabelPrecision(bool updateRanges=true)
Reset the label decimal places to a numberbased on the minimum class interval.
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...
void updateClasses(QgsVectorLayer *vlayer, Mode mode, int nclasses)
Recalculate classes for a layer.
QVariant maximumValue(int index) const override
Returns the maximum value for an attribute column or an invalid variant in case of error...
static void convertSymbolRotation(QgsSymbol *symbol, const QString &field)
virtual bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
Marker symbol.
Definition: qgssymbol.h:84
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
double minSymbolSize() const
return the min symbol size when graduated by size
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Return a list of attributes required to render this feature.
Definition: qgssymbol.cpp:631
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
Contains information about the context of a rendering operation.
QgsRendererRangeLabelFormat mLabelFormat
bool usingSymbolLevels() const
Definition: qgsrenderer.h:249
bool operator!=(const QgsRendererRangeLabelFormat &other) const
QgsLegendSymbolList legendSymbolItems() const override
Returns a list of symbology items for the legend.
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
GraduatedMethod graduatedMethod() const
return the method used for graduation (either size or color)
QgsGraduatedSymbolRenderer(const QString &attrName=QString(), const QgsRangeList &ranges=QgsRangeList())
virtual QgsSymbol * originalSymbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return symbol for feature.
double maxSymbolSize() const
return the max symbol size when graduated by size
virtual QgsSymbol * clone() const =0
Get a deep copy of this symbol.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
QString labelForRange(double lower, double upper) const
virtual QString dump() const override
Returns debug information about this renderer.
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props, bool firstRange=false) const
Creates a DOM element representing the range in SLD format.
virtual void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props=QgsStringMap()) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
virtual QgsGraduatedSymbolRenderer * clone() const override
Create a deep copy of this renderer.
void updateColorRamp(QgsColorRamp *ramp=0)
Update the color ramp used.
QMap< QString, QgsSymbol *> QgsSymbolMap
Definition: qgsrenderer.h:44
std::unique_ptr< QgsSymbol > mSymbol
virtual QSet< QString > legendKeysForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return legend keys matching a specified feature.
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:48
virtual QgsSymbolList symbols(QgsRenderContext &context) override
Returns list of symbols used by the renderer.
bool updateRangeRenderState(int rangeIndex, bool render)
bool valueGreaterThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
bool updateRangeLabel(int rangeIndex, const QString &label)
virtual bool legendSymbolItemChecked(const QString &key) override
items of symbology items in legend is checked
virtual QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
store renderer info to XML element
virtual QgsSymbol * symbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
To be overridden.
int mAttrNum
attribute index (derived from attribute name in startRender)
int ANALYSIS_EXPORT lower(int n, int i)
Lower function.
Definition: MathUtils.cc:425
std::unique_ptr< QgsColorRamp > mSourceColorRamp
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
void setFromDomElement(QDomElement &element)
QString legendKeyForValue(double value) const
Returns the matching legend key for a value.
A vector of attributes.
Definition: qgsattributes.h:57
Represents a vector layer which manages a vector based data sets.
Object that keeps configuration of appearance of marker symbol&#39;s data-defined size in legend...
std::unique_ptr< QgsDataDefinedSizeLegend > mDataDefinedSizeLegend
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:406
const QgsRangeList & ranges() const
static QgsDataDefinedSizeLegend * readXml(const QDomElement &elem, const QgsReadWriteContext &context) SIP_FACTORY
Creates instance from given element and returns it (caller takes ownership). Returns null on error...
void setSourceColorRamp(QgsColorRamp *ramp)
Sets the source color ramp.
void updateSymbols(QgsSymbol *sym)
Update all the symbols but leave breaks and colors.
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer...
QgsSymbol * sourceSymbol()
Returns the renderer&#39;s source symbol, which is the base symbol used for the each classes&#39; symbol befo...
void setSourceSymbol(QgsSymbol *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each classes&#39; symbol b...
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
QgsAttributes attributes
Definition: qgsfeature.h:71
bool isActive() const
Returns whether the property is currently active.
void setLabelFormat(const QgsRendererRangeLabelFormat &labelFormat, bool updateRanges=false)
Set the label format used to generate default classification labels.
void setColor(const QColor &color)
Definition: qgssymbol.cpp:428
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.