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