QGIS API Documentation  2.17.0-Master (3a3b9ab7)
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  QgsStringMap props;
565  props[ "attribute" ] = mAttrName;
566  props[ "method" ] = graduatedMethodStr( mGraduatedMethod );
567  if ( mRotation.data() )
568  props[ "angle" ] = mRotation->expression();
569  if ( mSizeScale.data() )
570  props[ "scale" ] = mSizeScale->expression();
571 
572  // create a Rule for each range
573  bool first = true;
574  for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
575  {
576  QgsStringMap catProps( props );
577  it->toSld( doc, element, catProps, first );
578  first = false;
579  }
580 }
581 
583 {
584  Q_UNUSED( context );
585  QgsSymbolV2List lst;
586  lst.reserve( mRanges.count() );
587  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
588  {
589  lst.append( range.symbol() );
590  }
591  return lst;
592 }
593 
594 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
595 {
596 
597  // Equal interval algorithm
598  //
599  // Returns breaks based on dividing the range ('minimum' to 'maximum')
600  // into 'classes' parts.
601 
602  double step = ( maximum - minimum ) / classes;
603 
604  QList<double> breaks;
605  double value = minimum;
606  breaks.reserve( classes );
607  for ( int i = 0; i < classes; i++ )
608  {
609  value += step;
610  breaks.append( value );
611  }
612 
613  // floating point arithmetics is not precise:
614  // set the last break to be exactly maximum so we do not miss it
615  breaks[classes-1] = maximum;
616 
617  return breaks;
618 }
619 
620 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
621 {
622  // q-th quantile of a data set:
623  // value where q fraction of data is below and (1-q) fraction is above this value
624  // Xq = (1 - r) * X_NI1 + r * X_NI2
625  // NI1 = (int) (q * (n+1))
626  // NI2 = NI1 + 1
627  // r = q * (n+1) - (int) (q * (n+1))
628  // (indices of X: 1...n)
629 
630  // sort the values first
631  qSort( values );
632 
633  QList<double> breaks;
634 
635  // If there are no values to process: bail out
636  if ( values.isEmpty() )
637  return breaks;
638 
639  int n = values.count();
640  double Xq = n > 0 ? values[0] : 0.0;
641 
642  breaks.reserve( classes );
643  for ( int i = 1; i < classes; i++ )
644  {
645  if ( n > 1 )
646  {
647  double q = i / static_cast< double >( classes );
648  double a = q * ( n - 1 );
649  int aa = static_cast< int >( a );
650 
651  double r = a - aa;
652  Xq = ( 1 - r ) * values[aa] + r * values[aa+1];
653  }
654  breaks.append( Xq );
655  }
656 
657  breaks.append( values[ n-1 ] );
658 
659  return breaks;
660 }
661 
662 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<double> &labels )
663 {
664 
665  // C++ implementation of the standard deviation class interval algorithm
666  // as implemented in the 'classInt' package available for the R statistical
667  // prgramming language.
668 
669  // Returns breaks based on 'prettyBreaks' of the centred and scaled
670  // values of 'values', and may have a number of classes different from 'classes'.
671 
672  // If there are no values to process: bail out
673  if ( values.isEmpty() )
674  return QList<double>();
675 
676  double mean = 0.0;
677  double stdDev = 0.0;
678  int n = values.count();
679  double minimum = values[0];
680  double maximum = values[0];
681 
682  for ( int i = 0; i < n; i++ )
683  {
684  mean += values[i];
685  minimum = qMin( values[i], minimum ); // could use precomputed max and min
686  maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
687  }
688  mean = mean / static_cast< double >( n );
689 
690  double sd = 0.0;
691  for ( int i = 0; i < n; i++ )
692  {
693  sd = values[i] - mean;
694  stdDev += sd * sd;
695  }
696  stdDev = sqrt( stdDev / n );
697 
698  QList<double> breaks = QgsSymbolLayerV2Utils::prettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
699  for ( int i = 0; i < breaks.count(); i++ )
700  {
701  labels.append( breaks[i] );
702  breaks[i] = ( breaks[i] * stdDev ) + mean;
703  }
704 
705  return breaks;
706 } // _calcStdDevBreaks
707 
708 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
709  double minimum, double maximum,
710  int maximumSize = 3000 )
711 {
712  // Jenks Optimal (Natural Breaks) algorithm
713  // Based on the Jenks algorithm from the 'classInt' package available for
714  // the R statistical prgramming language, and from Python code from here:
715  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
716  // and is based on a JAVA and Fortran code available here:
717  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
718 
719  // Returns class breaks such that classes are internally homogeneous while
720  // assuring heterogeneity among classes.
721 
722  if ( values.isEmpty() )
723  return QList<double>();
724 
725  if ( classes <= 1 )
726  {
727  return QList<double>() << maximum;
728  }
729 
730  if ( classes >= values.size() )
731  {
732  return values;
733  }
734 
735  QVector<double> sample;
736 
737  // if we have lots of values, we need to take a random sample
738  if ( values.size() > maximumSize )
739  {
740  // for now, sample at least maximumSize values or a 10% sample, whichever
741  // is larger. This will produce a more representative sample for very large
742  // layers, but could end up being computationally intensive...
743 
744  sample.resize( qMax( maximumSize, values.size() / 10 ) );
745 
746  QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
747  QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
748 
749  sample[ 0 ] = minimum;
750  sample[ 1 ] = maximum;
751  for ( int i = 2; i < sample.size(); i++ )
752  {
753  // pick a random integer from 0 to n
754  double r = qrand();
755  int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
756  sample[ i ] = values[ j ];
757  }
758  }
759  else
760  {
761  sample = values.toVector();
762  }
763 
764  int n = sample.size();
765 
766  // sort the sample values
767  qSort( sample );
768 
769  QVector< QVector<int> > matrixOne( n + 1 );
770  QVector< QVector<double> > matrixTwo( n + 1 );
771 
772  for ( int i = 0; i <= n; i++ )
773  {
774  matrixOne[i].resize( classes + 1 );
775  matrixTwo[i].resize( classes + 1 );
776  }
777 
778  for ( int i = 1; i <= classes; i++ )
779  {
780  matrixOne[0][i] = 1;
781  matrixOne[1][i] = 1;
782  matrixTwo[0][i] = 0.0;
783  for ( int j = 2; j <= n; j++ )
784  {
785  matrixTwo[j][i] = std::numeric_limits<double>::max();
786  }
787  }
788 
789  for ( int l = 2; l <= n; l++ )
790  {
791  double s1 = 0.0;
792  double s2 = 0.0;
793  int w = 0;
794 
795  double v = 0.0;
796 
797  for ( int m = 1; m <= l; m++ )
798  {
799  int i3 = l - m + 1;
800 
801  double val = sample[ i3 - 1 ];
802 
803  s2 += val * val;
804  s1 += val;
805  w++;
806 
807  v = s2 - ( s1 * s1 ) / static_cast< double >( w );
808  int i4 = i3 - 1;
809  if ( i4 != 0 )
810  {
811  for ( int j = 2; j <= classes; j++ )
812  {
813  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
814  {
815  matrixOne[l][j] = i4;
816  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
817  }
818  }
819  }
820  }
821  matrixOne[l][1] = 1;
822  matrixTwo[l][1] = v;
823  }
824 
825  QVector<double> breaks( classes );
826  breaks[classes-1] = sample[n-1];
827 
828  for ( int j = classes, k = n; j >= 2; j-- )
829  {
830  int id = matrixOne[k][j] - 1;
831  breaks[j - 2] = sample[id];
832  k = matrixOne[k][j] - 1;
833  }
834 
835  return breaks.toList();
836 } //_calcJenksBreaks
837 
838 
840  QgsVectorLayer* vlayer,
841  const QString& attrName,
842  int classes,
843  Mode mode,
844  QgsSymbolV2* symbol,
845  QgsVectorColorRampV2* ramp,
846  bool inverted,
848 )
849 {
851  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
852  r->setSourceSymbol( symbol->clone() );
853  r->setSourceColorRamp( ramp->clone() );
854  r->setInvertedColorRamp( inverted );
855  r->setMode( mode );
856  r->setLabelFormat( labelFormat );
857  r->updateClasses( vlayer, mode, classes );
858  return r;
859 }
860 
862 {
863  bool ok;
864  return vlayer->getDoubleValues( mAttrName, ok );
865 }
866 
868 {
869  if ( mAttrName.isEmpty() )
870  return;
871 
872  setMode( mode );
873  // Custom classes are not recalculated
874  if ( mode == Custom )
875  return;
876 
877  if ( nclasses < 1 )
878  nclasses = 1;
879 
880  QList<double> values;
881  bool valuesLoaded = false;
882  double minimum;
883  double maximum;
884 
885  int attrNum = vlayer->fieldNameIndex( mAttrName );
886 
887  bool ok;
888  if ( attrNum == -1 )
889  {
890  values = vlayer->getDoubleValues( mAttrName, ok );
891  if ( !ok || values.isEmpty() )
892  return;
893 
894  qSort( values ); // vmora: is wondering if O( n log(n) ) is really necessary here, min and max are O( n )
895  minimum = values.first();
896  maximum = values.last();
897  valuesLoaded = true;
898  }
899  else
900  {
901  minimum = vlayer->minimumValue( attrNum ).toDouble();
902  maximum = vlayer->maximumValue( attrNum ).toDouble();
903  }
904 
905  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
906  QList<double> breaks;
907  QList<double> labels;
908  if ( mode == EqualInterval )
909  {
910  breaks = _calcEqualIntervalBreaks( minimum, maximum, nclasses );
911  }
912  else if ( mode == Pretty )
913  {
914  breaks = QgsSymbolLayerV2Utils::prettyBreaks( minimum, maximum, nclasses );
915  }
916  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
917  {
918  // get values from layer
919  if ( !valuesLoaded )
920  {
921  values = vlayer->getDoubleValues( mAttrName, ok );
922  }
923 
924  // calculate the breaks
925  if ( mode == Quantile )
926  {
927  breaks = _calcQuantileBreaks( values, nclasses );
928  }
929  else if ( mode == Jenks )
930  {
931  breaks = _calcJenksBreaks( values, nclasses, minimum, maximum );
932  }
933  else if ( mode == StdDev )
934  {
935  breaks = _calcStdDevBreaks( values, nclasses, labels );
936  }
937  }
938  else
939  {
940  Q_ASSERT( false );
941  }
942 
943  double lower, upper = minimum;
944  QString label;
946 
947  // "breaks" list contains all values at class breaks plus maximum as last break
948 
949  int i = 0;
950  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
951  {
952  lower = upper; // upper border from last interval
953  upper = *it;
954 
955  // Label - either StdDev label or default label for a range
956  if ( mode == StdDev )
957  {
958  if ( i == 0 )
959  {
960  label = "< " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
961  }
962  else if ( i == labels.count() - 1 )
963  {
964  label = ">= " + QString::number( labels[i-1], 'f', 2 ) + " Std Dev";
965  }
966  else
967  {
968  label = QString::number( labels[i-1], 'f', 2 ) + " Std Dev" + " - " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
969  }
970  }
971  else
972  {
973  label = mLabelFormat.labelForRange( lower, upper );
974  }
975  QgsSymbolV2* newSymbol = mSourceSymbol ? mSourceSymbol->clone() : QgsSymbolV2::defaultSymbol( vlayer->geometryType() );
976  addClass( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
977  }
979 }
980 
982 {
983  QDomElement symbolsElem = element.firstChildElement( "symbols" );
984  if ( symbolsElem.isNull() )
985  return nullptr;
986 
987  QDomElement rangesElem = element.firstChildElement( "ranges" );
988  if ( rangesElem.isNull() )
989  return nullptr;
990 
991  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
993 
994  QDomElement rangeElem = rangesElem.firstChildElement();
995  while ( !rangeElem.isNull() )
996  {
997  if ( rangeElem.tagName() == "range" )
998  {
999  double lowerValue = rangeElem.attribute( "lower" ).toDouble();
1000  double upperValue = rangeElem.attribute( "upper" ).toDouble();
1001  QString symbolName = rangeElem.attribute( "symbol" );
1002  QString label = rangeElem.attribute( "label" );
1003  bool render = rangeElem.attribute( "render", "true" ) != "false";
1004  if ( symbolMap.contains( symbolName ) )
1005  {
1006  QgsSymbolV2* symbol = symbolMap.take( symbolName );
1007  ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label, render ) );
1008  }
1009  }
1010  rangeElem = rangeElem.nextSiblingElement();
1011  }
1012 
1013  QString attrName = element.attribute( "attr" );
1014 
1015  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
1016 
1017  QString attrMethod = element.attribute( "graduatedMethod" );
1018  if ( !attrMethod.isEmpty() )
1019  {
1020  if ( attrMethod == graduatedMethodStr( GraduatedColor ) )
1022  else if ( attrMethod == graduatedMethodStr( GraduatedSize ) )
1024  }
1025 
1026 
1027  // delete symbols if there are any more
1029 
1030  // try to load source symbol (optional)
1031  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
1032  if ( !sourceSymbolElem.isNull() )
1033  {
1034  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
1035  if ( sourceSymbolMap.contains( "0" ) )
1036  {
1037  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
1038  }
1039  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
1040  }
1041 
1042  // try to load color ramp (optional)
1043  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
1044  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
1045  {
1046  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
1047  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
1048  if ( !invertedColorRampElem.isNull() )
1049  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
1050  }
1051 
1052  // try to load mode
1053  QDomElement modeElem = element.firstChildElement( "mode" );
1054  if ( !modeElem.isNull() )
1055  {
1056  QString modeString = modeElem.attribute( "name" );
1057  if ( modeString == "equal" )
1058  r->setMode( EqualInterval );
1059  else if ( modeString == "quantile" )
1060  r->setMode( Quantile );
1061  else if ( modeString == "jenks" )
1062  r->setMode( Jenks );
1063  else if ( modeString == "stddev" )
1064  r->setMode( StdDev );
1065  else if ( modeString == "pretty" )
1066  r->setMode( Pretty );
1067  }
1068 
1069  QDomElement rotationElem = element.firstChildElement( "rotation" );
1070  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
1071  {
1072  Q_FOREACH ( const QgsRendererRangeV2& range, r->mRanges )
1073  {
1074  convertSymbolRotation( range.symbol(), rotationElem.attribute( "field" ) );
1075  }
1076  if ( r->mSourceSymbol.data() )
1077  {
1078  convertSymbolRotation( r->mSourceSymbol.data(), rotationElem.attribute( "field" ) );
1079  }
1080  }
1081 
1082  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
1083  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
1084  {
1085  Q_FOREACH ( const QgsRendererRangeV2& range, r->mRanges )
1086  {
1087  convertSymbolSizeScale( range.symbol(),
1088  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
1089  sizeScaleElem.attribute( "field" ) );
1090  }
1091  if ( r->mSourceSymbol.data() && r->mSourceSymbol->type() == QgsSymbolV2::Marker )
1092  {
1094  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
1095  sizeScaleElem.attribute( "field" ) );
1096  }
1097  }
1098 
1099  QDomElement labelFormatElem = element.firstChildElement( "labelformat" );
1100  if ( ! labelFormatElem.isNull() )
1101  {
1103  labelFormat.setFromDomElement( labelFormatElem );
1104  r->setLabelFormat( labelFormat );
1105  }
1106  // TODO: symbol levels
1107  return r;
1108 }
1109 
1111 {
1112  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1113  rendererElem.setAttribute( "type", "graduatedSymbol" );
1114  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
1115  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
1116  rendererElem.setAttribute( "attr", mAttrName );
1117  rendererElem.setAttribute( "graduatedMethod", graduatedMethodStr( mGraduatedMethod ) );
1118 
1119  // ranges
1120  int i = 0;
1122  QDomElement rangesElem = doc.createElement( "ranges" );
1124  for ( ; it != mRanges.constEnd(); ++it )
1125  {
1126  const QgsRendererRangeV2& range = *it;
1127  QString symbolName = QString::number( i );
1128  symbols.insert( symbolName, range.symbol() );
1129 
1130  QDomElement rangeElem = doc.createElement( "range" );
1131  rangeElem.setAttribute( "lower", QString::number( range.lowerValue(), 'f', 15 ) );
1132  rangeElem.setAttribute( "upper", QString::number( range.upperValue(), 'f', 15 ) );
1133  rangeElem.setAttribute( "symbol", symbolName );
1134  rangeElem.setAttribute( "label", range.label() );
1135  rangeElem.setAttribute( "render", range.renderState() ? "true" : "false" );
1136  rangesElem.appendChild( rangeElem );
1137  i++;
1138  }
1139 
1140  rendererElem.appendChild( rangesElem );
1141 
1142  // save symbols
1143  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
1144  rendererElem.appendChild( symbolsElem );
1145 
1146  // save source symbol
1147  if ( mSourceSymbol.data() )
1148  {
1149  QgsSymbolV2Map sourceSymbols;
1150  sourceSymbols.insert( "0", mSourceSymbol.data() );
1151  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
1152  rendererElem.appendChild( sourceSymbolElem );
1153  }
1154 
1155  // save source color ramp
1156  if ( mSourceColorRamp.data() )
1157  {
1158  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
1159  rendererElem.appendChild( colorRampElem );
1160  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
1161  invertedElem.setAttribute( "value", mInvertedColorRamp );
1162  rendererElem.appendChild( invertedElem );
1163  }
1164 
1165  // save mode
1166  QString modeString;
1167  if ( mMode == EqualInterval )
1168  modeString = "equal";
1169  else if ( mMode == Quantile )
1170  modeString = "quantile";
1171  else if ( mMode == Jenks )
1172  modeString = "jenks";
1173  else if ( mMode == StdDev )
1174  modeString = "stddev";
1175  else if ( mMode == Pretty )
1176  modeString = "pretty";
1177  if ( !modeString.isEmpty() )
1178  {
1179  QDomElement modeElem = doc.createElement( "mode" );
1180  modeElem.setAttribute( "name", modeString );
1181  rendererElem.appendChild( modeElem );
1182  }
1183 
1184  QDomElement rotationElem = doc.createElement( "rotation" );
1185  if ( mRotation.data() )
1187  rendererElem.appendChild( rotationElem );
1188 
1189  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
1190  if ( mSizeScale.data() )
1192  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
1193  rendererElem.appendChild( sizeScaleElem );
1194 
1195  QDomElement labelFormatElem = doc.createElement( "labelformat" );
1196  mLabelFormat.saveToDomElement( labelFormatElem );
1197  rendererElem.appendChild( labelFormatElem );
1198 
1200  mPaintEffect->saveProperties( doc, rendererElem );
1201 
1202  if ( !mOrderBy.isEmpty() )
1203  {
1204  QDomElement orderBy = doc.createElement( "orderby" );
1205  mOrderBy.save( orderBy );
1206  rendererElem.appendChild( orderBy );
1207  }
1208  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
1209 
1210  return rendererElem;
1211 }
1212 
1214 {
1216  int count = ranges().count();
1217  lst.reserve( count );
1218  for ( int i = 0; i < count; i++ )
1219  {
1220  const QgsRendererRangeV2& range = ranges()[i];
1221  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
1222  lst << qMakePair( range.label(), pix );
1223  }
1224  return lst;
1225 }
1226 
1228 {
1229  QgsLegendSymbolListV2 list;
1230  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
1231  {
1232  // check that all symbols that have the same size expression
1233  QgsDataDefined ddSize;
1234  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1235  {
1236  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( range.symbol() );
1237  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
1238  {
1239  // no common size expression
1241  }
1242  else
1243  {
1244  ddSize = symbol->dataDefinedSize();
1245  }
1246  }
1247 
1248  if ( !ddSize.isActive() || !ddSize.useExpression() )
1249  {
1251  }
1252 
1253  QgsScaleExpression exp( ddSize.expressionString() );
1254  if ( exp.type() != QgsScaleExpression::Unknown )
1255  {
1256  QgsLegendSymbolItemV2 title( nullptr, exp.baseExpression(), "" );
1257  list << title;
1258  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
1259  {
1261  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
1263  s->setSize( exp.size( v ) );
1264  list << si;
1265  }
1266  // now list the graduated symbols
1268  Q_FOREACH ( const QgsLegendSymbolItemV2& item, list2 )
1269  list << item;
1270  return list;
1271  }
1272  }
1273 
1275 }
1276 
1278 {
1279  QVariant value = valueForFeature( feature, context );
1280 
1281  // Null values should not be categorized
1282  if ( value.isNull() )
1283  return QSet< QString >();
1284 
1285  // find the right category
1286  QString key = legendKeyForValue( value.toDouble() );
1287  if ( !key.isNull() )
1288  return QSet< QString >() << key;
1289  else
1290  return QSet< QString >();
1291 }
1292 
1294 {
1295  Q_UNUSED( scaleDenominator );
1296  QgsLegendSymbolList lst;
1297 
1298  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1299  {
1300  if ( rule.isEmpty() || range.label() == rule )
1301  {
1302  lst << qMakePair( range.label(), range.symbol() );
1303  }
1304  }
1305  return lst;
1306 }
1307 
1309 {
1310  return mSourceSymbol.data();
1311 }
1313 {
1314  mSourceSymbol.reset( sym );
1315 }
1316 
1318 {
1319  return mSourceColorRamp.data();
1320 }
1321 
1323 {
1324  mSourceColorRamp.reset( ramp );
1325 }
1326 
1328 {
1329  double min = DBL_MAX;
1330  for ( int i = 0; i < mRanges.count(); i++ )
1331  {
1332  double sz = 0;
1333  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1334  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1335  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1336  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1337  min = qMin( sz, min );
1338  }
1339  return min;
1340 }
1341 
1343 {
1344  double max = DBL_MIN;
1345  for ( int i = 0; i < mRanges.count(); i++ )
1346  {
1347  double sz = 0;
1348  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1349  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1350  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1351  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1352  max = qMax( sz, max );
1353  }
1354  return max;
1355 }
1356 
1357 void QgsGraduatedSymbolRendererV2::setSymbolSizes( double minSize, double maxSize )
1358 {
1359  for ( int i = 0; i < mRanges.count(); i++ )
1360  {
1361  QScopedPointer<QgsSymbolV2> symbol( mRanges.at( i ).symbol() ? mRanges.at( i ).symbol()->clone() : nullptr );
1362  const double size = mRanges.count() > 1
1363  ? minSize + i * ( maxSize - minSize ) / ( mRanges.count() - 1 )
1364  : .5 * ( maxSize + minSize );
1365  if ( symbol->type() == QgsSymbolV2::Marker )
1366  static_cast< QgsMarkerSymbolV2 * >( symbol.data() )->setSize( size );
1367  if ( symbol->type() == QgsSymbolV2::Line )
1368  static_cast< QgsLineSymbolV2 * >( symbol.data() )->setWidth( size );
1369  updateRangeSymbol( i, symbol.take() );
1370  }
1371 }
1372 
1374 {
1375  int i = 0;
1376  if ( ramp )
1377  {
1378  setSourceColorRamp( ramp );
1379  setInvertedColorRamp( inverted );
1380  }
1381 
1382  if ( mSourceColorRamp )
1383  {
1384  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1385  {
1386  QgsSymbolV2 *symbol = range.symbol() ? range.symbol()->clone() : nullptr;
1387  if ( symbol )
1388  {
1389  double colorValue;
1390  if ( inverted )
1391  colorValue = ( mRanges.count() > 1 ? static_cast< double >( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
1392  else
1393  colorValue = ( mRanges.count() > 1 ? static_cast< double >( i ) / ( mRanges.count() - 1 ) : 0 );
1394  symbol->setColor( mSourceColorRamp->color( colorValue ) );
1395  }
1396  updateRangeSymbol( i, symbol );
1397  ++i;
1398  }
1399  }
1400 
1401 }
1402 
1404 {
1405  if ( !sym )
1406  return;
1407 
1408  int i = 0;
1409  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1410  {
1411  QScopedPointer<QgsSymbolV2> symbol( sym->clone() );
1413  {
1414  symbol->setColor( range.symbol()->color() );
1415  }
1416  else if ( mGraduatedMethod == GraduatedSize )
1417  {
1418  if ( symbol->type() == QgsSymbolV2::Marker )
1419  static_cast<QgsMarkerSymbolV2 *>( symbol.data() )->setSize(
1420  static_cast<QgsMarkerSymbolV2 *>( range.symbol() )->size() );
1421  else if ( symbol->type() == QgsSymbolV2::Line )
1422  static_cast<QgsLineSymbolV2 *>( symbol.data() )->setWidth(
1423  static_cast<QgsLineSymbolV2 *>( range.symbol() )->width() );
1424  }
1425  updateRangeSymbol( i, symbol.take() );
1426  ++i;
1427  }
1428  setSourceSymbol( sym->clone() );
1429 }
1430 
1432 {
1433  if ( mSourceSymbol->type() == QgsSymbolV2::Marker )
1434  {
1435  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
1436  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
1437  }
1438 
1439 }
1440 
1442 {
1443  if ( mSourceSymbol->type() == QgsSymbolV2::Marker )
1444  {
1445  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
1446  QgsDataDefined ddAngle = s->dataDefinedAngle();
1447  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
1448  }
1449 
1450  return QString();
1451 }
1452 
1454 {
1456 }
1457 
1459 {
1461 }
1462 
1464 {
1466  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1467  {
1468  if ( range.symbol() )
1470  }
1471 }
1472 
1474 {
1475  return true;
1476 }
1477 
1479 {
1480  bool ok;
1481  int index = key.toInt( &ok );
1482  if ( ok && index >= 0 && index < mRanges.size() )
1483  return mRanges.at( index ).renderState();
1484  else
1485  return true;
1486 }
1487 
1489 {
1490  bool ok;
1491  int index = key.toInt( &ok );
1492  if ( ok )
1493  updateRangeRenderState( index, state );
1494 }
1495 
1497 {
1498  bool ok;
1499  int index = key.toInt( &ok );
1500  if ( ok )
1501  updateRangeSymbol( index, symbol );
1502  else
1503  delete symbol;
1504 }
1505 
1507 {
1508  QgsSymbolV2* newSymbol = symbol->clone();
1509  QString label = "0.0 - 0.0";
1510  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1511 }
1512 
1513 void QgsGraduatedSymbolRendererV2::addClass( double lower, double upper )
1514 {
1515  QgsSymbolV2* newSymbol = mSourceSymbol->clone();
1516  QString label = mLabelFormat.labelForRange( lower, upper );
1517  mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
1518 }
1519 
1521 {
1523  while ( it.hasNext() )
1524  {
1525  QgsRendererRangeV2 range = it.next();
1526  if ( range.lowerValue() < breakValue && range.upperValue() > breakValue )
1527  {
1529  newRange.setLowerValue( breakValue );
1530  newRange.setUpperValue( range.upperValue() );
1531  newRange.setLabel( mLabelFormat.labelForRange( newRange ) );
1532  newRange.setSymbol( mSourceSymbol->clone() );
1533 
1534  //update old range
1535  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
1536  range.setUpperValue( breakValue );
1537  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range.lowerValue(), breakValue ) );
1538  it.setValue( range );
1539 
1540  it.insert( newRange );
1541  break;
1542  }
1543  }
1544 
1545  if ( updateSymbols )
1546  {
1547  switch ( mGraduatedMethod )
1548  {
1549  case GraduatedColor:
1551  break;
1552  case GraduatedSize:
1554  break;
1555  }
1556  }
1557 }
1558 
1560 {
1561  mRanges.append( range );
1562 }
1563 
1565 {
1566  mRanges.removeAt( idx );
1567 }
1568 
1570 {
1571  mRanges.clear();
1572 }
1573 
1575 {
1576  if ( updateRanges && labelFormat != mLabelFormat )
1577  {
1578  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1579  {
1580  it->setLabel( labelFormat.labelForRange( *it ) );
1581  }
1582  }
1584 }
1585 
1586 
1588 {
1589  // Find the minimum size of a class
1590  double minClassRange = 0.0;
1591  Q_FOREACH ( const QgsRendererRangeV2& rendererRange, mRanges )
1592  {
1593  double range = rendererRange.upperValue() - rendererRange.lowerValue();
1594  if ( range <= 0.0 )
1595  continue;
1596  if ( minClassRange == 0.0 || range < minClassRange )
1597  minClassRange = range;
1598  }
1599  if ( minClassRange <= 0.0 )
1600  return;
1601 
1602  // Now set the number of decimal places to ensure no more than 20% error in
1603  // representing this range (up to 10% at upper and lower end)
1604 
1605  int ndp = 10;
1606  double nextDpMinRange = 0.0000000099;
1607  while ( ndp > 0 && nextDpMinRange < minClassRange )
1608  {
1609  ndp--;
1610  nextDpMinRange *= 10.0;
1611  }
1612  mLabelFormat.setPrecision( ndp );
1613  if ( updateRanges ) setLabelFormat( mLabelFormat, true );
1614 }
1615 
1617 {
1618  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1619  return;
1620  mRanges.move( from, to );
1621 }
1622 
1624 {
1625  return r1 < r2;
1626 }
1627 
1629 {
1630  return !valueLessThan( r1, r2 );
1631 }
1632 
1634 {
1635  if ( order == Qt::AscendingOrder )
1636  {
1637  qSort( mRanges.begin(), mRanges.end(), valueLessThan );
1638  }
1639  else
1640  {
1641  qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1642  }
1643 }
1644 
1646 {
1647  QgsRangeList sortedRanges = mRanges;
1648  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1649 
1650  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1651  if ( it == sortedRanges.constEnd() )
1652  return false;
1653 
1654  if (( *it ).upperValue() < ( *it ).lowerValue() )
1655  return true;
1656 
1657  double prevMax = ( *it ).upperValue();
1658  ++it;
1659 
1660  for ( ; it != sortedRanges.constEnd(); ++it )
1661  {
1662  if (( *it ).upperValue() < ( *it ).lowerValue() )
1663  return true;
1664 
1665  if (( *it ).lowerValue() < prevMax )
1666  return true;
1667 
1668  prevMax = ( *it ).upperValue();
1669  }
1670  return false;
1671 }
1672 
1674 {
1675  QgsRangeList sortedRanges = mRanges;
1676  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1677 
1678  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1679  if ( it == sortedRanges.constEnd() )
1680  return false;
1681 
1682  double prevMax = ( *it ).upperValue();
1683  ++it;
1684 
1685  for ( ; it != sortedRanges.constEnd(); ++it )
1686  {
1687  if ( !qgsDoubleNear(( *it ).lowerValue(), prevMax ) )
1688  return true;
1689 
1690  prevMax = ( *it ).upperValue();
1691  }
1692  return false;
1693 }
1694 
1696 {
1697  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1698 }
1699 
1701 {
1702  return !labelLessThan( r1, r2 );
1703 }
1704 
1706 {
1707  if ( order == Qt::AscendingOrder )
1708  {
1709  qSort( mRanges.begin(), mRanges.end(), labelLessThan );
1710  }
1711  else
1712  {
1713  qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1714  }
1715 }
1716 
1718 {
1719  QgsGraduatedSymbolRendererV2* r = nullptr;
1720  if ( renderer->type() == "graduatedSymbol" )
1721  {
1722  r = dynamic_cast<QgsGraduatedSymbolRendererV2*>( renderer->clone() );
1723  }
1724  else if ( renderer->type() == "pointDisplacement" )
1725  {
1726  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1727  if ( pointDisplacementRenderer )
1728  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1729  }
1730  else if ( renderer->type() == "invertedPolygonRenderer" )
1731  {
1732  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1733  if ( invertedPolygonRenderer )
1734  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1735  }
1736 
1737  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1738  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1739 
1740  if ( !r )
1741  {
1742  r = new QgsGraduatedSymbolRendererV2( "", QgsRangeList() );
1743  QgsRenderContext context;
1744  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
1745  if ( !symbols.isEmpty() )
1746  {
1747  r->setSourceSymbol( symbols.at( 0 )->clone() );
1748  }
1749  }
1750 
1751  r->setOrderBy( renderer->orderBy() );
1752  r->setOrderByEnabled( renderer->orderByEnabled() );
1753 
1754  return r;
1755 }
1756 
1758 {
1759  switch ( method )
1760  {
1761  case GraduatedColor:
1762  return "GraduatedColor";
1763  case GraduatedSize:
1764  return "GraduatedSize";
1765  }
1766  return "";
1767 }
1768 
1769 
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
Class for parsing and evaluation of expressions (formerly called "search strings").
QList< QgsRendererRangeV2 > QgsRangeList
void clear()
static QgsSymbolV2Map loadSymbols(QDomElement &element)
#define RENDERER_TAG_NAME
Definition: qgsrendererv2.h:49
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
static unsigned index
virtual void toSld(QDomDocument &doc, QDomElement &element) const override
used from subclasses to create SLD Rule elements following 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
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QStringList referencedColumns() const
Get list of columns referenced by the expression.
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.
QgsFeatureRequest::OrderBy orderBy() const
Get the order in which features shall be processed by this renderer.
static QList< double > _calcStdDevBreaks(QList< double > values, int classes, QList< double > &labels)
virtual QString dump() const override
for debugging
QDomNode appendChild(const QDomNode &newChild)
SymbolType type() const
Definition: qgssymbolv2.h:107
QScopedPointer< QgsVectorColorRampV2 > mSourceColorRamp
QVariant maximumValue(int index)
Returns the maximum value for an attribute column or an invalid variant in case of error...
QSet< QString > usedAttributes() const
Return a list of attributes required to render this feature.
QString field() const
Get the field which this QgsDataDefined represents.
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).
QString formatNumber(double value) const
void reserve(int alloc)
static QDomElement saveColorRamp(const QString &name, QgsVectorColorRampV2 *ramp, QDomDocument &doc)
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:539
double rendererScale() const
virtual QgsSymbolV2 * clone() const =0
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
QgsDataDefined dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
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)
QString labelForRange(double lower, double upper) const
QDomElement nextSiblingElement(const QString &tagName) const
double minSymbolSize() const
return the min symbol size when graduated by size
Container of fields for a vector layer.
Definition: qgsfield.h:209
void updateColorRamp(QgsVectorColorRampV2 *ramp=nullptr, bool inverted=false)
Update the color ramp used.
Line symbol.
Definition: qgssymbolv2.h:82
QString expressionString() const
Returns the expression string of this QgsDataDefined.
void move(int from, int to)
QVector< T > toVector() const
QHash< QgsSymbolV2 *, QgsSymbolV2 * > mTempSymbols
temporary symbols, used for data-defined rotation and scaling
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)
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...
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)
Q_DECL_DEPRECATED QList< double > getDataValues(QgsVectorLayer *vlayer)
Evaluates the data expression and returns the list of values from the layer.
QScopedPointer< QgsSymbolV2 > mSymbol
QString type() const
Definition: qgsrendererv2.h:92
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)
bool rangesHaveGaps() const
Tests whether classes assigned to the renderer have gaps between the ranges.
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)
QgsSymbolV2::ScaleMethod scaleMethod() const
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...
bool operator==(const QgsRendererRangeV2LabelFormat &other) const
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
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)
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
bool isEmpty() 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) ...
QgsSymbolV2 * symbol() const
GraduatedMethod graduatedMethod() const
return the method used for graduation (either size or color)
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)
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()
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
bool operator<(const QgsRendererRangeV2 &other) const
void setFromDomElement(QDomElement &element)
QString legendKeyForValue(double value) const
Returns the matching legend key for a value.
QgsSymbolV2 * symbolForValue(double value)
static void convertSymbolSizeScale(QgsSymbolV2 *symbol, QgsSymbolV2::ScaleMethod method, const QString &field)
bool labelGreaterThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
bool useExpression() const
Returns if the field or the expression part is active.
QDomText createTextNode(const QString &value)
T * data() const
void clear()
bool valueLessThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
iterator end()
const T value(const Key &key) const
virtual QgsGraduatedSymbolRendererV2 * clone() const override
const QgsRendererRangeV2LabelFormat & labelFormat() const
Return the label format used to generate default classification labels.
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
bool hasNext() const
QgsExpressionContext & expressionContext()
Gets the expression context.
void copyRendererData(QgsFeatureRendererV2 *destRenderer) const
Clones generic renderer data to another renderer.
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)
bool rangesOverlap() const
Tests whether classes assigned to the renderer have ranges which overlap.
static QList< double > _calcEqualIntervalBreaks(double minimum, double maximum, int classes)
const T & at(int i) const
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
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.
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.
int mAttrNum
attribute index (derived from attribute name in startRender)
QList< T > toList() const
QgsDataDefined dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
void stopRender(QgsRenderContext &context)
QString mid(int position, int n) const
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)
void insert(int i, const T &value)
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=nullptr)
double maxSymbolSize() const
return the max symbol size when graduated by size
const QgsRangeList & ranges() const
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props, bool firstRange=false) const
Creates a DOM element representing the range in SLD format.
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const
Return a list of symbology items for the legend.
QgsFeatureRequest::OrderBy mOrderBy
bool operator!=(const QgsRendererRangeV2LabelFormat &other) const
static QString encodeScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
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 .
bool usingSymbolLevels() const
T & last()
void setScaleMethodToSymbol(QgsSymbolV2 *symbol, int scaleMethod)
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
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
Sets the source color ramp.
QScopedPointer< QgsSymbolV2 > mSourceSymbol
QList< T > toList() const
void setRenderHints(int hints)
Definition: qgssymbolv2.h:207
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)
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.
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
QgsRendererRangeV2 & operator=(QgsRendererRangeV2 range)
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.
int fieldNameIndex(const QString &fieldName) const
Returns the index of a field name or -1 if the field does not exist.
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
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)
QColor color() const
bool updateRangeLabel(int rangeIndex, const QString &label)