QGIS API Documentation  2.18.21-Las Palmas (9fba24a)
qgsgraduatedsymbolrendererv2.cpp
Go to the documentation of this file.
1 
2 /***************************************************************************
3  qgsgraduatedsymbolrendererv2.cpp
4  ---------------------
5  begin : November 2009
6  copyright : (C) 2009 by Martin Dobias
7  email : wonder dot sk at gmail dot com
8  ***************************************************************************
9  * *
10  * This program is free software; you can redistribute it and/or modify *
11  * it under the terms of the GNU General Public License as published by *
12  * the Free Software Foundation; either version 2 of the License, or *
13  * (at your option) any later version. *
14  * *
15  ***************************************************************************/
17 
18 #include "qgssymbolv2.h"
19 #include "qgssymbollayerv2utils.h"
20 #include "qgsvectorcolorrampv2.h"
23 #include "qgspainteffect.h"
24 #include "qgspainteffectregistry.h"
25 #include "qgsscaleexpression.h"
26 #include "qgsdatadefined.h"
27 
28 #include "qgsfeature.h"
29 #include "qgsvectorlayer.h"
30 #include "qgslogger.h"
31 #include "qgsvectordataprovider.h"
32 #include "qgsexpression.h"
33 #include <QDomDocument>
34 #include <QDomElement>
35 #include <QSettings> // for legend
36 #include <limits> // for jenks classification
37 #include <ctime>
38 
40  : mLowerValue( 0 )
41  , mUpperValue( 0 )
42  , mSymbol( nullptr )
43  , mLabel()
44  , mRender( true )
45 {
46 }
47 
49  : mLowerValue( lowerValue )
50  , mUpperValue( upperValue )
51  , mSymbol( symbol )
52  , mLabel( label )
53  , mRender( render )
54 {
55 }
56 
58  : mLowerValue( range.mLowerValue )
59  , mUpperValue( range.mUpperValue )
60  , mSymbol( range.mSymbol.data() ? range.mSymbol->clone() : nullptr )
61  , mLabel( range.mLabel )
62  , mRender( range.mRender )
63 {
64 }
65 
66 // cpy and swap idiom, note that the cpy is done with 'pass by value'
68 {
69  swap( range );
70  return *this;
71 }
72 
74 {
75  return
76  lowerValue() < other.lowerValue() ||
77  ( qgsDoubleNear( lowerValue(), other.lowerValue() ) && upperValue() < other.upperValue() );
78 }
79 
80 
82 {
83  qSwap( mLowerValue, other.mLowerValue );
84  qSwap( mUpperValue, other.mUpperValue );
85  qSwap( mSymbol, other.mSymbol );
86  std::swap( mLabel, other.mLabel );
87 }
88 
90 {
91  return mLowerValue;
92 }
93 
95 {
96  return mUpperValue;
97 }
98 
100 {
101  return mSymbol.data();
102 }
103 
105 {
106  return mLabel;
107 }
108 
110 {
111  if ( mSymbol.data() != s ) mSymbol.reset( s );
112 }
113 
115 {
116  mLabel = label;
117 }
118 
120 {
122 }
123 
125 {
127 }
128 
130 {
131  return mRender;
132 }
133 
135 {
136  mRender = render;
137 }
138 
140 {
141  return QString( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel, mSymbol.data() ? mSymbol->dump() : "(no symbol)" );
142 }
143 
144 void QgsRendererRangeV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props, bool firstRange ) const
145 {
146  if ( !mSymbol.data() || props.value( "attribute", "" ).isEmpty() )
147  return;
148 
149  QString attrName = props[ "attribute" ];
150 
151  QDomElement ruleElem = doc.createElement( "se:Rule" );
152  element.appendChild( ruleElem );
153 
154  QDomElement nameElem = doc.createElement( "se:Name" );
155  nameElem.appendChild( doc.createTextNode( mLabel ) );
156  ruleElem.appendChild( nameElem );
157 
158  QDomElement descrElem = doc.createElement( "se:Description" );
159  QDomElement titleElem = doc.createElement( "se:Title" );
160  QString descrStr = QString( "range: %1 - %2" ).arg( qgsDoubleToString( mLowerValue ), qgsDoubleToString( mUpperValue ) );
161  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
162  descrElem.appendChild( titleElem );
163  ruleElem.appendChild( descrElem );
164 
165  // create the ogc:Filter for the range
166  QString filterFunc = QString( "%1 %2 %3 AND %1 <= %4" )
167  .arg( attrName.replace( '\"', "\"\"" ),
168  firstRange ? ">=" : ">",
171  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, filterFunc );
172 
173  mSymbol->toSld( doc, ruleElem, props );
174 }
175 
177 
180 
182  : mFormat( " %1 - %2 " )
183  , mPrecision( 4 )
184  , mTrimTrailingZeroes( false )
185  , mNumberScale( 1.0 )
186  , mNumberSuffix( "" )
187  , mReTrailingZeroes( "[.,]?0*$" )
188  , mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
189 {
190 }
191 
193  : mReTrailingZeroes( "[.,]?0*$" )
194  , mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
195 {
196  setFormat( format );
197  setPrecision( precision );
198  setTrimTrailingZeroes( trimTrailingZeroes );
199 }
200 
201 
203 {
204  return
205  format() == other.format() &&
206  precision() == other.precision() &&
208 }
209 
211 {
212  return !( *this == other );
213 }
214 
216 {
217  // Limit the range of decimal places to a reasonable range
218  precision = qBound( MinPrecision, precision, MaxPrecision );
220  mNumberScale = 1.0;
221  mNumberSuffix = "";
222  while ( precision < 0 )
223  {
224  precision++;
225  mNumberScale /= 10.0;
226  mNumberSuffix.append( '0' );
227  }
228 }
229 
231 {
232  return labelForRange( range.lowerValue(), range.upperValue() );
233 }
234 
236 {
237  if ( mPrecision > 0 )
238  {
239  QString valueStr = QString::number( value, 'f', mPrecision );
240  if ( mTrimTrailingZeroes )
241  valueStr = valueStr.remove( mReTrailingZeroes );
242  if ( mReNegativeZero.exactMatch( valueStr ) )
243  valueStr = valueStr.mid( 1 );
244  return valueStr;
245  }
246  else
247  {
248  QString valueStr = QString::number( value * mNumberScale, 'f', 0 );
249  if ( valueStr == "-0" )
250  valueStr = '0';
251  if ( valueStr != "0" )
252  valueStr = valueStr + mNumberSuffix;
253  return valueStr;
254  }
255 }
256 
258 {
259  QString lowerStr = formatNumber( lower );
260  QString upperStr = formatNumber( upper );
261 
262  QString legend( mFormat );
263  return legend.replace( "%1", lowerStr ).replace( "%2", upperStr );
264 }
265 
267 {
268  mFormat = element.attribute( "format",
269  element.attribute( "prefix", " " ) + "%1" +
270  element.attribute( "separator", " - " ) + "%2" +
271  element.attribute( "suffix", " " )
272  );
273  setPrecision( element.attribute( "decimalplaces", "4" ).toInt() );
274  mTrimTrailingZeroes = element.attribute( "trimtrailingzeroes", "false" ) == "true";
275 }
276 
278 {
279  element.setAttribute( "format", mFormat );
280  element.setAttribute( "decimalplaces", mPrecision );
281  element.setAttribute( "trimtrailingzeroes", mTrimTrailingZeroes ? "true" : "false" );
282 }
283 
285 
287  : QgsFeatureRendererV2( "graduatedSymbol" )
288  , mAttrName( attrName )
289  , mMode( Custom )
290  , mInvertedColorRamp( false )
291  , mScaleMethod( DEFAULT_SCALE_METHOD )
292  , mGraduatedMethod( GraduatedColor )
293  , mAttrNum( -1 )
294  , mCounting( false )
295 
296 {
297  // TODO: check ranges for sanity (NULL symbols, invalid ranges)
298 
299  //important - we need a deep copy of the ranges list, not a shared copy. This is required because
300  //QgsRendererRangeV2::symbol() is marked const, and so retrieving the symbol via this method does not
301  //trigger a detachment and copy of mRanges BUT that same method CAN be used to modify a symbol in place
302  Q_FOREACH ( const QgsRendererRangeV2& range, ranges )
303  {
304  mRanges << range;
305  }
306 
307 }
308 
310 {
311  mRanges.clear(); // should delete all the symbols
312 }
313 
315 {
316  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
317  {
318  if ( range.lowerValue() <= value && range.upperValue() >= value )
319  {
320  if ( range.renderState() || mCounting )
321  return range.symbol();
322  else
323  return nullptr;
324  }
325  }
326  // the value is out of the range: return NULL instead of symbol
327  return nullptr;
328 }
329 
331 {
332  int i = 0;
333  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
334  {
335  if ( range.lowerValue() <= value && range.upperValue() >= value )
336  {
337  if ( range.renderState() || mCounting )
338  return QString::number( i );
339  else
340  return QString::null;
341  }
342  i++;
343  }
344  // the value is out of the range: return NULL
345  return QString::null;
346 }
347 
349 {
350  QgsSymbolV2* symbol = originalSymbolForFeature( feature, context );
351  if ( !symbol )
352  return nullptr;
353 
354  if ( !mRotation.data() && !mSizeScale.data() )
355  return symbol; // no data-defined rotation/scaling - just return the symbol
356 
357  // find out rotation, size scale
358  const double rotation = mRotation.data() ? mRotation->evaluate( &context.expressionContext() ).toDouble() : 0;
359  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( &context.expressionContext() ).toDouble() : 1.;
360 
361  // take a temporary symbol (or create it if doesn't exist)
362  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
363 
364  // modify the temporary symbol and return it
365  if ( tempSymbol->type() == QgsSymbolV2::Marker )
366  {
367  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
368  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
369  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
370  markerSymbol->setScaleMethod( mScaleMethod );
371  }
372  else if ( tempSymbol->type() == QgsSymbolV2::Line )
373  {
374  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
375  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
376  }
377  return tempSymbol;
378 }
379 
380 QVariant QgsGraduatedSymbolRendererV2::valueForFeature( QgsFeature& feature, QgsRenderContext &context ) const
381 {
382  QgsAttributes attrs = feature.attributes();
383  QVariant value;
384  if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
385  {
386  value = mExpression->evaluate( &context.expressionContext() );
387  }
388  else
389  {
390  value = attrs.at( mAttrNum );
391  }
392 
393  return value;
394 }
395 
397 {
398  QVariant value = valueForFeature( feature, context );
399 
400  // Null values should not be categorized
401  if ( value.isNull() )
402  return nullptr;
403 
404  // find the right category
405  return symbolForValue( value.toDouble() );
406 }
407 
409 {
410  mCounting = context.rendererScale() == 0.0;
411 
412  // find out classification attribute index from name
413  mAttrNum = fields.fieldNameIndex( mAttrName );
414 
415  if ( mAttrNum == -1 )
416  {
418  mExpression->prepare( &context.expressionContext() );
419  }
420 
421  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
422  {
423  if ( !range.symbol() )
424  continue;
425 
426  range.symbol()->startRender( context, &fields );
427 
428  if ( mRotation.data() || mSizeScale.data() )
429  {
430  QgsSymbolV2* tempSymbol = range.symbol()->clone();
433  tempSymbol->startRender( context, &fields );
434  mTempSymbols[ range.symbol()] = tempSymbol;
435  }
436  }
437  return;
438 }
439 
441 {
442  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
443  {
444  if ( !range.symbol() )
445  continue;
446 
447  range.symbol()->stopRender( context );
448  }
449 
450  // cleanup mTempSymbols
452  for ( ; it2 != mTempSymbols.constEnd(); ++it2 )
453  {
454  it2.value()->stopRender( context );
455  delete it2.value();
456  }
458 }
459 
461 {
462  QSet<QString> attributes;
463 
464  // mAttrName can contain either attribute name or an expression.
465  // Sometimes it is not possible to distinguish between those two,
466  // e.g. "a - b" can be both a valid attribute name or expression.
467  // Since we do not have access to fields here, try both options.
468  attributes << mAttrName;
469 
470  QgsExpression testExpr( mAttrName );
471  if ( !testExpr.hasParserError() )
472  attributes.unite( testExpr.referencedColumns().toSet() );
473 
474  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
475  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
476 
478  for ( ; range_it != mRanges.constEnd(); ++range_it )
479  {
480  QgsSymbolV2* symbol = range_it->symbol();
481  if ( symbol )
482  {
483  attributes.unite( symbol->usedAttributes() );
484  }
485  }
486  return attributes.toList();
487 }
488 
490 {
491  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
492  return false;
493  mRanges[rangeIndex].setSymbol( symbol );
494  return true;
495 }
496 
497 bool QgsGraduatedSymbolRendererV2::updateRangeLabel( int rangeIndex, const QString& label )
498 {
499  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
500  return false;
501  mRanges[rangeIndex].setLabel( label );
502  return true;
503 }
504 
505 bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double value )
506 {
507  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
508  return false;
509  QgsRendererRangeV2 &range = mRanges[rangeIndex];
510  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
511  range.setUpperValue( value );
512  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
513  return true;
514 }
515 
516 bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double value )
517 {
518  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
519  return false;
520  QgsRendererRangeV2 &range = mRanges[rangeIndex];
521  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
522  range.setLowerValue( value );
523  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
524  return true;
525 }
526 
528 {
529  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
530  return false;
531  mRanges[rangeIndex].setRenderState( value );
532  return true;
533 }
534 
536 {
537  QString s = QString( "GRADUATED: attr %1\n" ).arg( mAttrName );
538  for ( int i = 0; i < mRanges.count(); i++ )
539  s += mRanges[i].dump();
540  return s;
541 }
542 
544 {
546  r->setMode( mMode );
547  if ( mSourceSymbol.data() )
548  r->setSourceSymbol( mSourceSymbol->clone() );
549  if ( mSourceColorRamp.data() )
550  {
551  r->setSourceColorRamp( mSourceColorRamp->clone() );
553  }
556  r->setLabelFormat( labelFormat() );
558  copyRendererData( r );
559  return r;
560 }
561 
563 {
564  toSld( doc, element, QgsStringMap() );
565 }
566 
568 {
569  QgsStringMap locProps( props );
570  locProps[ "attribute" ] = mAttrName;
571  locProps[ "method" ] = graduatedMethodStr( mGraduatedMethod );
572  if ( mRotation.data() )
573  locProps[ "angle" ] = mRotation->expression();
574  if ( mSizeScale.data() )
575  locProps[ "scale" ] = mSizeScale->expression();
576 
577  // create a Rule for each range
578  bool first = true;
579  for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
580  {
581  QgsStringMap catProps( locProps );
582  it->toSld( doc, element, catProps, first );
583  first = false;
584  }
585 }
586 
588 {
589  Q_UNUSED( context );
590  QgsSymbolV2List lst;
591  lst.reserve( mRanges.count() );
592  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
593  {
594  lst.append( range.symbol() );
595  }
596  return lst;
597 }
598 
599 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
600 {
601 
602  // Equal interval algorithm
603  //
604  // Returns breaks based on dividing the range ('minimum' to 'maximum')
605  // into 'classes' parts.
606 
607  double step = ( maximum - minimum ) / classes;
608 
609  QList<double> breaks;
610  double value = minimum;
611  breaks.reserve( classes );
612  for ( int i = 0; i < classes; i++ )
613  {
614  value += step;
615  breaks.append( value );
616  }
617 
618  // floating point arithmetics is not precise:
619  // set the last break to be exactly maximum so we do not miss it
620  breaks[classes-1] = maximum;
621 
622  return breaks;
623 }
624 
625 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
626 {
627  // q-th quantile of a data set:
628  // value where q fraction of data is below and (1-q) fraction is above this value
629  // Xq = (1 - r) * X_NI1 + r * X_NI2
630  // NI1 = (int) (q * (n+1))
631  // NI2 = NI1 + 1
632  // r = q * (n+1) - (int) (q * (n+1))
633  // (indices of X: 1...n)
634 
635  // sort the values first
636  qSort( values );
637 
638  QList<double> breaks;
639 
640  // If there are no values to process: bail out
641  if ( values.isEmpty() )
642  return breaks;
643 
644  int n = values.count();
645  double Xq = n > 0 ? values[0] : 0.0;
646 
647  breaks.reserve( classes );
648  for ( int i = 1; i < classes; i++ )
649  {
650  if ( n > 1 )
651  {
652  double q = i / static_cast< double >( classes );
653  double a = q * ( n - 1 );
654  int aa = static_cast< int >( a );
655 
656  double r = a - aa;
657  Xq = ( 1 - r ) * values[aa] + r * values[aa+1];
658  }
659  breaks.append( Xq );
660  }
661 
662  breaks.append( values[ n-1 ] );
663 
664  return breaks;
665 }
666 
667 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<double> &labels )
668 {
669 
670  // C++ implementation of the standard deviation class interval algorithm
671  // as implemented in the 'classInt' package available for the R statistical
672  // prgramming language.
673 
674  // Returns breaks based on 'prettyBreaks' of the centred and scaled
675  // values of 'values', and may have a number of classes different from 'classes'.
676 
677  // If there are no values to process: bail out
678  if ( values.isEmpty() )
679  return QList<double>();
680 
681  double mean = 0.0;
682  double stdDev = 0.0;
683  int n = values.count();
684  double minimum = values[0];
685  double maximum = values[0];
686 
687  for ( int i = 0; i < n; i++ )
688  {
689  mean += values[i];
690  minimum = qMin( values[i], minimum ); // could use precomputed max and min
691  maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
692  }
693  mean = mean / static_cast< double >( n );
694 
695  double sd = 0.0;
696  for ( int i = 0; i < n; i++ )
697  {
698  sd = values[i] - mean;
699  stdDev += sd * sd;
700  }
701  stdDev = sqrt( stdDev / n );
702 
703  QList<double> breaks = QgsSymbolLayerV2Utils::prettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
704  for ( int i = 0; i < breaks.count(); i++ )
705  {
706  labels.append( breaks[i] );
707  breaks[i] = ( breaks[i] * stdDev ) + mean;
708  }
709 
710  return breaks;
711 } // _calcStdDevBreaks
712 
713 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
714  double minimum, double maximum,
715  int maximumSize = 3000 )
716 {
717  // Jenks Optimal (Natural Breaks) algorithm
718  // Based on the Jenks algorithm from the 'classInt' package available for
719  // the R statistical prgramming language, and from Python code from here:
720  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
721  // and is based on a JAVA and Fortran code available here:
722  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
723 
724  // Returns class breaks such that classes are internally homogeneous while
725  // assuring heterogeneity among classes.
726 
727  if ( values.isEmpty() )
728  return QList<double>();
729 
730  if ( classes <= 1 )
731  {
732  return QList<double>() << maximum;
733  }
734 
735  if ( classes >= values.size() )
736  {
737  return values;
738  }
739 
740  QVector<double> sample;
741 
742  // if we have lots of values, we need to take a random sample
743  if ( values.size() > maximumSize )
744  {
745  // for now, sample at least maximumSize values or a 10% sample, whichever
746  // is larger. This will produce a more representative sample for very large
747  // layers, but could end up being computationally intensive...
748 
749  sample.resize( qMax( maximumSize, values.size() / 10 ) );
750 
751  QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
752  QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
753 
754  sample[ 0 ] = minimum;
755  sample[ 1 ] = maximum;
756  for ( int i = 2; i < sample.size(); i++ )
757  {
758  // pick a random integer from 0 to n
759  double r = qrand();
760  int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
761  sample[ i ] = values[ j ];
762  }
763  }
764  else
765  {
766  sample = values.toVector();
767  }
768 
769  int n = sample.size();
770 
771  // sort the sample values
772  qSort( sample );
773 
774  QVector< QVector<int> > matrixOne( n + 1 );
775  QVector< QVector<double> > matrixTwo( n + 1 );
776 
777  for ( int i = 0; i <= n; i++ )
778  {
779  matrixOne[i].resize( classes + 1 );
780  matrixTwo[i].resize( classes + 1 );
781  }
782 
783  for ( int i = 1; i <= classes; i++ )
784  {
785  matrixOne[0][i] = 1;
786  matrixOne[1][i] = 1;
787  matrixTwo[0][i] = 0.0;
788  for ( int j = 2; j <= n; j++ )
789  {
790  matrixTwo[j][i] = std::numeric_limits<double>::max();
791  }
792  }
793 
794  for ( int l = 2; l <= n; l++ )
795  {
796  double s1 = 0.0;
797  double s2 = 0.0;
798  int w = 0;
799 
800  double v = 0.0;
801 
802  for ( int m = 1; m <= l; m++ )
803  {
804  int i3 = l - m + 1;
805 
806  double val = sample[ i3 - 1 ];
807 
808  s2 += val * val;
809  s1 += val;
810  w++;
811 
812  v = s2 - ( s1 * s1 ) / static_cast< double >( w );
813  int i4 = i3 - 1;
814  if ( i4 != 0 )
815  {
816  for ( int j = 2; j <= classes; j++ )
817  {
818  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
819  {
820  matrixOne[l][j] = i4;
821  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
822  }
823  }
824  }
825  }
826  matrixOne[l][1] = 1;
827  matrixTwo[l][1] = v;
828  }
829 
830  QVector<double> breaks( classes );
831  breaks[classes-1] = sample[n-1];
832 
833  for ( int j = classes, k = n; j >= 2; j-- )
834  {
835  int id = matrixOne[k][j] - 1;
836  breaks[j - 2] = sample[id];
837  k = matrixOne[k][j] - 1;
838  }
839 
840  return breaks.toList();
841 } //_calcJenksBreaks
842 
843 
845  QgsVectorLayer* vlayer,
846  const QString& attrName,
847  int classes,
848  Mode mode,
849  QgsSymbolV2* symbol,
850  QgsVectorColorRampV2* ramp,
851  bool inverted,
853 )
854 {
856  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
857  r->setSourceSymbol( symbol->clone() );
858  r->setSourceColorRamp( ramp->clone() );
859  r->setInvertedColorRamp( inverted );
860  r->setMode( mode );
861  r->setLabelFormat( labelFormat );
862  r->updateClasses( vlayer, mode, classes );
863  return r;
864 }
865 
867 {
868  bool ok;
869  return vlayer->getDoubleValues( mAttrName, ok );
870 }
871 
873 {
874  if ( mAttrName.isEmpty() )
875  return;
876 
877  setMode( mode );
878  // Custom classes are not recalculated
879  if ( mode == Custom )
880  return;
881 
882  if ( nclasses < 1 )
883  nclasses = 1;
884 
885  QList<double> values;
886  bool valuesLoaded = false;
887  double minimum;
888  double maximum;
889 
890  int attrNum = vlayer->fieldNameIndex( mAttrName );
891 
892  bool ok;
893  if ( attrNum == -1 )
894  {
895  values = vlayer->getDoubleValues( mAttrName, ok );
896  if ( !ok || values.isEmpty() )
897  return;
898 
899  qSort( values ); // vmora: is wondering if O( n log(n) ) is really necessary here, min and max are O( n )
900  minimum = values.first();
901  maximum = values.last();
902  valuesLoaded = true;
903  }
904  else
905  {
906  minimum = vlayer->minimumValue( attrNum ).toDouble();
907  maximum = vlayer->maximumValue( attrNum ).toDouble();
908  }
909 
910  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
911  QList<double> breaks;
912  QList<double> labels;
913  if ( mode == EqualInterval )
914  {
915  breaks = _calcEqualIntervalBreaks( minimum, maximum, nclasses );
916  }
917  else if ( mode == Pretty )
918  {
919  breaks = QgsSymbolLayerV2Utils::prettyBreaks( minimum, maximum, nclasses );
920  }
921  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
922  {
923  // get values from layer
924  if ( !valuesLoaded )
925  {
926  values = vlayer->getDoubleValues( mAttrName, ok );
927  }
928 
929  // calculate the breaks
930  if ( mode == Quantile )
931  {
932  breaks = _calcQuantileBreaks( values, nclasses );
933  }
934  else if ( mode == Jenks )
935  {
936  breaks = _calcJenksBreaks( values, nclasses, minimum, maximum );
937  }
938  else if ( mode == StdDev )
939  {
940  breaks = _calcStdDevBreaks( values, nclasses, labels );
941  }
942  }
943  else
944  {
945  Q_ASSERT( false );
946  }
947 
948  double lower, upper = minimum;
949  QString label;
951 
952  // "breaks" list contains all values at class breaks plus maximum as last break
953 
954  int i = 0;
955  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
956  {
957  lower = upper; // upper border from last interval
958  upper = *it;
959 
960  // Label - either StdDev label or default label for a range
961  if ( mode == StdDev )
962  {
963  if ( i == 0 )
964  {
965  label = "< " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
966  }
967  else if ( i == labels.count() - 1 )
968  {
969  label = ">= " + QString::number( labels[i-1], 'f', 2 ) + " Std Dev";
970  }
971  else
972  {
973  label = QString::number( labels[i-1], 'f', 2 ) + " Std Dev" + " - " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
974  }
975  }
976  else
977  {
978  label = mLabelFormat.labelForRange( lower, upper );
979  }
980  QgsSymbolV2* newSymbol = mSourceSymbol ? mSourceSymbol->clone() : QgsSymbolV2::defaultSymbol( vlayer->geometryType() );
981  addClass( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
982  }
984 }
985 
987 {
988  QDomElement symbolsElem = element.firstChildElement( "symbols" );
989  if ( symbolsElem.isNull() )
990  return nullptr;
991 
992  QDomElement rangesElem = element.firstChildElement( "ranges" );
993  if ( rangesElem.isNull() )
994  return nullptr;
995 
996  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
998 
999  QDomElement rangeElem = rangesElem.firstChildElement();
1000  while ( !rangeElem.isNull() )
1001  {
1002  if ( rangeElem.tagName() == "range" )
1003  {
1004  double lowerValue = rangeElem.attribute( "lower" ).toDouble();
1005  double upperValue = rangeElem.attribute( "upper" ).toDouble();
1006  QString symbolName = rangeElem.attribute( "symbol" );
1007  QString label = rangeElem.attribute( "label" );
1008  bool render = rangeElem.attribute( "render", "true" ) != "false";
1009  if ( symbolMap.contains( symbolName ) )
1010  {
1011  QgsSymbolV2* symbol = symbolMap.take( symbolName );
1012  ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label, render ) );
1013  }
1014  }
1015  rangeElem = rangeElem.nextSiblingElement();
1016  }
1017 
1018  QString attrName = element.attribute( "attr" );
1019 
1020  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
1021 
1022  QString attrMethod = element.attribute( "graduatedMethod" );
1023  if ( !attrMethod.isEmpty() )
1024  {
1025  if ( attrMethod == graduatedMethodStr( GraduatedColor ) )
1027  else if ( attrMethod == graduatedMethodStr( GraduatedSize ) )
1029  }
1030 
1031 
1032  // delete symbols if there are any more
1034 
1035  // try to load source symbol (optional)
1036  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
1037  if ( !sourceSymbolElem.isNull() )
1038  {
1039  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
1040  if ( sourceSymbolMap.contains( "0" ) )
1041  {
1042  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
1043  }
1044  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
1045  }
1046 
1047  // try to load color ramp (optional)
1048  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
1049  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
1050  {
1051  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
1052  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
1053  if ( !invertedColorRampElem.isNull() )
1054  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
1055  }
1056 
1057  // try to load mode
1058  QDomElement modeElem = element.firstChildElement( "mode" );
1059  if ( !modeElem.isNull() )
1060  {
1061  QString modeString = modeElem.attribute( "name" );
1062  if ( modeString == "equal" )
1063  r->setMode( EqualInterval );
1064  else if ( modeString == "quantile" )
1065  r->setMode( Quantile );
1066  else if ( modeString == "jenks" )
1067  r->setMode( Jenks );
1068  else if ( modeString == "stddev" )
1069  r->setMode( StdDev );
1070  else if ( modeString == "pretty" )
1071  r->setMode( Pretty );
1072  }
1073 
1074  QDomElement rotationElem = element.firstChildElement( "rotation" );
1075  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
1076  {
1077  Q_FOREACH ( const QgsRendererRangeV2& range, r->mRanges )
1078  {
1079  convertSymbolRotation( range.symbol(), rotationElem.attribute( "field" ) );
1080  }
1081  if ( r->mSourceSymbol.data() )
1082  {
1083  convertSymbolRotation( r->mSourceSymbol.data(), rotationElem.attribute( "field" ) );
1084  }
1085  }
1086 
1087  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
1088  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
1089  {
1090  Q_FOREACH ( const QgsRendererRangeV2& range, r->mRanges )
1091  {
1092  convertSymbolSizeScale( range.symbol(),
1093  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
1094  sizeScaleElem.attribute( "field" ) );
1095  }
1096  if ( r->mSourceSymbol.data() && r->mSourceSymbol->type() == QgsSymbolV2::Marker )
1097  {
1099  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
1100  sizeScaleElem.attribute( "field" ) );
1101  }
1102  }
1103 
1104  QDomElement labelFormatElem = element.firstChildElement( "labelformat" );
1105  if ( ! labelFormatElem.isNull() )
1106  {
1108  labelFormat.setFromDomElement( labelFormatElem );
1109  r->setLabelFormat( labelFormat );
1110  }
1111  // TODO: symbol levels
1112  return r;
1113 }
1114 
1116 {
1117  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1118  rendererElem.setAttribute( "type", "graduatedSymbol" );
1119  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
1120  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
1121  rendererElem.setAttribute( "attr", mAttrName );
1122  rendererElem.setAttribute( "graduatedMethod", graduatedMethodStr( mGraduatedMethod ) );
1123 
1124  // ranges
1125  int i = 0;
1127  QDomElement rangesElem = doc.createElement( "ranges" );
1129  for ( ; it != mRanges.constEnd(); ++it )
1130  {
1131  const QgsRendererRangeV2& range = *it;
1132  QString symbolName = QString::number( i );
1133  symbols.insert( symbolName, range.symbol() );
1134 
1135  QDomElement rangeElem = doc.createElement( "range" );
1136  rangeElem.setAttribute( "lower", QString::number( range.lowerValue(), 'f', 15 ) );
1137  rangeElem.setAttribute( "upper", QString::number( range.upperValue(), 'f', 15 ) );
1138  rangeElem.setAttribute( "symbol", symbolName );
1139  rangeElem.setAttribute( "label", range.label() );
1140  rangeElem.setAttribute( "render", range.renderState() ? "true" : "false" );
1141  rangesElem.appendChild( rangeElem );
1142  i++;
1143  }
1144 
1145  rendererElem.appendChild( rangesElem );
1146 
1147  // save symbols
1148  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
1149  rendererElem.appendChild( symbolsElem );
1150 
1151  // save source symbol
1152  if ( mSourceSymbol.data() )
1153  {
1154  QgsSymbolV2Map sourceSymbols;
1155  sourceSymbols.insert( "0", mSourceSymbol.data() );
1156  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
1157  rendererElem.appendChild( sourceSymbolElem );
1158  }
1159 
1160  // save source color ramp
1161  if ( mSourceColorRamp.data() )
1162  {
1163  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
1164  rendererElem.appendChild( colorRampElem );
1165  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
1166  invertedElem.setAttribute( "value", mInvertedColorRamp );
1167  rendererElem.appendChild( invertedElem );
1168  }
1169 
1170  // save mode
1171  QString modeString;
1172  if ( mMode == EqualInterval )
1173  modeString = "equal";
1174  else if ( mMode == Quantile )
1175  modeString = "quantile";
1176  else if ( mMode == Jenks )
1177  modeString = "jenks";
1178  else if ( mMode == StdDev )
1179  modeString = "stddev";
1180  else if ( mMode == Pretty )
1181  modeString = "pretty";
1182  if ( !modeString.isEmpty() )
1183  {
1184  QDomElement modeElem = doc.createElement( "mode" );
1185  modeElem.setAttribute( "name", modeString );
1186  rendererElem.appendChild( modeElem );
1187  }
1188 
1189  QDomElement rotationElem = doc.createElement( "rotation" );
1190  if ( mRotation.data() )
1192  rendererElem.appendChild( rotationElem );
1193 
1194  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
1195  if ( mSizeScale.data() )
1197  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
1198  rendererElem.appendChild( sizeScaleElem );
1199 
1200  QDomElement labelFormatElem = doc.createElement( "labelformat" );
1201  mLabelFormat.saveToDomElement( labelFormatElem );
1202  rendererElem.appendChild( labelFormatElem );
1203 
1205  mPaintEffect->saveProperties( doc, rendererElem );
1206 
1207  if ( !mOrderBy.isEmpty() )
1208  {
1209  QDomElement orderBy = doc.createElement( "orderby" );
1210  mOrderBy.save( orderBy );
1211  rendererElem.appendChild( orderBy );
1212  }
1213  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
1214 
1215  return rendererElem;
1216 }
1217 
1219 {
1221  int count = ranges().count();
1222  lst.reserve( count );
1223  for ( int i = 0; i < count; i++ )
1224  {
1225  const QgsRendererRangeV2& range = ranges()[i];
1226  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
1227  lst << qMakePair( range.label(), pix );
1228  }
1229  return lst;
1230 }
1231 
1233 {
1234  QgsLegendSymbolListV2 list;
1235  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
1236  {
1237  // check that all symbols that have the same size expression
1238  QgsDataDefined ddSize;
1239  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1240  {
1241  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( range.symbol() );
1242  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
1243  {
1244  // no common size expression
1246  }
1247  else
1248  {
1249  ddSize = symbol->dataDefinedSize();
1250  }
1251  }
1252 
1253  if ( !ddSize.isActive() || !ddSize.useExpression() )
1254  {
1256  }
1257 
1258  QgsScaleExpression exp( ddSize.expressionString() );
1259  if ( exp.type() != QgsScaleExpression::Unknown )
1260  {
1261  QgsLegendSymbolItemV2 title( nullptr, exp.baseExpression(), "" );
1262  list << title;
1263  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
1264  {
1266  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
1268  s->setSize( exp.size( v ) );
1269  list << si;
1270  }
1271  // now list the graduated symbols
1273  Q_FOREACH ( const QgsLegendSymbolItemV2& item, list2 )
1274  list << item;
1275  return list;
1276  }
1277  }
1278 
1280 }
1281 
1283 {
1284  QVariant value = valueForFeature( feature, context );
1285 
1286  // Null values should not be categorized
1287  if ( value.isNull() )
1288  return QSet< QString >();
1289 
1290  // find the right category
1291  QString key = legendKeyForValue( value.toDouble() );
1292  if ( !key.isNull() )
1293  return QSet< QString >() << key;
1294  else
1295  return QSet< QString >();
1296 }
1297 
1299 {
1300  Q_UNUSED( scaleDenominator );
1301  QgsLegendSymbolList lst;
1302 
1303  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1304  {
1305  if ( rule.isEmpty() || range.label() == rule )
1306  {
1307  lst << qMakePair( range.label(), range.symbol() );
1308  }
1309  }
1310  return lst;
1311 }
1312 
1314 {
1315  return mSourceSymbol.data();
1316 }
1318 {
1319  mSourceSymbol.reset( sym );
1320 }
1321 
1323 {
1324  return mSourceColorRamp.data();
1325 }
1326 
1328 {
1329  mSourceColorRamp.reset( ramp );
1330 }
1331 
1333 {
1334  double min = DBL_MAX;
1335  for ( int i = 0; i < mRanges.count(); i++ )
1336  {
1337  double sz = 0;
1338  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1339  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1340  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1341  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1342  min = qMin( sz, min );
1343  }
1344  return min;
1345 }
1346 
1348 {
1349  double max = DBL_MIN;
1350  for ( int i = 0; i < mRanges.count(); i++ )
1351  {
1352  double sz = 0;
1353  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1354  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1355  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1356  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1357  max = qMax( sz, max );
1358  }
1359  return max;
1360 }
1361 
1362 void QgsGraduatedSymbolRendererV2::setSymbolSizes( double minSize, double maxSize )
1363 {
1364  for ( int i = 0; i < mRanges.count(); i++ )
1365  {
1366  QScopedPointer<QgsSymbolV2> symbol( mRanges.at( i ).symbol() ? mRanges.at( i ).symbol()->clone() : nullptr );
1367  const double size = mRanges.count() > 1
1368  ? minSize + i * ( maxSize - minSize ) / ( mRanges.count() - 1 )
1369  : .5 * ( maxSize + minSize );
1370  if ( symbol->type() == QgsSymbolV2::Marker )
1371  static_cast< QgsMarkerSymbolV2 * >( symbol.data() )->setSize( size );
1372  if ( symbol->type() == QgsSymbolV2::Line )
1373  static_cast< QgsLineSymbolV2 * >( symbol.data() )->setWidth( size );
1374  updateRangeSymbol( i, symbol.take() );
1375  }
1376 }
1377 
1379 {
1380  int i = 0;
1381  if ( ramp )
1382  {
1383  setSourceColorRamp( ramp );
1384  setInvertedColorRamp( inverted );
1385  }
1386 
1387  if ( mSourceColorRamp )
1388  {
1389  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1390  {
1391  QgsSymbolV2 *symbol = range.symbol() ? range.symbol()->clone() : nullptr;
1392  if ( symbol )
1393  {
1394  double colorValue;
1395  if ( inverted )
1396  colorValue = ( mRanges.count() > 1 ? static_cast< double >( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
1397  else
1398  colorValue = ( mRanges.count() > 1 ? static_cast< double >( i ) / ( mRanges.count() - 1 ) : 0 );
1399  symbol->setColor( mSourceColorRamp->color( colorValue ) );
1400  }
1401  updateRangeSymbol( i, symbol );
1402  ++i;
1403  }
1404  }
1405 
1406 }
1407 
1409 {
1410  if ( !sym )
1411  return;
1412 
1413  int i = 0;
1414  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1415  {
1416  QScopedPointer<QgsSymbolV2> symbol( sym->clone() );
1418  {
1419  symbol->setColor( range.symbol()->color() );
1420  }
1421  else if ( mGraduatedMethod == GraduatedSize )
1422  {
1423  if ( symbol->type() == QgsSymbolV2::Marker )
1424  static_cast<QgsMarkerSymbolV2 *>( symbol.data() )->setSize(
1425  static_cast<QgsMarkerSymbolV2 *>( range.symbol() )->size() );
1426  else if ( symbol->type() == QgsSymbolV2::Line )
1427  static_cast<QgsLineSymbolV2 *>( symbol.data() )->setWidth(
1428  static_cast<QgsLineSymbolV2 *>( range.symbol() )->width() );
1429  }
1430  updateRangeSymbol( i, symbol.take() );
1431  ++i;
1432  }
1433  setSourceSymbol( sym->clone() );
1434 }
1435 
1437 {
1438  if ( mSourceSymbol->type() == QgsSymbolV2::Marker )
1439  {
1440  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
1441  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
1442  }
1443 
1444 }
1445 
1447 {
1448  if ( mSourceSymbol->type() == QgsSymbolV2::Marker )
1449  {
1450  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
1451  QgsDataDefined ddAngle = s->dataDefinedAngle();
1452  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
1453  }
1454 
1455  return QString();
1456 }
1457 
1459 {
1461 }
1462 
1464 {
1466 }
1467 
1469 {
1471  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1472  {
1473  if ( range.symbol() )
1475  }
1476 }
1477 
1479 {
1480  return true;
1481 }
1482 
1484 {
1485  bool ok;
1486  int index = key.toInt( &ok );
1487  if ( ok && index >= 0 && index < mRanges.size() )
1488  return mRanges.at( index ).renderState();
1489  else
1490  return true;
1491 }
1492 
1494 {
1495  bool ok;
1496  int index = key.toInt( &ok );
1497  if ( ok )
1498  updateRangeRenderState( index, state );
1499 }
1500 
1502 {
1503  bool ok;
1504  int index = key.toInt( &ok );
1505  if ( ok )
1506  updateRangeSymbol( index, symbol );
1507  else
1508  delete symbol;
1509 }
1510 
1512 {
1513  QgsSymbolV2* newSymbol = symbol->clone();
1514  QString label = "0.0 - 0.0";
1515  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1516 }
1517 
1518 void QgsGraduatedSymbolRendererV2::addClass( double lower, double upper )
1519 {
1520  QgsSymbolV2* newSymbol = mSourceSymbol->clone();
1521  QString label = mLabelFormat.labelForRange( lower, upper );
1522  mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
1523 }
1524 
1526 {
1528  while ( it.hasNext() )
1529  {
1530  QgsRendererRangeV2 range = it.next();
1531  if ( range.lowerValue() < breakValue && range.upperValue() > breakValue )
1532  {
1534  newRange.setLowerValue( breakValue );
1535  newRange.setUpperValue( range.upperValue() );
1536  newRange.setLabel( mLabelFormat.labelForRange( newRange ) );
1537  newRange.setSymbol( mSourceSymbol->clone() );
1538 
1539  //update old range
1540  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
1541  range.setUpperValue( breakValue );
1542  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range.lowerValue(), breakValue ) );
1543  it.setValue( range );
1544 
1545  it.insert( newRange );
1546  break;
1547  }
1548  }
1549 
1550  if ( updateSymbols )
1551  {
1552  switch ( mGraduatedMethod )
1553  {
1554  case GraduatedColor:
1556  break;
1557  case GraduatedSize:
1559  break;
1560  }
1561  }
1562 }
1563 
1565 {
1566  mRanges.append( range );
1567 }
1568 
1570 {
1571  mRanges.removeAt( idx );
1572 }
1573 
1575 {
1576  mRanges.clear();
1577 }
1578 
1580 {
1581  if ( updateRanges && labelFormat != mLabelFormat )
1582  {
1583  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1584  {
1585  it->setLabel( labelFormat.labelForRange( *it ) );
1586  }
1587  }
1589 }
1590 
1591 
1593 {
1594  // Find the minimum size of a class
1595  double minClassRange = 0.0;
1596  Q_FOREACH ( const QgsRendererRangeV2& rendererRange, mRanges )
1597  {
1598  double range = rendererRange.upperValue() - rendererRange.lowerValue();
1599  if ( range <= 0.0 )
1600  continue;
1601  if ( minClassRange == 0.0 || range < minClassRange )
1602  minClassRange = range;
1603  }
1604  if ( minClassRange <= 0.0 )
1605  return;
1606 
1607  // Now set the number of decimal places to ensure no more than 20% error in
1608  // representing this range (up to 10% at upper and lower end)
1609 
1610  int ndp = 10;
1611  double nextDpMinRange = 0.0000000099;
1612  while ( ndp > 0 && nextDpMinRange < minClassRange )
1613  {
1614  ndp--;
1615  nextDpMinRange *= 10.0;
1616  }
1617  mLabelFormat.setPrecision( ndp );
1618  if ( updateRanges ) setLabelFormat( mLabelFormat, true );
1619 }
1620 
1622 {
1623  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1624  return;
1625  mRanges.move( from, to );
1626 }
1627 
1629 {
1630  return r1 < r2;
1631 }
1632 
1634 {
1635  return !valueLessThan( r1, r2 );
1636 }
1637 
1639 {
1640  if ( order == Qt::AscendingOrder )
1641  {
1642  qSort( mRanges.begin(), mRanges.end(), valueLessThan );
1643  }
1644  else
1645  {
1646  qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1647  }
1648 }
1649 
1651 {
1652  QgsRangeList sortedRanges = mRanges;
1653  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1654 
1655  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1656  if ( it == sortedRanges.constEnd() )
1657  return false;
1658 
1659  if (( *it ).upperValue() < ( *it ).lowerValue() )
1660  return true;
1661 
1662  double prevMax = ( *it ).upperValue();
1663  ++it;
1664 
1665  for ( ; it != sortedRanges.constEnd(); ++it )
1666  {
1667  if (( *it ).upperValue() < ( *it ).lowerValue() )
1668  return true;
1669 
1670  if (( *it ).lowerValue() < prevMax )
1671  return true;
1672 
1673  prevMax = ( *it ).upperValue();
1674  }
1675  return false;
1676 }
1677 
1679 {
1680  QgsRangeList sortedRanges = mRanges;
1681  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1682 
1683  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1684  if ( it == sortedRanges.constEnd() )
1685  return false;
1686 
1687  double prevMax = ( *it ).upperValue();
1688  ++it;
1689 
1690  for ( ; it != sortedRanges.constEnd(); ++it )
1691  {
1692  if ( !qgsDoubleNear(( *it ).lowerValue(), prevMax ) )
1693  return true;
1694 
1695  prevMax = ( *it ).upperValue();
1696  }
1697  return false;
1698 }
1699 
1701 {
1702  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1703 }
1704 
1706 {
1707  return !labelLessThan( r1, r2 );
1708 }
1709 
1711 {
1712  if ( order == Qt::AscendingOrder )
1713  {
1714  qSort( mRanges.begin(), mRanges.end(), labelLessThan );
1715  }
1716  else
1717  {
1718  qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1719  }
1720 }
1721 
1723 {
1724  QgsGraduatedSymbolRendererV2* r = nullptr;
1725  if ( renderer->type() == "graduatedSymbol" )
1726  {
1727  r = dynamic_cast<QgsGraduatedSymbolRendererV2*>( renderer->clone() );
1728  }
1729  else if ( renderer->type() == "pointDisplacement" )
1730  {
1731  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1732  if ( pointDisplacementRenderer )
1733  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1734  }
1735  else if ( renderer->type() == "invertedPolygonRenderer" )
1736  {
1737  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1738  if ( invertedPolygonRenderer )
1739  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1740  }
1741 
1742  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1743  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1744 
1745  if ( !r )
1746  {
1747  r = new QgsGraduatedSymbolRendererV2( "", QgsRangeList() );
1748  QgsRenderContext context;
1749  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
1750  if ( !symbols.isEmpty() )
1751  {
1752  r->setSourceSymbol( symbols.at( 0 )->clone() );
1753  }
1754  }
1755 
1756  r->setOrderBy( renderer->orderBy() );
1757  r->setOrderByEnabled( renderer->orderByEnabled() );
1758 
1759  return r;
1760 }
1761 
1763 {
1764  switch ( method )
1765  {
1766  case GraduatedColor:
1767  return "GraduatedColor";
1768  case GraduatedSize:
1769  return "GraduatedSize";
1770  }
1771  return "";
1772 }
1773 
1774 
QgsDataDefined dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
QgsSymbolV2::ScaleMethod scaleMethod() const
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
Class for parsing and evaluation of expressions (formerly called "search strings").
QList< QgsRendererRangeV2 > QgsRangeList
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
void clear()
static QgsSymbolV2Map loadSymbols(QDomElement &element)
#define RENDERER_TAG_NAME
Definition: qgsrendererv2.h:49
static unsigned index
double rendererScale() const
virtual void toSld(QDomDocument &doc, QDomElement &element) const override
Writes the SLD element following the SLD v1.1 specs.
void setFormat(const QString &format)
QString & append(QChar ch)
void setLowerValue(double lowerValue)
QgsGraduatedSymbolRendererV2(const QString &attrName=QString(), const QgsRangeList &ranges=QgsRangeList())
void setSymbolSizes(double minSize, double maxSize)
set varying symbol size for classes
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
void addBreak(double breakValue, bool updateSymbols=true)
Add a breakpoint by splitting existing classes so that the specified value becomes a break between tw...
void setDataDefinedAngle(const QgsDataDefined &dd)
Set data defined angle for whole symbol (including all symbol layers).
A container class for data source field mapping or expression.
void setLabelFormat(const QgsRendererRangeV2LabelFormat &labelFormat, bool updateRanges=false)
Set the label format used to generate default classification labels.
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...
bool labelLessThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
bool contains(const Key &key) const
static QgsGraduatedSymbolRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsGraduatedSymbolRendererV2 from an existing renderer.
static QgsVectorColorRampV2 * loadColorRamp(QDomElement &element)
int localeAwareCompare(const QString &other) const
void updateClasses(QgsVectorLayer *vlayer, Mode mode, int nclasses)
Recalculate classes for a layer.
static QList< double > _calcStdDevBreaks(QList< double > values, int classes, QList< double > &labels)
virtual QString dump() const override
for debugging
QDomNode appendChild(const QDomNode &newChild)
QScopedPointer< QgsVectorColorRampV2 > mSourceColorRamp
QVariant maximumValue(int index)
Returns the maximum value for an attribute column or an invalid variant in case of error...
QString attribute(const QString &name, const QString &defValue) const
void updateSymbols(QgsSymbolV2 *sym)
Update all the symbols but leave breaks and colors.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setDataDefinedSize(const QgsDataDefined &dd)
Set data defined size for whole symbol (including all symbol layers).
void reserve(int alloc)
static QDomElement saveColorRamp(const QString &name, QgsVectorColorRampV2 *ramp, QDomDocument &doc)
QStringList referencedColumns() const
Get list of columns referenced by the expression.
GraduatedMethod graduatedMethod() const
return the method used for graduation (either size or color)
virtual QgsSymbolV2 * clone() const =0
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
bool operator==(const QgsRendererRangeV2LabelFormat &other) const
static QgsSymbolV2::ScaleMethod decodeScaleMethod(const QString &str)
virtual bool legendSymbolItemChecked(const QString &key) override
item in symbology was checked
bool updateRangeRenderState(int rangeIndex, bool render)
virtual QgsSymbolV2 * symbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
const T & at(int i) const
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
void removeAt(int i)
void setSizeScaleField(const QString &fieldOrExpression)
QDomElement nextSiblingElement(const QString &tagName) const
Container of fields for a vector layer.
Definition: qgsfield.h:252
SymbolType type() const
Definition: qgssymbolv2.h:107
void updateColorRamp(QgsVectorColorRampV2 *ramp=nullptr, bool inverted=false)
Update the color ramp used.
Line symbol.
Definition: qgssymbolv2.h:82
void move(int from, int to)
QVector< T > toVector() const
QHash< QgsSymbolV2 *, QgsSymbolV2 * > mTempSymbols
temporary symbols, used for data-defined rotation and scaling
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const
Return a list of symbology items for the legend.
QSet< T > toSet() const
void calculateLabelPrecision(bool updateRanges=true)
Reset the label decimal places to a numberbased on the minimum class interval.
QList< double > getDoubleValues(const QString &fieldOrExpression, bool &ok, bool selectedOnly=false, int *nullCount=nullptr)
Fetches all double values from a specified field name or expression.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
QString & remove(int position, int n)
QString labelForRange(double lower, double upper) const
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, const QString &rule=QString()) override
return a list of item text / symbol
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
double toDouble(bool *ok) const
QVariant minimumValue(int index)
Returns the minimum value for an attribute column or an invalid variant in case of error...
QMap< QString, QString > QgsStringMap
Definition: qgis.h:492
void setValue(const T &value) const
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Compare two doubles (but allow some difference)
Definition: qgis.h:353
static const char * graduatedMethodStr(GraduatedMethod method)
QgsVectorColorRampV2 * sourceColorRamp()
Returns the source color ramp, from which each classes&#39; color is derived.
QgsPaintEffect * mPaintEffect
void setWidth(double width)
Marker symbol.
Definition: qgssymbolv2.h:81
int size() const
bool isNull() const
void reset(T *other)
bool useExpression() const
Returns if the field or the expression part is active.
Q_DECL_DEPRECATED QList< double > getDataValues(QgsVectorLayer *vlayer)
Evaluates the data expression and returns the list of values from the layer.
QScopedPointer< QgsSymbolV2 > mSymbol
double ANALYSIS_EXPORT max(double x, double y)
Returns the maximum of two doubles or the first argument if both are equal.
virtual void stopRender(QgsRenderContext &context) override
Needs to be called when a render cycle has finished to clean up.
QScopedPointer< QgsExpression > mExpression
void setColor(const QColor &color)
virtual QgsFeatureRendererV2 * clone() const =0
QString number(int n, int base)
int count(const T &value) const
static QgsGraduatedSymbolRendererV2 * createRenderer(QgsVectorLayer *vlayer, const QString &attrName, int classes, Mode mode, QgsSymbolV2 *symbol, QgsVectorColorRampV2 *ramp, bool inverted=false, const QgsRendererRangeV2LabelFormat &legendFormat=QgsRendererRangeV2LabelFormat())
void setGraduatedMethod(GraduatedMethod method)
set the method used for graduation (either size or color)
void append(const T &value)
const QgsFeatureRendererV2 * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
void resize(int size)
const_iterator constEnd() const
void startRender(QgsRenderContext &context, const QgsFields *fields=nullptr)
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
bool isNull() const
static QList< double > _calcQuantileBreaks(QList< double > values, int classes)
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
const QgsRangeList & ranges() const
void setAttribute(const QString &name, const QString &value)
#define DEFAULT_SCALE_METHOD
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer...
virtual QgsVectorColorRampV2 * clone() const =0
Creates a clone of the color ramp.
void swap(QgsRendererRangeV2 &other)
QString expressionString() const
Returns the expression string of this QgsDataDefined.
int toInt(bool *ok, int base) const
QString qgsDoubleToString(double a, int precision=17)
Returns a string representation of a double.
Definition: qgis.h:341
bool isEmpty() const
const T & value() const
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props, bool firstRange=false) const
Creates a DOM element representing the range in SLD format.
QString type() const
Definition: qgsrendererv2.h:92
bool isEmpty() const
bool operator<(const QgsRendererRangeV2 &other) const
bool operator!=(const QgsRendererRangeV2LabelFormat &other) const
QgsSymbolV2 * sourceSymbol()
Returns the renderer&#39;s source symbol, which is the base symbol used for the each classes&#39; symbol befo...
Q_DECL_DEPRECATED QString rotationField() const override
return rotation field name (or empty string if not set or not supported by renderer) ...
void setAngle(double angle)
Sets the angle for the whole symbol.
static QList< double > _calcJenksBreaks(QList< double > values, int classes, double minimum, double maximum, int maximumSize=3000)
bool rangesHaveGaps() const
Tests whether classes assigned to the renderer have gaps between the ranges.
void setSourceSymbol(QgsSymbolV2 *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each classes&#39; symbol b...
bool updateRangeLowerValue(int rangeIndex, double value)
void setSize(double size)
Sets the size for the whole symbol.
QgsRendererRangeV2LabelFormat mLabelFormat
QGis::GeometryType geometryType() const
Returns point, line or polygon.
T & first()
bool rangesOverlap() const
Tests whether classes assigned to the renderer have ranges which overlap.
bool usingSymbolLevels() const
virtual Q_DECL_DEPRECATED QgsSymbolV2List symbols()
For symbol levels.
void setUpperValue(double upperValue)
virtual QDomElement save(QDomDocument &doc) override
store renderer info to XML element
void setFromDomElement(QDomElement &element)
QgsSymbolV2 * symbolForValue(double value)
static void convertSymbolSizeScale(QgsSymbolV2 *symbol, QgsSymbolV2::ScaleMethod method, const QString &field)
bool labelGreaterThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
QDomText createTextNode(const QString &value)
T * data() const
void clear()
bool valueLessThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
iterator end()
int fieldNameIndex(const QString &fieldName) const
Look up field&#39;s index from name also looks up case-insensitive if there is no match otherwise...
Definition: qgsfield.cpp:571
virtual QgsGraduatedSymbolRendererV2 * clone() const override
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
bool hasNext() const
QgsExpressionContext & expressionContext()
Gets the expression context.
QString field() const
Get the field which this QgsDataDefined represents.
A renderer that automatically displaces points with the same position.
virtual bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
bool isNull() const
void setUsingSymbolLevels(bool usingSymbolLevels)
QString & replace(int position, int n, QChar after)
static QList< double > _calcEqualIntervalBreaks(double minimum, double maximum, int classes)
const T & at(int i) const
QgsFeatureRequest::OrderBy orderBy() const
Get the order in which features shall be processed by this renderer.
const_iterator constBegin() const
int ANALYSIS_EXPORT lower(int n, int i)
Lower function.
void moveClass(int from, int to)
Moves the category at index position from to index position to.
void copyRendererData(QgsFeatureRendererV2 *destRenderer) const
Clones generic renderer data to another renderer.
Contains information about the context of a rendering operation.
void setOrderBy(const QgsFeatureRequest::OrderBy &orderBy)
Define the order in which features shall be processed by this renderer.
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
int mAttrNum
attribute index (derived from attribute name in startRender)
QList< T > toList() const
void stopRender(QgsRenderContext &context)
QString mid(int position, int n) const
const QgsRendererRangeV2LabelFormat & labelFormat() const
Return the label format used to generate default classification labels.
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
QSet< T > & unite(const QSet< T > &other)
static QgsSymbolV2 * defaultSymbol(QGis::GeometryType geomType)
return new default symbol for specified geometry type
static QgsExpression * fieldOrExpressionToExpression(const QString &fieldOrExpression)
Return a new valid expression instance for given field or expression string.
ScaleMethod
Scale method.
Definition: qgssymbolv2.h:90
Q_DECL_DEPRECATED void setRotationField(const QString &fieldOrExpression) override
sets rotation field of renderer (if supported by the renderer)
QgsDataDefined dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
void insert(int i, const T &value)
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=nullptr)
QgsFeatureRequest::OrderBy mOrderBy
QString legendKeyForValue(double value) const
Returns the matching legend key for a value.
static QString encodeScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
QColor color() const
double minSymbolSize() const
return the min symbol size when graduated by size
QDomElement firstChildElement(const QString &tagName) const
static QString fieldOrExpressionFromExpression(QgsExpression *expression)
Return a field name if the whole expression is just a name of the field .
T & last()
void setScaleMethodToSymbol(QgsSymbolV2 *symbol, int scaleMethod)
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
virtual void setLegendSymbolItem(const QString &key, QgsSymbolV2 *symbol) override
Sets the symbol to be used for a legend symbol item.
int count(const T &value) const
bool updateRangeSymbol(int rangeIndex, QgsSymbolV2 *symbol)
virtual void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
double maxSymbolSize() const
return the max symbol size when graduated by size
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
Sets the source color ramp.
QScopedPointer< QgsSymbolV2 > mSourceSymbol
QList< T > toList() const
void setRenderHints(int hints)
Definition: qgssymbolv2.h:208
static void clearSymbolMap(QgsSymbolV2Map &symbols)
void setTrimTrailingZeroes(bool trimTrailingZeroes)
bool updateRangeUpperValue(int rangeIndex, double value)
QScopedPointer< QgsExpression > mRotation
virtual QSet< QString > legendKeysForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return legend keys matching a specified feature.
double toDouble(bool *ok) const
virtual QgsSymbolV2 * originalSymbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
iterator insert(const Key &key, const T &value)
static QgsFeatureRendererV2 * create(QDomElement &element)
create renderer from XML element
virtual QgsLegendSymbologyList legendSymbologyItems(QSize iconSize) override
return a list of symbology items for the legend
QString tagName() const
void setLabel(const QString &label)
virtual QList< QString > usedAttributes() override
Returns a set of attributes required for this renderer.
double ANALYSIS_EXPORT min(double x, double y)
Returns the minimum of two doubles or the first argument if both are equal.
const_iterator constEnd() const
QScopedPointer< QgsExpression > mSizeScale
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
QSet< QString > usedAttributes() const
Return a list of attributes required to render this feature.
QgsRendererRangeV2 & operator=(QgsRendererRangeV2 range)
bool hasDefaultValues() const
Returns whether the data defined container is set to all the default values, ie, disabled, with empty expression and no assigned field.
A vector of attributes.
Definition: qgsfeature.h:115
void insert(const T &value)
int size() const
Represents a vector layer which manages a vector based data sets.
Abstract base class for color ramps.
bool exactMatch(const QString &str) const
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
bool isActive() const
iterator begin()
T take(const Key &key)
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
const QgsFeatureRendererV2 * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
QgsLegendSymbolListV2 legendSymbolItemsV2() const override
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
bool valueGreaterThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
const T value(const Key &key) const
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
Needs to be called when a new render cycle is started.
static void convertSymbolRotation(QgsSymbolV2 *symbol, const QString &field)
bool updateRangeLabel(int rangeIndex, const QString &label)
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.