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