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