QGIS API Documentation  3.6.0-Noosa (5873452)
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  Q_FOREACH ( const QgsRendererRange &range, ranges )
289  {
290  mRanges << range;
291  }
292 }
293 
295 {
296  mRanges.clear(); // should delete all the symbols
297 }
298 
300 {
301  Q_FOREACH ( const QgsRendererRange &range, mRanges )
302  {
303  if ( range.lowerValue() <= value && range.upperValue() >= value )
304  {
305  if ( range.renderState() || mCounting )
306  return range.symbol();
307  else
308  return nullptr;
309  }
310  }
311  // the value is out of the range: return NULL instead of symbol
312  return nullptr;
313 }
314 
316 {
317  int i = 0;
318  Q_FOREACH ( const QgsRendererRange &range, mRanges )
319  {
320  if ( range.lowerValue() <= value && range.upperValue() >= value )
321  {
322  if ( range.renderState() || mCounting )
323  return QString::number( i );
324  else
325  return QString();
326  }
327  i++;
328  }
329  // the value is out of the range: return NULL
330  return QString();
331 }
332 
334 {
335  return originalSymbolForFeature( feature, context );
336 }
337 
338 QVariant QgsGraduatedSymbolRenderer::valueForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
339 {
340  QgsAttributes attrs = feature.attributes();
341  QVariant value;
342  if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
343  {
344  value = mExpression->evaluate( &context.expressionContext() );
345  }
346  else
347  {
348  value = attrs.at( mAttrNum );
349  }
350 
351  return value;
352 }
353 
355 {
356  QVariant value = valueForFeature( feature, context );
357 
358  // Null values should not be categorized
359  if ( value.isNull() )
360  return nullptr;
361 
362  // find the right category
363  return symbolForValue( value.toDouble() );
364 }
365 
367 {
368  QgsFeatureRenderer::startRender( context, fields );
369 
370  mCounting = context.rendererScale() == 0.0;
371 
372  // find out classification attribute index from name
373  mAttrNum = fields.lookupField( mAttrName );
374 
375  if ( mAttrNum == -1 )
376  {
377  mExpression.reset( new QgsExpression( mAttrName ) );
378  mExpression->prepare( &context.expressionContext() );
379  }
380 
381  Q_FOREACH ( const QgsRendererRange &range, mRanges )
382  {
383  if ( !range.symbol() )
384  continue;
385 
386  range.symbol()->startRender( context, fields );
387  }
388 }
389 
391 {
393 
394  Q_FOREACH ( const QgsRendererRange &range, mRanges )
395  {
396  if ( !range.symbol() )
397  continue;
398 
399  range.symbol()->stopRender( context );
400  }
401 }
402 
404 {
405  QSet<QString> attributes;
406 
407  // mAttrName can contain either attribute name or an expression.
408  // Sometimes it is not possible to distinguish between those two,
409  // e.g. "a - b" can be both a valid attribute name or expression.
410  // Since we do not have access to fields here, try both options.
411  attributes << mAttrName;
412 
413  QgsExpression testExpr( mAttrName );
414  if ( !testExpr.hasParserError() )
415  attributes.unite( testExpr.referencedColumns() );
416 
417  QgsRangeList::const_iterator range_it = mRanges.constBegin();
418  for ( ; range_it != mRanges.constEnd(); ++range_it )
419  {
420  QgsSymbol *symbol = range_it->symbol();
421  if ( symbol )
422  {
423  attributes.unite( symbol->usedAttributes( context ) );
424  }
425  }
426  return attributes;
427 }
428 
430 {
431  QgsExpression testExpr( mAttrName );
432  if ( !testExpr.hasParserError() )
433  {
434  QgsExpressionContext context;
435  context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) ); // unfortunately no layer access available!
436  testExpr.prepare( &context );
437  return testExpr.needsGeometry();
438  }
439  return false;
440 }
441 
443 {
444  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
445  return false;
446  mRanges[rangeIndex].setSymbol( symbol );
447  return true;
448 }
449 
450 bool QgsGraduatedSymbolRenderer::updateRangeLabel( int rangeIndex, const QString &label )
451 {
452  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
453  return false;
454  mRanges[rangeIndex].setLabel( label );
455  return true;
456 }
457 
458 bool QgsGraduatedSymbolRenderer::updateRangeUpperValue( int rangeIndex, double value )
459 {
460  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
461  return false;
462  QgsRendererRange &range = mRanges[rangeIndex];
463  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
464  range.setUpperValue( value );
465  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
466  return true;
467 }
468 
469 bool QgsGraduatedSymbolRenderer::updateRangeLowerValue( int rangeIndex, double value )
470 {
471  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
472  return false;
473  QgsRendererRange &range = mRanges[rangeIndex];
474  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
475  range.setLowerValue( value );
476  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
477  return true;
478 }
479 
480 bool QgsGraduatedSymbolRenderer::updateRangeRenderState( int rangeIndex, bool value )
481 {
482  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
483  return false;
484  mRanges[rangeIndex].setRenderState( value );
485  return true;
486 }
487 
489 {
490  QString s = QStringLiteral( "GRADUATED: attr %1\n" ).arg( mAttrName );
491  for ( int i = 0; i < mRanges.count(); i++ )
492  s += mRanges[i].dump();
493  return s;
494 }
495 
497 {
499  r->setMode( mMode );
503  r->setAstride( mAstride );
504 
505  if ( mSourceSymbol )
506  r->setSourceSymbol( mSourceSymbol->clone() );
507  if ( mSourceColorRamp )
508  {
509  r->setSourceColorRamp( mSourceColorRamp->clone() );
510  }
513  r->setLabelFormat( labelFormat() );
515  copyRendererData( r );
516  return r;
517 }
518 
519 void QgsGraduatedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
520 {
521  QgsStringMap newProps = props;
522  newProps[ QStringLiteral( "attribute" )] = mAttrName;
523  newProps[ QStringLiteral( "method" )] = graduatedMethodStr( mGraduatedMethod );
524 
525  // create a Rule for each range
526  bool first = true;
527  for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
528  {
529  it->toSld( doc, element, newProps, first );
530  first = false;
531  }
532 }
533 
535 {
536  Q_UNUSED( context );
537  QgsSymbolList lst;
538  lst.reserve( mRanges.count() );
539  Q_FOREACH ( const QgsRendererRange &range, mRanges )
540  {
541  lst.append( range.symbol() );
542  }
543  return lst;
544 }
545 
546 void QgsGraduatedSymbolRenderer::makeBreaksSymmetric( QList<double> &breaks, double symmetryPoint, bool astride )
547 {
548  // remove the breaks that are above the existing opposite sign classes
549  // to keep colors symmetrically balanced around symmetryPoint
550  // if astride is true, remove the symmetryPoint break so that
551  // the 2 classes form only one
552 
553  if ( breaks.size() > 1 ) //to avoid crash when only 1 class
554  {
555  std::sort( breaks.begin(), breaks.end() );
556  // breaks contain the maximum of the distrib but not the minimum
557  double distBelowSymmetricValue = std::fabs( breaks[0] - symmetryPoint );
558  double distAboveSymmetricValue = std::fabs( breaks[ breaks.size() - 2 ] - symmetryPoint ) ;
559  double absMin = std::min( distAboveSymmetricValue, distBelowSymmetricValue );
560 
561  // make symmetric
562  for ( int i = 0; i <= breaks.size() - 2; ++i )
563  {
564  // part after "absMin" is for doubles rounding issues
565  if ( std::fabs( breaks.at( i ) - symmetryPoint ) >= ( absMin - std::fabs( breaks[0] - breaks[1] ) / 100. ) )
566  {
567  breaks.removeAt( i );
568  --i;
569  }
570  }
571  // remove symmetry point
572  if ( astride ) // && breaks.indexOf( symmetryPoint ) != -1) // if symmetryPoint is found
573  {
574  breaks.removeAt( breaks.indexOf( symmetryPoint ) );
575  }
576  }
577 }
578 
579 QList<double> QgsGraduatedSymbolRenderer::calcEqualIntervalBreaks( double minimum, double maximum, int classes, bool useSymmetricMode, double symmetryPoint, bool astride )
580 {
581  // Equal interval algorithm
582  // Returns breaks based on dividing the range ('minimum' to 'maximum') into 'classes' parts.
583  QList<double> breaks;
584  if ( !useSymmetricMode ) // nomal mode
585  {
586  double step = ( maximum - minimum ) / classes;
587 
588  double value = minimum;
589  breaks.reserve( classes );
590  for ( int i = 0; i < classes; i++ )
591  {
592  value += step;
593  breaks.append( value );
594  }
595  // floating point arithmetics is not precise:
596  // set the last break to be exactly maximum so we do not miss it
597  breaks[classes - 1] = maximum;
598  }
599  else if ( useSymmetricMode ) // symmetric mode
600  {
601  double distBelowSymmetricValue = std::abs( minimum - symmetryPoint );
602  double distAboveSymmetricValue = std::abs( maximum - symmetryPoint ) ;
603 
604  if ( astride )
605  {
606  if ( classes % 2 == 0 ) // we want odd number of classes
607  ++classes;
608  }
609  else
610  {
611  if ( classes % 2 == 1 ) // we want even number of classes
612  ++classes;
613  }
614  double step = 2 * std::min( distBelowSymmetricValue, distAboveSymmetricValue ) / classes;
615 
616  breaks.reserve( classes );
617  double value = ( distBelowSymmetricValue < distAboveSymmetricValue ) ? minimum : maximum - classes * step;
618 
619  for ( int i = 0; i < classes; i++ )
620  {
621  value += step;
622  breaks.append( value );
623  }
624  breaks[classes - 1] = maximum;
625  }
626  return breaks;
627 }
628 
629 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
630 {
631  // q-th quantile of a data set:
632  // value where q fraction of data is below and (1-q) fraction is above this value
633  // Xq = (1 - r) * X_NI1 + r * X_NI2
634  // NI1 = (int) (q * (n+1))
635  // NI2 = NI1 + 1
636  // r = q * (n+1) - (int) (q * (n+1))
637  // (indices of X: 1...n)
638 
639  // sort the values first
640  std::sort( values.begin(), values.end() );
641 
642  QList<double> breaks;
643 
644  // If there are no values to process: bail out
645  if ( values.isEmpty() )
646  return breaks;
647 
648  int n = values.count();
649  double Xq = n > 0 ? values[0] : 0.0;
650 
651  breaks.reserve( classes );
652  for ( int i = 1; i < classes; i++ )
653  {
654  if ( n > 1 )
655  {
656  double q = i / static_cast< double >( classes );
657  double a = q * ( n - 1 );
658  int aa = static_cast< int >( a );
659 
660  double r = a - aa;
661  Xq = ( 1 - r ) * values[aa] + r * values[aa + 1];
662  }
663  breaks.append( Xq );
664  }
665 
666  breaks.append( values[ n - 1 ] );
667 
668  return breaks;
669 }
670 
671 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<double> &labels, bool useSymmetricMode, double symmetryPoint, bool astride )
672 {
673 
674  // C++ implementation of the standard deviation class interval algorithm
675  // as implemented in the 'classInt' package available for the R statistical
676  // prgramming language.
677 
678  // Returns breaks based on 'prettyBreaks' of the centred and scaled
679  // values of 'values', and may have a number of classes different from 'classes'.
680 
681  // If there are no values to process: bail out
682  if ( values.isEmpty() )
683  return QList<double>();
684 
685  double mean = 0.0;
686  double stdDev = 0.0;
687  int n = values.count();
688  double minimum = values[0];
689  double maximum = values[0];
690 
691  for ( int i = 0; i < n; i++ )
692  {
693  mean += values[i];
694  minimum = std::min( values[i], minimum ); // could use precomputed max and min
695  maximum = std::max( values[i], maximum ); // but have to go through entire list anyway
696  }
697  mean = mean / static_cast< double >( n );
698 
699  double sd = 0.0;
700  for ( int i = 0; i < n; i++ )
701  {
702  sd = values[i] - mean;
703  stdDev += sd * sd;
704  }
705  stdDev = std::sqrt( stdDev / n );
706 
707  if ( !useSymmetricMode )
708  symmetryPoint = mean; // otherwise symmetryPoint = symmetryPoint
709 
710  QList<double> breaks = QgsSymbolLayerUtils::prettyBreaks( ( minimum - symmetryPoint ) / stdDev, ( maximum - symmetryPoint ) / stdDev, classes );
711  QgsGraduatedSymbolRenderer::makeBreaksSymmetric( breaks, 0.0, astride ); //0.0 because breaks where computed on a centered distribution
712 
713  for ( int i = 0; i < breaks.count(); i++ ) //unNormalize breaks and put labels
714  {
715  labels.append( breaks[i] );
716  breaks[i] = ( breaks[i] * stdDev ) + symmetryPoint;
717  }
718  return breaks;
719 } // _calcStdDevBreaks
720 
721 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
722  double minimum, double maximum,
723  int maximumSize = 3000 )
724 {
725  // Jenks Optimal (Natural Breaks) algorithm
726  // Based on the Jenks algorithm from the 'classInt' package available for
727  // the R statistical prgramming language, and from Python code from here:
728  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
729  // and is based on a JAVA and Fortran code available here:
730  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
731 
732  // Returns class breaks such that classes are internally homogeneous while
733  // assuring heterogeneity among classes.
734 
735  if ( values.isEmpty() )
736  return QList<double>();
737 
738  if ( classes <= 1 )
739  {
740  return QList<double>() << maximum;
741  }
742 
743  if ( classes >= values.size() )
744  {
745  return values;
746  }
747 
748  QVector<double> sample;
749 
750  // if we have lots of values, we need to take a random sample
751  if ( values.size() > maximumSize )
752  {
753  // for now, sample at least maximumSize values or a 10% sample, whichever
754  // is larger. This will produce a more representative sample for very large
755  // layers, but could end up being computationally intensive...
756 
757  sample.resize( std::max( maximumSize, values.size() / 10 ) );
758 
759  QgsDebugMsg( QStringLiteral( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
760  QgsDebugMsg( QStringLiteral( "values:%1" ).arg( values.size() ) );
761 
762  sample[ 0 ] = minimum;
763  sample[ 1 ] = maximum;
764  for ( int i = 2; i < sample.size(); i++ )
765  {
766  // pick a random integer from 0 to n
767  double r = qrand();
768  int j = std::floor( r / RAND_MAX * ( values.size() - 1 ) );
769  sample[ i ] = values[ j ];
770  }
771  }
772  else
773  {
774  sample = values.toVector();
775  }
776 
777  int n = sample.size();
778 
779  // sort the sample values
780  std::sort( sample.begin(), sample.end() );
781 
782  QVector< QVector<int> > matrixOne( n + 1 );
783  QVector< QVector<double> > matrixTwo( n + 1 );
784 
785  for ( int i = 0; i <= n; i++ )
786  {
787  matrixOne[i].resize( classes + 1 );
788  matrixTwo[i].resize( classes + 1 );
789  }
790 
791  for ( int i = 1; i <= classes; i++ )
792  {
793  matrixOne[0][i] = 1;
794  matrixOne[1][i] = 1;
795  matrixTwo[0][i] = 0.0;
796  for ( int j = 2; j <= n; j++ )
797  {
798  matrixTwo[j][i] = std::numeric_limits<double>::max();
799  }
800  }
801 
802  for ( int l = 2; l <= n; l++ )
803  {
804  double s1 = 0.0;
805  double s2 = 0.0;
806  int w = 0;
807 
808  double v = 0.0;
809 
810  for ( int m = 1; m <= l; m++ )
811  {
812  int i3 = l - m + 1;
813 
814  double val = sample[ i3 - 1 ];
815 
816  s2 += val * val;
817  s1 += val;
818  w++;
819 
820  v = s2 - ( s1 * s1 ) / static_cast< double >( w );
821  int i4 = i3 - 1;
822  if ( i4 != 0 )
823  {
824  for ( int j = 2; j <= classes; j++ )
825  {
826  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
827  {
828  matrixOne[l][j] = i4;
829  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
830  }
831  }
832  }
833  }
834  matrixOne[l][1] = 1;
835  matrixTwo[l][1] = v;
836  }
837 
838  QVector<double> breaks( classes );
839  breaks[classes - 1] = sample[n - 1];
840 
841  for ( int j = classes, k = n; j >= 2; j-- )
842  {
843  int id = matrixOne[k][j] - 1;
844  breaks[j - 2] = sample[id];
845  k = matrixOne[k][j] - 1;
846  }
847 
848  return breaks.toList();
849 } //_calcJenksBreaks
850 
851 static QStringList _breaksAsStrings( const QList<double> &breaks ) // get QStringList from QList<double> without maxi break (min is not in)
852 {
853  QStringList breaksAsStrings;
854  for ( int i = 0; i < breaks.count() - 1; i++ )
855  {
856  breaksAsStrings << QString::number( breaks.at( i ), 'f', 2 );
857  }
858  return breaksAsStrings;
859 }
860 
862  QgsVectorLayer *vlayer,
863  const QString &attrName,
864  int classes,
865  Mode mode,
866  QgsSymbol *symbol,
867  QgsColorRamp *ramp,
869  bool useSymmetricMode,
870  double symmetryPoint,
871  QStringList listForCboPrettyBreaks,
872  bool astride
873 )
874 {
876  QgsGraduatedSymbolRenderer *r = new QgsGraduatedSymbolRenderer( attrName, ranges );
877  r->setSourceSymbol( symbol->clone() );
878  r->setSourceColorRamp( ramp->clone() );
879  r->setMode( mode );
880  r->setUseSymmetricMode( useSymmetricMode );
881  r->setSymmetryPoint( symmetryPoint );
882  r->setListForCboPrettyBreaks( listForCboPrettyBreaks );
883  r->setAstride( astride );
884  r->setLabelFormat( labelFormat );
885  r->updateClasses( vlayer, mode, classes, useSymmetricMode, symmetryPoint, astride );
886  return r;
887 }
888 
890  bool useSymmetricMode, double symmetryPoint, bool astride )
891 {
892  if ( mAttrName.isEmpty() )
893  return;
894  setMode( mode );
895  setSymmetryPoint( symmetryPoint );
896  setUseSymmetricMode( useSymmetricMode );
897  setAstride( astride );
898 
899  // Custom classes are not recalculated
900  if ( mode == Custom )
901  return;
902 
903  if ( nclasses < 1 )
904  nclasses = 1;
905 
906  QList<double> values;
907  bool valuesLoaded = false;
908  double minimum;
909  double maximum;
910 
911  int attrNum = vlayer->fields().lookupField( mAttrName );
912 
913  bool ok;
914  if ( attrNum == -1 )
915  {
916  values = QgsVectorLayerUtils::getDoubleValues( vlayer, mAttrName, ok );
917  if ( !ok || values.isEmpty() )
918  return;
919 
920  auto result = std::minmax_element( values.begin(), values.end() );
921  minimum = *result.first;
922  maximum = *result.second;
923  valuesLoaded = true;
924  }
925  else
926  {
927  minimum = vlayer->minimumValue( attrNum ).toDouble();
928  maximum = vlayer->maximumValue( attrNum ).toDouble();
929  }
930 
931  QgsDebugMsg( QStringLiteral( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
932  QList<double> breaks;
933  QList<double> labels;
934 
935  switch ( mode )
936  {
937  case EqualInterval:
938  {
939  breaks = QgsGraduatedSymbolRenderer::calcEqualIntervalBreaks( minimum, maximum, nclasses, mUseSymmetricMode, symmetryPoint, astride );
940  break;
941  }
942 
943  case Pretty:
944  {
945  breaks = QgsSymbolLayerUtils::prettyBreaks( minimum, maximum, nclasses );
946  setListForCboPrettyBreaks( _breaksAsStrings( breaks ) );
947 
948  if ( useSymmetricMode )
949  QgsGraduatedSymbolRenderer::makeBreaksSymmetric( breaks, symmetryPoint, astride );
950  break;
951  }
952 
953  case Quantile:
954  case Jenks:
955  case StdDev:
956  {
957  // get values from layer
958  if ( !valuesLoaded )
959  {
960  values = QgsVectorLayerUtils::getDoubleValues( vlayer, mAttrName, ok );
961  }
962  // calculate the breaks
963  if ( mode == Quantile )
964  breaks = _calcQuantileBreaks( values, nclasses );
965  else if ( mode == Jenks )
966  breaks = _calcJenksBreaks( values, nclasses, minimum, maximum );
967  else if ( mode == StdDev )
968  breaks = _calcStdDevBreaks( values, nclasses, labels, mUseSymmetricMode, symmetryPoint, astride );
969  break;
970  }
971 
972  case Custom:
973  Q_ASSERT( false );
974  break;
975  }
976 
977  double lower, upper = minimum;
978  QString label;
980 
981  // "breaks" list contains all values at class breaks plus maximum as last break
982  int i = 0;
983  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
984  {
985  lower = upper; // upper border from last interval
986  upper = *it;
987 
988  // Label - either StdDev label or default label for a range
989  if ( mode == StdDev )
990  {
991  if ( i == 0 )
992  {
993  label = "< " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
994  }
995  else if ( i == labels.count() - 1 )
996  {
997  label = ">= " + QString::number( labels[i - 1], 'f', 2 ) + " Std Dev";
998  }
999  else
1000  {
1001  label = QString::number( labels[i - 1], 'f', 2 ) + " Std Dev" + " - " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
1002  }
1003  }
1004  else
1005  {
1006  label = mLabelFormat.labelForRange( lower, upper );
1007  }
1008  QgsSymbol *newSymbol = mSourceSymbol ? mSourceSymbol->clone() : QgsSymbol::defaultSymbol( vlayer->geometryType() );
1009  addClass( QgsRendererRange( lower, upper, newSymbol, label ) );
1010  }
1011  updateColorRamp( nullptr );
1012 }
1013 
1014 
1016 {
1017  QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
1018  if ( symbolsElem.isNull() )
1019  return nullptr;
1020 
1021  QDomElement rangesElem = element.firstChildElement( QStringLiteral( "ranges" ) );
1022  if ( rangesElem.isNull() )
1023  return nullptr;
1024 
1025  QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
1027 
1028  QDomElement rangeElem = rangesElem.firstChildElement();
1029  while ( !rangeElem.isNull() )
1030  {
1031  if ( rangeElem.tagName() == QLatin1String( "range" ) )
1032  {
1033  double lowerValue = rangeElem.attribute( QStringLiteral( "lower" ) ).toDouble();
1034  double upperValue = rangeElem.attribute( QStringLiteral( "upper" ) ).toDouble();
1035  QString symbolName = rangeElem.attribute( QStringLiteral( "symbol" ) );
1036  QString label = rangeElem.attribute( QStringLiteral( "label" ) );
1037  bool render = rangeElem.attribute( QStringLiteral( "render" ), QStringLiteral( "true" ) ) != QLatin1String( "false" );
1038  if ( symbolMap.contains( symbolName ) )
1039  {
1040  QgsSymbol *symbol = symbolMap.take( symbolName );
1041  ranges.append( QgsRendererRange( lowerValue, upperValue, symbol, label, render ) );
1042  }
1043  }
1044  rangeElem = rangeElem.nextSiblingElement();
1045  }
1046 
1047  QString attrName = element.attribute( QStringLiteral( "attr" ) );
1048 
1049  QgsGraduatedSymbolRenderer *r = new QgsGraduatedSymbolRenderer( attrName, ranges );
1050 
1051  QString attrMethod = element.attribute( QStringLiteral( "graduatedMethod" ) );
1052  if ( !attrMethod.isEmpty() )
1053  {
1054  if ( attrMethod == graduatedMethodStr( GraduatedColor ) )
1056  else if ( attrMethod == graduatedMethodStr( GraduatedSize ) )
1058  }
1059 
1060 
1061  // delete symbols if there are any more
1063 
1064  // try to load source symbol (optional)
1065  QDomElement sourceSymbolElem = element.firstChildElement( QStringLiteral( "source-symbol" ) );
1066  if ( !sourceSymbolElem.isNull() )
1067  {
1068  QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
1069  if ( sourceSymbolMap.contains( QStringLiteral( "0" ) ) )
1070  {
1071  r->setSourceSymbol( sourceSymbolMap.take( QStringLiteral( "0" ) ) );
1072  }
1073  QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
1074  }
1075 
1076  // try to load color ramp (optional)
1077  QDomElement sourceColorRampElem = element.firstChildElement( QStringLiteral( "colorramp" ) );
1078  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
1079  {
1080  r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
1081  }
1082 
1083  // try to load mode
1084  QDomElement modeElem = element.firstChildElement( QStringLiteral( "mode" ) );
1085  if ( !modeElem.isNull() )
1086  {
1087  QString modeString = modeElem.attribute( QStringLiteral( "name" ) );
1088  if ( modeString == QLatin1String( "equal" ) )
1089  r->setMode( EqualInterval );
1090  else if ( modeString == QLatin1String( "quantile" ) )
1091  r->setMode( Quantile );
1092  else if ( modeString == QLatin1String( "jenks" ) )
1093  r->setMode( Jenks );
1094  else if ( modeString == QLatin1String( "stddev" ) )
1095  r->setMode( StdDev );
1096  else if ( modeString == QLatin1String( "pretty" ) )
1097  r->setMode( Pretty );
1098  }
1099 
1100  // symmetric mode
1101  QDomElement symmetricModeElem = element.firstChildElement( QStringLiteral( "symmetricMode" ) );
1102  if ( !symmetricModeElem.isNull() )
1103  {
1104  QString symmetricEnabled = symmetricModeElem.attribute( QStringLiteral( "enabled" ) );
1105  symmetricEnabled == QLatin1String( "true" ) ? r->setUseSymmetricMode( true ) : r->setUseSymmetricMode( false );
1106 
1107  QString symmetricPointString = symmetricModeElem.attribute( QStringLiteral( "symmetryPoint" ) );
1108  r->setSymmetryPoint( symmetricPointString.toDouble() );
1109  QString breaksForPretty = symmetricModeElem.attribute( QStringLiteral( "valueForCboPrettyBreaks" ) );
1110  r->setListForCboPrettyBreaks( breaksForPretty.split( '/' ) );
1111 
1112  QString astrideEnabled = symmetricModeElem.attribute( QStringLiteral( "astride" ) );
1113  astrideEnabled == QLatin1String( "true" ) ? r->setAstride( true ) : r->setAstride( false );
1114  }
1115  QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
1116  if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
1117  {
1118  Q_FOREACH ( const QgsRendererRange &range, r->mRanges )
1119  {
1120  convertSymbolRotation( range.symbol(), rotationElem.attribute( QStringLiteral( "field" ) ) );
1121  }
1122  if ( r->mSourceSymbol )
1123  {
1124  convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
1125  }
1126  }
1127  QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
1128  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
1129  {
1130  Q_FOREACH ( const QgsRendererRange &range, r->mRanges )
1131  {
1132  convertSymbolSizeScale( range.symbol(),
1133  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
1134  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
1135  }
1136  if ( r->mSourceSymbol && r->mSourceSymbol->type() == QgsSymbol::Marker )
1137  {
1139  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
1140  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
1141  }
1142  }
1143 
1144  QDomElement labelFormatElem = element.firstChildElement( QStringLiteral( "labelformat" ) );
1145  if ( ! labelFormatElem.isNull() )
1146  {
1148  labelFormat.setFromDomElement( labelFormatElem );
1149  r->setLabelFormat( labelFormat );
1150  }
1151 
1152  QDomElement ddsLegendSizeElem = element.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
1153  if ( !ddsLegendSizeElem.isNull() )
1154  {
1155  r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
1156  }
1157  // TODO: symbol levels
1158  return r;
1159 }
1160 
1161 QDomElement QgsGraduatedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
1162 {
1163  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1164  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "graduatedSymbol" ) );
1165  rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
1166  rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
1167  rendererElem.setAttribute( QStringLiteral( "attr" ), mAttrName );
1168  rendererElem.setAttribute( QStringLiteral( "graduatedMethod" ), graduatedMethodStr( mGraduatedMethod ) );
1169 
1170  // ranges
1171  int i = 0;
1173  QDomElement rangesElem = doc.createElement( QStringLiteral( "ranges" ) );
1174  QgsRangeList::const_iterator it = mRanges.constBegin();
1175  for ( ; it != mRanges.constEnd(); ++it )
1176  {
1177  const QgsRendererRange &range = *it;
1178  QString symbolName = QString::number( i );
1179  symbols.insert( symbolName, range.symbol() );
1180 
1181  QDomElement rangeElem = doc.createElement( QStringLiteral( "range" ) );
1182  rangeElem.setAttribute( QStringLiteral( "lower" ), QString::number( range.lowerValue(), 'f', 15 ) );
1183  rangeElem.setAttribute( QStringLiteral( "upper" ), QString::number( range.upperValue(), 'f', 15 ) );
1184  rangeElem.setAttribute( QStringLiteral( "symbol" ), symbolName );
1185  rangeElem.setAttribute( QStringLiteral( "label" ), range.label() );
1186  rangeElem.setAttribute( QStringLiteral( "render" ), range.renderState() ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
1187  rangesElem.appendChild( rangeElem );
1188  i++;
1189  }
1190 
1191  rendererElem.appendChild( rangesElem );
1192 
1193  // save symbols
1194  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
1195  rendererElem.appendChild( symbolsElem );
1196 
1197  // save source symbol
1198  if ( mSourceSymbol )
1199  {
1200  QgsSymbolMap sourceSymbols;
1201  sourceSymbols.insert( QStringLiteral( "0" ), mSourceSymbol.get() );
1202  QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, QStringLiteral( "source-symbol" ), doc, context );
1203  rendererElem.appendChild( sourceSymbolElem );
1204  }
1205 
1206  // save source color ramp
1207  if ( mSourceColorRamp )
1208  {
1209  QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), mSourceColorRamp.get(), doc );
1210  rendererElem.appendChild( colorRampElem );
1211  }
1212 
1213  // save mode
1214  QString modeString;
1215  switch ( mMode )
1216  {
1217  case EqualInterval:
1218  modeString = QStringLiteral( "equal" );
1219  break;
1220  case Quantile:
1221  modeString = QStringLiteral( "quantile" );
1222  break;
1223  case Jenks:
1224  modeString = QStringLiteral( "jenks" );
1225  break;
1226  case StdDev:
1227  modeString = QStringLiteral( "stddev" );
1228  break;
1229  case Pretty:
1230  modeString = QStringLiteral( "pretty" );
1231  break;
1232  case Custom:
1233  break;
1234  }
1235  if ( !modeString.isEmpty() )
1236  {
1237  QDomElement modeElem = doc.createElement( QStringLiteral( "mode" ) );
1238  modeElem.setAttribute( QStringLiteral( "name" ), modeString );
1239  rendererElem.appendChild( modeElem );
1240  }
1241 
1242  // symmetry
1243  QDomElement symmetricModeElem = doc.createElement( QStringLiteral( "symmetricMode" ) );
1244  symmetricModeElem.setAttribute( QStringLiteral( "enabled" ), mUseSymmetricMode ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
1245  symmetricModeElem.setAttribute( QStringLiteral( "symmetryPoint" ), mSymmetryPoint );
1246  symmetricModeElem.setAttribute( QStringLiteral( "astride" ), mAstride ? QStringLiteral( "true" ) : QStringLiteral( "false" ) );
1247  if ( Pretty == mMode )
1248  {
1249  QString breaks;
1250  for ( int i = 0; i < mListForCboPrettyBreaks.size() - 1; i++ ) // -1 to write 1/2/3 instead of 1/2/3/
1251  {
1252  breaks.append( mListForCboPrettyBreaks.at( i ) );
1253  breaks.append( '/' );
1254  }
1255  if ( mListForCboPrettyBreaks.size() > 0 ) //make sure we can go at size-1
1256  breaks.append( mListForCboPrettyBreaks.at( mListForCboPrettyBreaks.size() - 1 ) ); //add the last break
1257  symmetricModeElem.setAttribute( QStringLiteral( "valueForCboPrettyBreaks" ), breaks );
1258  }
1259 
1260  rendererElem.appendChild( symmetricModeElem );
1261 
1262  QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
1263  rendererElem.appendChild( rotationElem );
1264 
1265  QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
1266  rendererElem.appendChild( sizeScaleElem );
1267 
1268  QDomElement labelFormatElem = doc.createElement( QStringLiteral( "labelformat" ) );
1269  mLabelFormat.saveToDomElement( labelFormatElem );
1270  rendererElem.appendChild( labelFormatElem );
1271 
1273  mPaintEffect->saveProperties( doc, rendererElem );
1274 
1275  if ( !mOrderBy.isEmpty() )
1276  {
1277  QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
1278  mOrderBy.save( orderBy );
1279  rendererElem.appendChild( orderBy );
1280  }
1281  rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
1282 
1283  if ( mDataDefinedSizeLegend )
1284  {
1285  QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
1286  mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
1287  rendererElem.appendChild( ddsLegendElem );
1288  }
1289 
1290  return rendererElem;
1291 }
1292 
1293 QgsLegendSymbolList QgsGraduatedSymbolRenderer::baseLegendSymbolItems() const
1294 {
1295  QgsLegendSymbolList lst;
1296  int i = 0;
1297  Q_FOREACH ( const QgsRendererRange &range, mRanges )
1298  {
1299  lst << QgsLegendSymbolItem( range.symbol(), range.label(), QString::number( i++ ), true );
1300  }
1301  return lst;
1302 }
1303 
1305 {
1307  {
1308  // check that all symbols that have the same size expression
1309  QgsProperty ddSize;
1310  Q_FOREACH ( const QgsRendererRange &range, mRanges )
1311  {
1312  const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( range.symbol() );
1313  if ( ddSize )
1314  {
1315  QgsProperty sSize( symbol->dataDefinedSize() );
1316  if ( sSize && sSize != ddSize )
1317  {
1318  // no common size expression
1319  return baseLegendSymbolItems();
1320  }
1321  }
1322  else
1323  {
1324  ddSize = symbol->dataDefinedSize();
1325  }
1326  }
1327 
1328  if ( ddSize && ddSize.isActive() )
1329  {
1330  QgsLegendSymbolList lst;
1331 
1333  ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
1334  lst += ddSizeLegend.legendSymbolList();
1335 
1336  lst += baseLegendSymbolItems();
1337  return lst;
1338  }
1339  }
1340 
1341  return baseLegendSymbolItems();
1342 }
1343 
1344 QSet< QString > QgsGraduatedSymbolRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
1345 {
1346  QVariant value = valueForFeature( feature, context );
1347 
1348  // Null values should not be categorized
1349  if ( value.isNull() )
1350  return QSet< QString >();
1351 
1352  // find the right category
1353  QString key = legendKeyForValue( value.toDouble() );
1354  if ( !key.isNull() )
1355  return QSet< QString >() << key;
1356  else
1357  return QSet< QString >();
1358 }
1359 
1361 {
1362  return mSourceSymbol.get();
1363 }
1365 {
1366  mSourceSymbol.reset( sym );
1367 }
1368 
1370 {
1371  return mSourceColorRamp.get();
1372 }
1373 
1375 {
1376  if ( ramp == mSourceColorRamp.get() )
1377  return;
1378 
1379  mSourceColorRamp.reset( ramp );
1380 }
1381 
1383 {
1384  double min = std::numeric_limits<double>::max();
1385  for ( int i = 0; i < mRanges.count(); i++ )
1386  {
1387  double sz = 0;
1388  if ( mRanges[i].symbol()->type() == QgsSymbol::Marker )
1389  sz = static_cast< QgsMarkerSymbol * >( mRanges[i].symbol() )->size();
1390  else if ( mRanges[i].symbol()->type() == QgsSymbol::Line )
1391  sz = static_cast< QgsLineSymbol * >( mRanges[i].symbol() )->width();
1392  min = std::min( sz, min );
1393  }
1394  return min;
1395 }
1396 
1398 {
1399  double max = std::numeric_limits<double>::min();
1400  for ( int i = 0; i < mRanges.count(); i++ )
1401  {
1402  double sz = 0;
1403  if ( mRanges[i].symbol()->type() == QgsSymbol::Marker )
1404  sz = static_cast< QgsMarkerSymbol * >( mRanges[i].symbol() )->size();
1405  else if ( mRanges[i].symbol()->type() == QgsSymbol::Line )
1406  sz = static_cast< QgsLineSymbol * >( mRanges[i].symbol() )->width();
1407  max = std::max( sz, max );
1408  }
1409  return max;
1410 }
1411 
1412 void QgsGraduatedSymbolRenderer::setSymbolSizes( double minSize, double maxSize )
1413 {
1414  for ( int i = 0; i < mRanges.count(); i++ )
1415  {
1416  std::unique_ptr<QgsSymbol> symbol( mRanges.at( i ).symbol() ? mRanges.at( i ).symbol()->clone() : nullptr );
1417  const double size = mRanges.count() > 1
1418  ? minSize + i * ( maxSize - minSize ) / ( mRanges.count() - 1 )
1419  : .5 * ( maxSize + minSize );
1420  if ( symbol->type() == QgsSymbol::Marker )
1421  static_cast< QgsMarkerSymbol * >( symbol.get() )->setSize( size );
1422  if ( symbol->type() == QgsSymbol::Line )
1423  static_cast< QgsLineSymbol * >( symbol.get() )->setWidth( size );
1424  updateRangeSymbol( i, symbol.release() );
1425  }
1426 }
1427 
1429 {
1430  int i = 0;
1431  if ( ramp )
1432  {
1433  setSourceColorRamp( ramp );
1434  }
1435 
1436  if ( mSourceColorRamp )
1437  {
1438  Q_FOREACH ( const QgsRendererRange &range, mRanges )
1439  {
1440  QgsSymbol *symbol = range.symbol() ? range.symbol()->clone() : nullptr;
1441  if ( symbol )
1442  {
1443  double colorValue;
1444  colorValue = ( mRanges.count() > 1 ? static_cast< double >( i ) / ( mRanges.count() - 1 ) : 0 );
1445  symbol->setColor( mSourceColorRamp->color( colorValue ) );
1446  }
1447  updateRangeSymbol( i, symbol );
1448  ++i;
1449  }
1450  }
1451 
1452 }
1453 
1455 {
1456  if ( !sym )
1457  return;
1458 
1459  int i = 0;
1460  Q_FOREACH ( const QgsRendererRange &range, mRanges )
1461  {
1462  std::unique_ptr<QgsSymbol> symbol( sym->clone() );
1464  {
1465  symbol->setColor( range.symbol()->color() );
1466  }
1467  else if ( mGraduatedMethod == GraduatedSize )
1468  {
1469  if ( symbol->type() == QgsSymbol::Marker )
1470  static_cast<QgsMarkerSymbol *>( symbol.get() )->setSize(
1471  static_cast<QgsMarkerSymbol *>( range.symbol() )->size() );
1472  else if ( symbol->type() == QgsSymbol::Line )
1473  static_cast<QgsLineSymbol *>( symbol.get() )->setWidth(
1474  static_cast<QgsLineSymbol *>( range.symbol() )->width() );
1475  }
1476  updateRangeSymbol( i, symbol.release() );
1477  ++i;
1478  }
1479  setSourceSymbol( sym->clone() );
1480 }
1481 
1483 {
1484  return true;
1485 }
1486 
1488 {
1489  bool ok;
1490  int index = key.toInt( &ok );
1491  if ( ok && index >= 0 && index < mRanges.size() )
1492  return mRanges.at( index ).renderState();
1493  else
1494  return true;
1495 }
1496 
1497 void QgsGraduatedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
1498 {
1499  bool ok;
1500  int index = key.toInt( &ok );
1501  if ( ok )
1502  updateRangeRenderState( index, state );
1503 }
1504 
1506 {
1507  bool ok;
1508  int index = key.toInt( &ok );
1509  if ( ok )
1510  updateRangeSymbol( index, symbol );
1511  else
1512  delete symbol;
1513 }
1514 
1516 {
1517  QgsSymbol *newSymbol = symbol->clone();
1518  QString label = QStringLiteral( "0.0 - 0.0" );
1519  mRanges.insert( 0, QgsRendererRange( 0.0, 0.0, newSymbol, label ) );
1520 }
1521 
1522 void QgsGraduatedSymbolRenderer::addClass( double lower, double upper )
1523 {
1524  QgsSymbol *newSymbol = mSourceSymbol->clone();
1525  QString label = mLabelFormat.labelForRange( lower, upper );
1526  mRanges.append( QgsRendererRange( lower, upper, newSymbol, label ) );
1527 }
1528 
1530 {
1531  QMutableListIterator< QgsRendererRange > it( mRanges );
1532  while ( it.hasNext() )
1533  {
1534  QgsRendererRange range = it.next();
1535  if ( range.lowerValue() < breakValue && range.upperValue() > breakValue )
1536  {
1537  QgsRendererRange newRange = QgsRendererRange();
1538  newRange.setLowerValue( breakValue );
1539  newRange.setUpperValue( range.upperValue() );
1540  newRange.setLabel( mLabelFormat.labelForRange( newRange ) );
1541  newRange.setSymbol( mSourceSymbol->clone() );
1542 
1543  //update old range
1544  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
1545  range.setUpperValue( breakValue );
1546  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range.lowerValue(), breakValue ) );
1547  it.setValue( range );
1548 
1549  it.insert( newRange );
1550  break;
1551  }
1552  }
1553 
1554  if ( updateSymbols )
1555  {
1556  switch ( mGraduatedMethod )
1557  {
1558  case GraduatedColor:
1560  break;
1561  case GraduatedSize:
1563  break;
1564  }
1565  }
1566 }
1567 
1569 {
1570  mRanges.append( range );
1571 }
1572 
1574 {
1575  mRanges.removeAt( idx );
1576 }
1577 
1579 {
1580  mRanges.clear();
1581 }
1582 
1584 {
1585  if ( updateRanges && labelFormat != mLabelFormat )
1586  {
1587  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1588  {
1589  it->setLabel( labelFormat.labelForRange( *it ) );
1590  }
1591  }
1593 }
1594 
1595 
1597 {
1598  // Find the minimum size of a class
1599  double minClassRange = 0.0;
1600  Q_FOREACH ( const QgsRendererRange &rendererRange, mRanges )
1601  {
1602  double range = rendererRange.upperValue() - rendererRange.lowerValue();
1603  if ( range <= 0.0 )
1604  continue;
1605  if ( minClassRange == 0.0 || range < minClassRange )
1606  minClassRange = range;
1607  }
1608  if ( minClassRange <= 0.0 )
1609  return;
1610 
1611  // Now set the number of decimal places to ensure no more than 20% error in
1612  // representing this range (up to 10% at upper and lower end)
1613 
1614  int ndp = 10;
1615  double nextDpMinRange = 0.0000000099;
1616  while ( ndp > 0 && nextDpMinRange < minClassRange )
1617  {
1618  ndp--;
1619  nextDpMinRange *= 10.0;
1620  }
1621  mLabelFormat.setPrecision( ndp );
1622  if ( updateRanges ) setLabelFormat( mLabelFormat, true );
1623 }
1624 
1626 {
1627  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1628  return;
1629  mRanges.move( from, to );
1630 }
1631 
1633 {
1634  return r1 < r2;
1635 }
1636 
1638 {
1639  return !valueLessThan( r1, r2 );
1640 }
1641 
1642 void QgsGraduatedSymbolRenderer::sortByValue( Qt::SortOrder order )
1643 {
1644  if ( order == Qt::AscendingOrder )
1645  {
1646  std::sort( mRanges.begin(), mRanges.end(), valueLessThan );
1647  }
1648  else
1649  {
1650  std::sort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1651  }
1652 }
1653 
1655 {
1656  QgsRangeList sortedRanges = mRanges;
1657  std::sort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1658 
1659  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1660  if ( it == sortedRanges.constEnd() )
1661  return false;
1662 
1663  if ( ( *it ).upperValue() < ( *it ).lowerValue() )
1664  return true;
1665 
1666  double prevMax = ( *it ).upperValue();
1667  ++it;
1668 
1669  for ( ; it != sortedRanges.constEnd(); ++it )
1670  {
1671  if ( ( *it ).upperValue() < ( *it ).lowerValue() )
1672  return true;
1673 
1674  if ( ( *it ).lowerValue() < prevMax )
1675  return true;
1676 
1677  prevMax = ( *it ).upperValue();
1678  }
1679  return false;
1680 }
1681 
1683 {
1684  QgsRangeList sortedRanges = mRanges;
1685  std::sort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1686 
1687  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1688  if ( it == sortedRanges.constEnd() )
1689  return false;
1690 
1691  double prevMax = ( *it ).upperValue();
1692  ++it;
1693 
1694  for ( ; it != sortedRanges.constEnd(); ++it )
1695  {
1696  if ( !qgsDoubleNear( ( *it ).lowerValue(), prevMax ) )
1697  return true;
1698 
1699  prevMax = ( *it ).upperValue();
1700  }
1701  return false;
1702 }
1703 
1705 {
1706  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1707 }
1708 
1710 {
1711  return !labelLessThan( r1, r2 );
1712 }
1713 
1714 void QgsGraduatedSymbolRenderer::sortByLabel( Qt::SortOrder order )
1715 {
1716  if ( order == Qt::AscendingOrder )
1717  {
1718  std::sort( mRanges.begin(), mRanges.end(), labelLessThan );
1719  }
1720  else
1721  {
1722  std::sort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1723  }
1724 }
1725 
1727 {
1728  QgsGraduatedSymbolRenderer *r = nullptr;
1729  if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1730  {
1731  r = dynamic_cast<QgsGraduatedSymbolRenderer *>( renderer->clone() );
1732  }
1733  else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1734  {
1735  const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
1736  if ( pointDistanceRenderer )
1737  r = convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1738  }
1739  else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1740  {
1741  const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
1742  if ( invertedPolygonRenderer )
1743  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1744  }
1745 
1746  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1747  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
1748 
1749  if ( !r )
1750  {
1751  r = new QgsGraduatedSymbolRenderer( QString(), QgsRangeList() );
1752  QgsRenderContext context;
1753  QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
1754  if ( !symbols.isEmpty() )
1755  {
1756  r->setSourceSymbol( symbols.at( 0 )->clone() );
1757  }
1758  }
1759 
1760  r->setOrderBy( renderer->orderBy() );
1761  r->setOrderByEnabled( renderer->orderByEnabled() );
1762 
1763  return r;
1764 }
1765 
1767 {
1768  mDataDefinedSizeLegend.reset( settings );
1769 }
1770 
1772 {
1773  return mDataDefinedSizeLegend.get();
1774 }
1775 
1777 {
1778  switch ( method )
1779  {
1780  case GraduatedColor:
1781  return "GraduatedColor";
1782  case GraduatedSize:
1783  return "GraduatedSize";
1784  }
1785  return "";
1786 }
1787 
1788 
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: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: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: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: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:732
void startRender(QgsRenderContext &context, const QgsFields &fields=QgsFields())
Begins the rendering process for the symbol.
Definition: qgssymbol.cpp:406
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:277
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.
QColor color() const
Returns the symbol&#39;s color.
Definition: qgssymbol.cpp:461
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:1428
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:664
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
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:428
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:452
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.