QGIS API Documentation  3.4.15-Madeira (e83d02e274)
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...
Class for parsing and evaluation of expressions (formerly called "search strings").
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...
QString type() const
Definition: qgsrenderer.h:129
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props, bool firstRange=false) const
Creates a DOM element representing the range in SLD format.
double minSymbolSize() const
Returns the min symbol size when graduated by size.
static QgsSymbol::ScaleMethod decodeScaleMethod(const QString &str)
std::unique_ptr< QgsSymbol > mSourceSymbol
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
double maxSymbolSize() const
Returns the max symbol size when graduated by size.
QList< QgsLegendSymbolItem > QgsLegendSymbolList
bool filterNeedsGeometry() const override
Returns true if this renderer requires the geometry to apply the filter.
QgsFeatureRequest::OrderBy mOrderBy
Definition: qgsrenderer.h:521
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
bool labelLessThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
create renderer from XML element
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
bool updateRangeUpperValue(int rangeIndex, double value)
QList< QgsRendererRange > QgsRangeList
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.
bool isActive() const
Returns whether the property is currently active.
double rendererScale() const
Returns the renderer map scale.
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.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the 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
Abstract base class for color ramps.
Definition: qgscolorramp.h:31
bool usingSymbolLevels() const
Definition: qgsrenderer.h:271
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...
bool astride() const
Returns if we want to have a central class astride the pivot value.
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.
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
static void clearSymbolMap(QgsSymbolMap &symbols)
QgsRendererRange & operator=(QgsRendererRange range)
void setAstride(bool astride)
Set if we want a central class astride the pivot value.
QgsSymbol * symbol() const
QgsPaintEffect * mPaintEffect
Definition: qgsrenderer.h:505
Line symbol.
Definition: qgssymbol.h:86
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
void setSymbolSizes(double minSize, double maxSize)
set varying symbol size for classes
QString labelForRange(double lower, double upper) const
bool useSymmetricMode() const
Returns if we want to classify symmetric around a given value.
static const char * graduatedMethodStr(GraduatedMethod method)
std::unique_ptr< QgsExpression > mExpression
void setUpperValue(double upperValue)
QMap< QString, QString > QgsStringMap
Definition: qgis.h:577
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend when using data-defined size for marker symbols...
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:404
void setTrimTrailingZeroes(bool trimTrailingZeroes)
void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
QString formatNumber(double value) const
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
void setUseSymmetricMode(bool useSymmetricMode)
Set if we want to classify symmetric around a given value.
bool operator==(const QgsRendererRangeLabelFormat &other) const
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
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.
double symmetryPoint() const
Returns the pivot value for symmetric classification.
void setLowerValue(double lowerValue)
bool labelGreaterThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
QgsLegendSymbolList legendSymbolList() const
Generates legend symbol items according to the configuration.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
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.
QgsSymbol * symbolForValue(double value) const
Gets the symbol which is used to represent value.
void updateFromSymbolAndProperty(const QgsMarkerSymbol *symbol, const QgsProperty &ddSize)
Updates the list of classes, source symbol and title label from given symbol and property.
static QgsColorRamp * loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static void convertSymbolSizeScale(QgsSymbol *symbol, QgsSymbol::ScaleMethod method, const QString &field)
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer&#39;s project and layer.
QVariant minimumValue(int index) const FINAL
Returns the minimum value for an attribute column or an invalid variant in case of error...
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:320
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.
const QgsRangeList & ranges() const
A store for object properties.
Definition: qgsproperty.h:229
bool valueLessThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
void swap(QgsRendererRange &other)
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
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.
const QgsRendererRangeLabelFormat & labelFormat() const
Returns the label format used to generate default classification labels.
QgsSymbol * originalSymbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns symbol for feature.
QgsExpressionContext & expressionContext()
Gets the expression context.
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
bool operator<(const QgsRendererRange &other) const
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
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.
QString legendKeyForValue(double value) const
Returns the matching legend key for a value.
QgsRendererRangeLabelFormat mLabelFormat
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1430
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)
QgsGraduatedSymbolRenderer(const QString &attrName=QString(), const QgsRangeList &ranges=QgsRangeList())
bool rangesOverlap() const
Tests whether classes assigned to the renderer have ranges which overlap.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:92
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.
GraduatedMethod graduatedMethod() const
Returns the method used for graduation (either size or color)
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:48
QString dump() const override
Returns debug information about this renderer.
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
QStringList listForCboPrettyBreaks() const
Returns the list of breaks used in the prettybreaks mode.
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)
QgsFeatureRequest::OrderBy orderBy() const
Gets the order in which features shall be processed by this renderer.
bool valueGreaterThan(const QgsRendererRange &r1, const QgsRendererRange &r2)
QgsWkbTypes::GeometryType geometryType() const
Returns point, line or polygon.
bool updateRangeLabel(int rangeIndex, const QString &label)
bool legendSymbolItemChecked(const QString &key) override
items of symbology items in legend is checked
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
store renderer info to XML element
bool operator!=(const QgsRendererRangeLabelFormat &other) const
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...
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
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.
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Definition: qgssymbol.cpp:654
void setFromDomElement(QDomElement &element)
A vector of attributes.
Definition: qgsattributes.h:57
QColor color() const
Returns the symbol&#39;s color.
Definition: qgssymbol.cpp:459
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.
bool rangesHaveGaps() const
Tests whether classes assigned to the renderer have gaps between the ranges.
Object that keeps configuration of appearance of marker symbol&#39;s data-defined size in legend...
std::unique_ptr< QgsDataDefinedSizeLegend > mDataDefinedSizeLegend
void stopRender(QgsRenderContext &context)
Ends the rendering process.
Definition: qgssymbol.cpp:426
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
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