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