QGIS API Documentation  2.12.0-Lyon
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( 0 )
43  , mLabel()
44  , mRender( true )
45 {
46 }
47 
48 QgsRendererRangeV2::QgsRendererRangeV2( double lowerValue, double upperValue, QgsSymbolV2* symbol, const QString& label, bool render )
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() : NULL )
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  ( 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 
119 void QgsRendererRangeV2::setUpperValue( double upperValue )
120 {
122 }
123 
124 void QgsRendererRangeV2::setLowerValue( double lowerValue )
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 
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( mLowerValue ).arg( 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 AND %1 <= %3" )
167  .arg( attrName.replace( "\"", "\"\"" ) )
168  .arg( mLowerValue ).arg( mUpperValue );
169  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, filterFunc );
170 
171  mSymbol->toSld( doc, ruleElem, props );
172 }
173 
175 
178 
180  mFormat( " %1 - %2 " ),
181  mPrecision( 4 ),
182  mTrimTrailingZeroes( false ),
183  mNumberScale( 1.0 ),
184  mNumberSuffix( "" ),
185  mReTrailingZeroes( "[.,]?0*$" ),
186  mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
187 {
188 }
189 
190 QgsRendererRangeV2LabelFormat::QgsRendererRangeV2LabelFormat( const QString& format, int precision, bool trimTrailingZeroes ):
191  mReTrailingZeroes( "[.,]?0*$" ),
192  mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
193 {
194  setFormat( format );
195  setPrecision( precision );
196  setTrimTrailingZeroes( trimTrailingZeroes );
197 }
198 
199 
201 {
202  return
203  format() == other.format() &&
204  precision() == other.precision() &&
206 }
207 
209 {
210  return !( *this == other );
211 }
212 
214 {
215  // Limit the range of decimal places to a reasonable range
216  precision = qBound( MinPrecision, precision, MaxPrecision );
218  mNumberScale = 1.0;
219  mNumberSuffix = "";
220  while ( precision < 0 )
221  {
222  precision++;
223  mNumberScale /= 10.0;
224  mNumberSuffix.append( '0' );
225  }
226 }
227 
229 {
230  return labelForRange( range.lowerValue(), range.upperValue() );
231 }
232 
234 {
235  if ( mPrecision > 0 )
236  {
237  QString valueStr = QString::number( value, 'f', mPrecision );
238  if ( mTrimTrailingZeroes )
239  valueStr = valueStr.replace( mReTrailingZeroes, "" );
240  if ( mReNegativeZero.exactMatch( valueStr ) )
241  valueStr = valueStr.mid( 1 );
242  return valueStr;
243  }
244  else
245  {
246  QString valueStr = QString::number( value * mNumberScale, 'f', 0 );
247  if ( valueStr == "-0" )
248  valueStr = "0";
249  if ( valueStr != "0" )
250  valueStr = valueStr + mNumberSuffix;
251  return valueStr;
252  }
253 }
254 
256 {
257  QString lowerStr = formatNumber( lower );
258  QString upperStr = formatNumber( upper );
259 
260  QString legend( mFormat );
261  return legend.replace( "%1", lowerStr ).replace( "%2", upperStr );
262 }
263 
265 {
266  mFormat = element.attribute( "format",
267  element.attribute( "prefix", " " ) + "%1" +
268  element.attribute( "separator", " - " ) + "%2" +
269  element.attribute( "suffix", " " )
270  );
271  setPrecision( element.attribute( "decimalplaces", "4" ).toInt() );
272  mTrimTrailingZeroes = element.attribute( "trimtrailingzeroes", "false" ) == "true";
273 }
274 
276 {
277  element.setAttribute( "format", mFormat );
278  element.setAttribute( "decimalplaces", mPrecision );
279  element.setAttribute( "trimtrailingzeroes", mTrimTrailingZeroes ? "true" : "false" );
280 }
281 
283 
285  : QgsFeatureRendererV2( "graduatedSymbol" )
286  , mAttrName( attrName )
287  , mRanges( ranges )
288  , mMode( Custom )
289  , mInvertedColorRamp( false )
290  , mScaleMethod( DEFAULT_SCALE_METHOD )
291  , mGraduatedMethod( GraduatedColor )
292  , mAttrNum( -1 )
293  , mCounting( false )
294 
295 {
296  // TODO: check ranges for sanity (NULL symbols, invalid ranges)
297 }
298 
300 {
301  mRanges.clear(); // should delete all the symbols
302 }
303 
305 {
306  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
307  {
308  if ( it->lowerValue() <= value && it->upperValue() >= value )
309  {
310  if ( it->renderState() || mCounting )
311  return it->symbol();
312  else
313  return NULL;
314  }
315  }
316  // the value is out of the range: return NULL instead of symbol
317  return NULL;
318 }
319 
321 {
322  QgsSymbolV2* symbol = originalSymbolForFeature( feature, context );
323  if ( symbol == NULL )
324  return NULL;
325 
326  if ( !mRotation.data() && !mSizeScale.data() )
327  return symbol; // no data-defined rotation/scaling - just return the symbol
328 
329  // find out rotation, size scale
330  const double rotation = mRotation.data() ? mRotation->evaluate( &context.expressionContext() ).toDouble() : 0;
331  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( &context.expressionContext() ).toDouble() : 1.;
332 
333  // take a temporary symbol (or create it if doesn't exist)
334  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
335 
336  // modify the temporary symbol and return it
337  if ( tempSymbol->type() == QgsSymbolV2::Marker )
338  {
339  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
340  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
341  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
342  markerSymbol->setScaleMethod( mScaleMethod );
343  }
344  else if ( tempSymbol->type() == QgsSymbolV2::Line )
345  {
346  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
347  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
348  }
349  return tempSymbol;
350 }
351 
353 {
354  Q_UNUSED( context );
355  QgsAttributes attrs = feature.attributes();
356  QVariant value;
357  if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
358  {
359  value = mExpression->evaluate( &context.expressionContext() );
360  }
361  else
362  {
363  value = attrs.at( mAttrNum );
364  }
365 
366  // Null values should not be categorized
367  if ( value.isNull() )
368  return NULL;
369 
370  // find the right category
371  return symbolForValue( value.toDouble() );
372 }
373 
375 {
376  mCounting = context.rendererScale() == 0.0;
377 
378  // find out classification attribute index from name
379  mAttrNum = fields.fieldNameIndex( mAttrName );
380 
381  if ( mAttrNum == -1 )
382  {
384  mExpression->prepare( &context.expressionContext() );
385  }
386 
388  for ( ; it != mRanges.end(); ++it )
389  {
390  if ( !it->symbol() )
391  continue;
392 
393  it->symbol()->startRender( context, &fields );
394 
395  if ( mRotation.data() || mSizeScale.data() )
396  {
397  QgsSymbolV2* tempSymbol = it->symbol()->clone();
400  tempSymbol->startRender( context, &fields );
401  mTempSymbols[ it->symbol()] = tempSymbol;
402  }
403  }
404  return;
405 }
406 
408 {
410  for ( ; it != mRanges.end(); ++it )
411  {
412  if ( !it->symbol() )
413  continue;
414 
415  it->symbol()->stopRender( context );
416  }
417 
418  // cleanup mTempSymbols
420  for ( ; it2 != mTempSymbols.end(); ++it2 )
421  {
422  it2.value()->stopRender( context );
423  delete it2.value();
424  }
426 }
427 
429 {
430  QSet<QString> attributes;
431 
432  // mAttrName can contain either attribute name or an expression.
433  // Sometimes it is not possible to distinguish between those two,
434  // e.g. "a - b" can be both a valid attribute name or expression.
435  // Since we do not have access to fields here, try both options.
436  attributes << mAttrName;
437 
438  QgsExpression testExpr( mAttrName );
439  if ( !testExpr.hasParserError() )
440  attributes.unite( testExpr.referencedColumns().toSet() );
441 
442  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
443  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
444 
446  for ( ; range_it != mRanges.constEnd(); ++range_it )
447  {
448  QgsSymbolV2* symbol = range_it->symbol();
449  if ( symbol )
450  {
451  attributes.unite( symbol->usedAttributes() );
452  }
453  }
454  return attributes.toList();
455 }
456 
458 {
459  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
460  return false;
461  mRanges[rangeIndex].setSymbol( symbol );
462  return true;
463 }
464 
465 bool QgsGraduatedSymbolRendererV2::updateRangeLabel( int rangeIndex, const QString& label )
466 {
467  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
468  return false;
469  mRanges[rangeIndex].setLabel( label );
470  return true;
471 }
472 
473 bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double value )
474 {
475  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
476  return false;
477  QgsRendererRangeV2 &range = mRanges[rangeIndex];
478  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
479  range.setUpperValue( value );
480  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
481  return true;
482 }
483 
484 bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double value )
485 {
486  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
487  return false;
488  QgsRendererRangeV2 &range = mRanges[rangeIndex];
489  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
490  range.setLowerValue( value );
491  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
492  return true;
493 }
494 
496 {
497  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
498  return false;
499  mRanges[rangeIndex].setRenderState( value );
500  return true;
501 }
502 
504 {
505  QString s = QString( "GRADUATED: attr %1\n" ).arg( mAttrName );
506  for ( int i = 0; i < mRanges.count(); i++ )
507  s += mRanges[i].dump();
508  return s;
509 }
510 
512 {
514  r->setMode( mMode );
515  if ( mSourceSymbol.data() )
516  r->setSourceSymbol( mSourceSymbol->clone() );
517  if ( mSourceColorRamp.data() )
518  {
519  r->setSourceColorRamp( mSourceColorRamp->clone() );
521  }
524  r->setLabelFormat( labelFormat() );
526  copyPaintEffect( r );
527  return r;
528 }
529 
531 {
532  QgsStringMap props;
533  props[ "attribute" ] = mAttrName;
534  props[ "method" ] = graduatedMethodStr( mGraduatedMethod );
535  if ( mRotation.data() )
536  props[ "angle" ] = mRotation->expression();
537  if ( mSizeScale.data() )
538  props[ "scale" ] = mSizeScale->expression();
539 
540  // create a Rule for each range
541  for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
542  {
543  QgsStringMap catProps( props );
544  it->toSld( doc, element, catProps );
545  }
546 }
547 
549 {
550  Q_UNUSED( context );
551  QgsSymbolV2List lst;
552  lst.reserve( mRanges.count() );
553  for ( int i = 0; i < mRanges.count(); i++ )
554  lst.append( mRanges[i].symbol() );
555  return lst;
556 }
557 
558 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
559 {
560 
561  // Equal interval algorithm
562  //
563  // Returns breaks based on dividing the range ('minimum' to 'maximum')
564  // into 'classes' parts.
565 
566  double step = ( maximum - minimum ) / classes;
567 
568  QList<double> breaks;
569  double value = minimum;
570  breaks.reserve( classes );
571  for ( int i = 0; i < classes; i++ )
572  {
573  value += step;
574  breaks.append( value );
575  }
576 
577  // floating point arithmetics is not precise:
578  // set the last break to be exactly maximum so we do not miss it
579  breaks[classes-1] = maximum;
580 
581  return breaks;
582 }
583 
584 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
585 {
586  // q-th quantile of a data set:
587  // value where q fraction of data is below and (1-q) fraction is above this value
588  // Xq = (1 - r) * X_NI1 + r * X_NI2
589  // NI1 = (int) (q * (n+1))
590  // NI2 = NI1 + 1
591  // r = q * (n+1) - (int) (q * (n+1))
592  // (indices of X: 1...n)
593 
594  // sort the values first
595  qSort( values );
596 
597  QList<double> breaks;
598 
599  // If there are no values to process: bail out
600  if ( !values.count() )
601  return breaks;
602 
603  int n = values.count();
604  double Xq = n > 0 ? values[0] : 0.0;
605 
606  breaks.reserve( classes );
607  for ( int i = 1; i < classes; i++ )
608  {
609  if ( n > 1 )
610  {
611  double q = i / ( double ) classes;
612  double a = q * ( n - 1 );
613  int aa = ( int )( a );
614 
615  double r = a - aa;
616  Xq = ( 1 - r ) * values[aa] + r * values[aa+1];
617  }
618  breaks.append( Xq );
619  }
620 
621  breaks.append( values[ n-1 ] );
622 
623  return breaks;
624 }
625 
626 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<double> &labels )
627 {
628 
629  // C++ implementation of the standard deviation class interval algorithm
630  // as implemented in the 'classInt' package available for the R statistical
631  // prgramming language.
632 
633  // Returns breaks based on 'prettyBreaks' of the centred and scaled
634  // values of 'values', and may have a number of classes different from 'classes'.
635 
636  // If there are no values to process: bail out
637  if ( !values.count() )
638  return QList<double>();
639 
640  double mean = 0.0;
641  double stdDev = 0.0;
642  int n = values.count();
643  double minimum = values[0];
644  double maximum = values[0];
645 
646  for ( int i = 0; i < n; i++ )
647  {
648  mean += values[i];
649  minimum = qMin( values[i], minimum ); // could use precomputed max and min
650  maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
651  }
652  mean = mean / ( double ) n;
653 
654  double sd = 0.0;
655  for ( int i = 0; i < n; i++ )
656  {
657  sd = values[i] - mean;
658  stdDev += sd * sd;
659  }
660  stdDev = sqrt( stdDev / n );
661 
662  QList<double> breaks = QgsSymbolLayerV2Utils::prettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
663  for ( int i = 0; i < breaks.count(); i++ )
664  {
665  labels.append( breaks[i] );
666  breaks[i] = ( breaks[i] * stdDev ) + mean;
667  }
668 
669  return breaks;
670 } // _calcStdDevBreaks
671 
672 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
673  double minimum, double maximum,
674  int maximumSize = 1000 )
675 {
676  // Jenks Optimal (Natural Breaks) algorithm
677  // Based on the Jenks algorithm from the 'classInt' package available for
678  // the R statistical prgramming language, and from Python code from here:
679  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
680  // and is based on a JAVA and Fortran code available here:
681  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
682 
683  // Returns class breaks such that classes are internally homogeneous while
684  // assuring heterogeneity among classes.
685 
686  if ( !values.count() )
687  return QList<double>();
688 
689  if ( classes <= 1 )
690  {
691  return QList<double>() << maximum;
692  }
693 
694  if ( classes >= values.size() )
695  {
696  return values;
697  }
698 
699  QVector<double> sample;
700 
701  // if we have lots of values, we need to take a random sample
702  if ( values.size() > maximumSize )
703  {
704  // for now, sample at least maximumSize values or a 10% sample, whichever
705  // is larger. This will produce a more representative sample for very large
706  // layers, but could end up being computationally intensive...
707 
708  sample.resize( qMax( maximumSize, values.size() / 10 ) );
709 
710  QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
711  QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
712 
713  sample[ 0 ] = minimum;
714  sample[ 1 ] = maximum;
715  for ( int i = 2; i < sample.size(); i++ )
716  {
717  // pick a random integer from 0 to n
718  double r = qrand();
719  int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
720  sample[ i ] = values[ j ];
721  }
722  }
723  else
724  {
725  sample = values.toVector();
726  }
727 
728  int n = sample.size();
729 
730  // sort the sample values
731  qSort( sample );
732 
733  QVector< QVector<int> > matrixOne( n + 1 );
734  QVector< QVector<double> > matrixTwo( n + 1 );
735 
736  for ( int i = 0; i <= n; i++ )
737  {
738  matrixOne[i].resize( classes + 1 );
739  matrixTwo[i].resize( classes + 1 );
740  }
741 
742  for ( int i = 1; i <= classes; i++ )
743  {
744  matrixOne[0][i] = 1;
745  matrixOne[1][i] = 1;
746  matrixTwo[0][i] = 0.0;
747  for ( int j = 2; j <= n; j++ )
748  {
749  matrixTwo[j][i] = std::numeric_limits<double>::max();
750  }
751  }
752 
753  for ( int l = 2; l <= n; l++ )
754  {
755  double s1 = 0.0;
756  double s2 = 0.0;
757  int w = 0;
758 
759  double v = 0.0;
760 
761  for ( int m = 1; m <= l; m++ )
762  {
763  int i3 = l - m + 1;
764 
765  double val = sample[ i3 - 1 ];
766 
767  s2 += val * val;
768  s1 += val;
769  w++;
770 
771  v = s2 - ( s1 * s1 ) / ( double ) w;
772  int i4 = i3 - 1;
773  if ( i4 != 0 )
774  {
775  for ( int j = 2; j <= classes; j++ )
776  {
777  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
778  {
779  matrixOne[l][j] = i4;
780  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
781  }
782  }
783  }
784  }
785  matrixOne[l][1] = 1;
786  matrixTwo[l][1] = v;
787  }
788 
789  QVector<double> breaks( classes );
790  breaks[classes-1] = sample[n-1];
791 
792  for ( int j = classes, k = n; j >= 2; j-- )
793  {
794  int id = matrixOne[k][j] - 1;
795  breaks[j - 2] = sample[id];
796  k = matrixOne[k][j] - 1;
797  }
798 
799  return breaks.toList();
800 } //_calcJenksBreaks
801 
802 
804  QgsVectorLayer* vlayer,
805  const QString& attrName,
806  int classes,
807  Mode mode,
808  QgsSymbolV2* symbol,
809  QgsVectorColorRampV2* ramp,
810  bool inverted,
811  const QgsRendererRangeV2LabelFormat& labelFormat
812 )
813 {
815  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
816  r->setSourceSymbol( symbol->clone() );
817  r->setSourceColorRamp( ramp->clone() );
818  r->setInvertedColorRamp( inverted );
819  r->setMode( mode );
820  r->setLabelFormat( labelFormat );
821  r->updateClasses( vlayer, mode, classes );
822  return r;
823 }
824 
826 {
827  bool ok;
828  return vlayer->getDoubleValues( mAttrName, ok );
829 }
830 
832 {
833  if ( mAttrName.isEmpty() )
834  return;
835 
836  setMode( mode );
837  // Custom classes are not recalculated
838  if ( mode == Custom )
839  return;
840 
841  if ( nclasses < 1 )
842  nclasses = 1;
843 
844  QList<double> values;
845  bool valuesLoaded = false;
846  double minimum;
847  double maximum;
848 
849  int attrNum = vlayer->fieldNameIndex( mAttrName );
850 
851  bool ok;
852  if ( attrNum == -1 )
853  {
854  values = vlayer->getDoubleValues( mAttrName, ok );
855  if ( !ok || values.isEmpty() )
856  return;
857 
858  qSort( values ); // vmora: is wondering if O( n log(n) ) is really necessary here, min and max are O( n )
859  minimum = values.first();
860  maximum = values.last();
861  valuesLoaded = true;
862  }
863  else
864  {
865  minimum = vlayer->minimumValue( attrNum ).toDouble();
866  maximum = vlayer->maximumValue( attrNum ).toDouble();
867  }
868 
869  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
870  QList<double> breaks;
871  QList<double> labels;
872  if ( mode == EqualInterval )
873  {
874  breaks = _calcEqualIntervalBreaks( minimum, maximum, nclasses );
875  }
876  else if ( mode == Pretty )
877  {
878  breaks = QgsSymbolLayerV2Utils::prettyBreaks( minimum, maximum, nclasses );
879  }
880  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
881  {
882  // get values from layer
883  if ( !valuesLoaded )
884  {
885  values = vlayer->getDoubleValues( mAttrName, ok );
886  }
887 
888  // calculate the breaks
889  if ( mode == Quantile )
890  {
891  breaks = _calcQuantileBreaks( values, nclasses );
892  }
893  else if ( mode == Jenks )
894  {
895  breaks = _calcJenksBreaks( values, nclasses, minimum, maximum );
896  }
897  else if ( mode == StdDev )
898  {
899  breaks = _calcStdDevBreaks( values, nclasses, labels );
900  }
901  }
902  else
903  {
904  Q_ASSERT( false );
905  }
906 
907  double lower, upper = minimum;
908  QString label;
910 
911  // "breaks" list contains all values at class breaks plus maximum as last break
912 
913  int i = 0;
914  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
915  {
916  lower = upper; // upper border from last interval
917  upper = *it;
918 
919  // Label - either StdDev label or default label for a range
920  if ( mode == StdDev )
921  {
922  if ( i == 0 )
923  {
924  label = "< " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
925  }
926  else if ( i == labels.count() - 1 )
927  {
928  label = ">= " + QString::number( labels[i-1], 'f', 2 ) + " Std Dev";
929  }
930  else
931  {
932  label = QString::number( labels[i-1], 'f', 2 ) + " Std Dev" + " - " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
933  }
934  }
935  else
936  {
937  label = mLabelFormat.labelForRange( lower, upper );
938  }
939  QgsSymbolV2* newSymbol = mSourceSymbol ? mSourceSymbol->clone() : QgsSymbolV2::defaultSymbol( vlayer->geometryType() );
940  addClass( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
941  }
943 }
944 
946 {
947  QDomElement symbolsElem = element.firstChildElement( "symbols" );
948  if ( symbolsElem.isNull() )
949  return NULL;
950 
951  QDomElement rangesElem = element.firstChildElement( "ranges" );
952  if ( rangesElem.isNull() )
953  return NULL;
954 
955  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
957 
958  QDomElement rangeElem = rangesElem.firstChildElement();
959  while ( !rangeElem.isNull() )
960  {
961  if ( rangeElem.tagName() == "range" )
962  {
963  double lowerValue = rangeElem.attribute( "lower" ).toDouble();
964  double upperValue = rangeElem.attribute( "upper" ).toDouble();
965  QString symbolName = rangeElem.attribute( "symbol" );
966  QString label = rangeElem.attribute( "label" );
967  bool render = rangeElem.attribute( "render", "true" ) != "false";
968  if ( symbolMap.contains( symbolName ) )
969  {
970  QgsSymbolV2* symbol = symbolMap.take( symbolName );
971  ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label, render ) );
972  }
973  }
974  rangeElem = rangeElem.nextSiblingElement();
975  }
976 
977  QString attrName = element.attribute( "attr" );
978 
979  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
980 
981  QString attrMethod = element.attribute( "graduatedMethod" );
982  if ( attrMethod.length() )
983  {
984  if ( attrMethod == graduatedMethodStr( GraduatedColor ) )
986  else if ( attrMethod == graduatedMethodStr( GraduatedSize ) )
988  }
989 
990 
991  // delete symbols if there are any more
993 
994  // try to load source symbol (optional)
995  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
996  if ( !sourceSymbolElem.isNull() )
997  {
998  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
999  if ( sourceSymbolMap.contains( "0" ) )
1000  {
1001  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
1002  }
1003  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
1004  }
1005 
1006  // try to load color ramp (optional)
1007  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
1008  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
1009  {
1010  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
1011  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
1012  if ( !invertedColorRampElem.isNull() )
1013  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
1014  }
1015 
1016  // try to load mode
1017  QDomElement modeElem = element.firstChildElement( "mode" );
1018  if ( !modeElem.isNull() )
1019  {
1020  QString modeString = modeElem.attribute( "name" );
1021  if ( modeString == "equal" )
1022  r->setMode( EqualInterval );
1023  else if ( modeString == "quantile" )
1024  r->setMode( Quantile );
1025  else if ( modeString == "jenks" )
1026  r->setMode( Jenks );
1027  else if ( modeString == "stddev" )
1028  r->setMode( StdDev );
1029  else if ( modeString == "pretty" )
1030  r->setMode( Pretty );
1031  }
1032 
1033  QDomElement rotationElem = element.firstChildElement( "rotation" );
1034  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
1035  {
1036  for ( QgsRangeList::iterator it = r->mRanges.begin(); it != r->mRanges.end(); ++it )
1037  {
1038  convertSymbolRotation( it->symbol(), rotationElem.attribute( "field" ) );
1039  }
1040  if ( r->mSourceSymbol.data() )
1041  {
1042  convertSymbolRotation( r->mSourceSymbol.data(), rotationElem.attribute( "field" ) );
1043  }
1044  }
1045 
1046  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
1047  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
1048  {
1049  for ( QgsRangeList::iterator it = r->mRanges.begin(); it != r->mRanges.end(); ++it )
1050  {
1051  convertSymbolSizeScale( it->symbol(),
1052  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
1053  sizeScaleElem.attribute( "field" ) );
1054  }
1055  if ( r->mSourceSymbol.data() && r->mSourceSymbol->type() == QgsSymbolV2::Marker )
1056  {
1058  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
1059  sizeScaleElem.attribute( "field" ) );
1060  }
1061  }
1062 
1063  QDomElement labelFormatElem = element.firstChildElement( "labelformat" );
1064  if ( ! labelFormatElem.isNull() )
1065  {
1067  labelFormat.setFromDomElement( labelFormatElem );
1068  r->setLabelFormat( labelFormat );
1069  }
1070  // TODO: symbol levels
1071  return r;
1072 }
1073 
1075 {
1076  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1077  rendererElem.setAttribute( "type", "graduatedSymbol" );
1078  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
1079  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
1080  rendererElem.setAttribute( "attr", mAttrName );
1081  rendererElem.setAttribute( "graduatedMethod", graduatedMethodStr( mGraduatedMethod ) );
1082 
1083  // ranges
1084  int i = 0;
1086  QDomElement rangesElem = doc.createElement( "ranges" );
1088  for ( ; it != mRanges.constEnd(); ++it )
1089  {
1090  const QgsRendererRangeV2& range = *it;
1091  QString symbolName = QString::number( i );
1092  symbols.insert( symbolName, range.symbol() );
1093 
1094  QDomElement rangeElem = doc.createElement( "range" );
1095  rangeElem.setAttribute( "lower", QString::number( range.lowerValue(), 'f' ) );
1096  rangeElem.setAttribute( "upper", QString::number( range.upperValue(), 'f' ) );
1097  rangeElem.setAttribute( "symbol", symbolName );
1098  rangeElem.setAttribute( "label", range.label() );
1099  rangeElem.setAttribute( "render", range.renderState() ? "true" : "false" );
1100  rangesElem.appendChild( rangeElem );
1101  i++;
1102  }
1103 
1104  rendererElem.appendChild( rangesElem );
1105 
1106  // save symbols
1107  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
1108  rendererElem.appendChild( symbolsElem );
1109 
1110  // save source symbol
1111  if ( mSourceSymbol.data() )
1112  {
1113  QgsSymbolV2Map sourceSymbols;
1114  sourceSymbols.insert( "0", mSourceSymbol.data() );
1115  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
1116  rendererElem.appendChild( sourceSymbolElem );
1117  }
1118 
1119  // save source color ramp
1120  if ( mSourceColorRamp.data() )
1121  {
1122  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
1123  rendererElem.appendChild( colorRampElem );
1124  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
1125  invertedElem.setAttribute( "value", mInvertedColorRamp );
1126  rendererElem.appendChild( invertedElem );
1127  }
1128 
1129  // save mode
1130  QString modeString;
1131  if ( mMode == EqualInterval )
1132  modeString = "equal";
1133  else if ( mMode == Quantile )
1134  modeString = "quantile";
1135  else if ( mMode == Jenks )
1136  modeString = "jenks";
1137  else if ( mMode == StdDev )
1138  modeString = "stddev";
1139  else if ( mMode == Pretty )
1140  modeString = "pretty";
1141  if ( !modeString.isEmpty() )
1142  {
1143  QDomElement modeElem = doc.createElement( "mode" );
1144  modeElem.setAttribute( "name", modeString );
1145  rendererElem.appendChild( modeElem );
1146  }
1147 
1148  QDomElement rotationElem = doc.createElement( "rotation" );
1149  if ( mRotation.data() )
1151  rendererElem.appendChild( rotationElem );
1152 
1153  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
1154  if ( mSizeScale.data() )
1156  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
1157  rendererElem.appendChild( sizeScaleElem );
1158 
1159  QDomElement labelFormatElem = doc.createElement( "labelformat" );
1160  mLabelFormat.saveToDomElement( labelFormatElem );
1161  rendererElem.appendChild( labelFormatElem );
1162 
1164  mPaintEffect->saveProperties( doc, rendererElem );
1165 
1166  return rendererElem;
1167 }
1168 
1170 {
1172  int count = ranges().count();
1173  lst.reserve( count );
1174  for ( int i = 0; i < count; i++ )
1175  {
1176  const QgsRendererRangeV2& range = ranges()[i];
1177  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
1178  lst << qMakePair( range.label(), pix );
1179  }
1180  return lst;
1181 }
1182 
1184 {
1185  QgsLegendSymbolListV2 list;
1186  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
1187  {
1188  // check that all symbols that have the same size expression
1189  QgsDataDefined ddSize;
1190  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1191  {
1192  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( range.symbol() );
1193  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
1194  {
1195  // no common size expression
1197  }
1198  else
1199  {
1200  ddSize = symbol->dataDefinedSize();
1201  }
1202  }
1203 
1204  if ( !ddSize.isActive() || !ddSize.useExpression() )
1205  {
1207  }
1208 
1209  QgsScaleExpression exp( ddSize.expressionString() );
1210  if ( exp.type() != QgsScaleExpression::Unknown )
1211  {
1212  QgsLegendSymbolItemV2 title( NULL, exp.baseExpression(), "" );
1213  list << title;
1214  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
1215  {
1217  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
1219  s->setSize( exp.size( v ) );
1220  list << si;
1221  }
1222  // now list the graduated symbols
1224  Q_FOREACH ( const QgsLegendSymbolItemV2& item, list2 )
1225  list << item;
1226  return list;
1227  }
1228  }
1229 
1231 }
1232 
1234 {
1235  Q_UNUSED( scaleDenominator );
1236  QgsLegendSymbolList lst;
1237 
1238  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1239  {
1240  if ( rule.isEmpty() || range.label() == rule )
1241  {
1242  lst << qMakePair( range.label(), range.symbol() );
1243  }
1244  }
1245  return lst;
1246 }
1247 
1249 {
1250  return mSourceSymbol.data();
1251 }
1253 {
1254  mSourceSymbol.reset( sym );
1255 }
1256 
1258 {
1259  return mSourceColorRamp.data();
1260 }
1261 
1263 {
1264  mSourceColorRamp.reset( ramp );
1265 }
1266 
1268 {
1269  double min = DBL_MAX;
1270  for ( int i = 0; i < mRanges.count(); i++ )
1271  {
1272  double sz = 0;
1273  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1274  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1275  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1276  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1277  min = qMin( sz, min );
1278  }
1279  return min;
1280 }
1281 
1283 {
1284  double max = DBL_MIN;
1285  for ( int i = 0; i < mRanges.count(); i++ )
1286  {
1287  double sz = 0;
1288  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1289  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1290  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1291  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1292  max = qMax( sz, max );
1293  }
1294  return max;
1295 }
1296 
1297 void QgsGraduatedSymbolRendererV2::setSymbolSizes( double minSize, double maxSize )
1298 {
1299  for ( int i = 0; i < mRanges.count(); i++ )
1300  {
1301  QScopedPointer<QgsSymbolV2> symbol( mRanges[i].symbol() ? mRanges[i].symbol()->clone() : 0 );
1302  const double size = mRanges.count() > 1
1303  ? minSize + i * ( maxSize - minSize ) / ( mRanges.count() - 1 )
1304  : .5 * ( maxSize + minSize );
1305  if ( symbol->type() == QgsSymbolV2::Marker )
1306  static_cast< QgsMarkerSymbolV2 * >( symbol.data() )->setSize( size );
1307  if ( symbol->type() == QgsSymbolV2::Line )
1308  static_cast< QgsLineSymbolV2 * >( symbol.data() )->setWidth( size );
1309  updateRangeSymbol( i, symbol.take() );
1310  }
1311 }
1312 
1314 {
1315  int i = 0;
1316  if ( ramp )
1317  {
1318  setSourceColorRamp( ramp );
1319  setInvertedColorRamp( inverted );
1320  }
1321 
1322  if ( mSourceColorRamp )
1323  {
1324  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1325  {
1326  QgsSymbolV2 *symbol = range.symbol() ? range.symbol()->clone() : 0;
1327  if ( symbol )
1328  {
1329  double colorValue;
1330  if ( inverted )
1331  colorValue = ( mRanges.count() > 1 ? ( double )( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
1332  else
1333  colorValue = ( mRanges.count() > 1 ? ( double ) i / ( mRanges.count() - 1 ) : 0 );
1334  symbol->setColor( mSourceColorRamp->color( colorValue ) );
1335  }
1336  updateRangeSymbol( i, symbol );
1337  ++i;
1338  }
1339  }
1340 
1341 }
1342 
1344 {
1345  if ( !sym )
1346  return;
1347 
1348  int i = 0;
1349  Q_FOREACH ( const QgsRendererRangeV2& range, mRanges )
1350  {
1351  QScopedPointer<QgsSymbolV2> symbol( sym->clone() );
1353  {
1354  symbol->setColor( range.symbol()->color() );
1355  }
1356  else if ( mGraduatedMethod == GraduatedSize )
1357  {
1358  if ( symbol->type() == QgsSymbolV2::Marker )
1359  static_cast<QgsMarkerSymbolV2 *>( symbol.data() )->setSize(
1360  static_cast<QgsMarkerSymbolV2 *>( range.symbol() )->size() );
1361  else if ( symbol->type() == QgsSymbolV2::Line )
1362  static_cast<QgsLineSymbolV2 *>( symbol.data() )->setWidth(
1363  static_cast<QgsLineSymbolV2 *>( range.symbol() )->width() );
1364  }
1365  updateRangeSymbol( i, symbol.take() );
1366  ++i;
1367  }
1368  setSourceSymbol( sym->clone() );
1369 }
1370 
1372 {
1373  if ( mSourceSymbol->type() == QgsSymbolV2::Marker )
1374  {
1375  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
1376  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
1377  }
1378 
1379 }
1380 
1382 {
1383  if ( mSourceSymbol->type() == QgsSymbolV2::Marker )
1384  {
1385  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
1386  QgsDataDefined ddAngle = s->dataDefinedAngle();
1387  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
1388  }
1389 
1390  return QString();
1391 }
1392 
1394 {
1396 }
1397 
1399 {
1401 }
1402 
1404 {
1406  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1407  {
1408  if ( it->symbol() )
1409  setScaleMethodToSymbol( it->symbol(), scaleMethod );
1410  }
1411 }
1412 
1414 {
1415  return true;
1416 }
1417 
1419 {
1420  bool ok;
1421  int index = key.toInt( &ok );
1422  if ( ok && index >= 0 && index < mRanges.size() )
1423  return mRanges[ index ].renderState();
1424  else
1425  return true;
1426 }
1427 
1429 {
1430  bool ok;
1431  int index = key.toInt( &ok );
1432  if ( ok )
1433  updateRangeRenderState( index, state );
1434 }
1435 
1436 
1438 {
1439  QgsSymbolV2* newSymbol = symbol->clone();
1440  QString label = "0.0 - 0.0";
1441  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1442 }
1443 
1444 void QgsGraduatedSymbolRendererV2::addClass( double lower, double upper )
1445 {
1446  QgsSymbolV2* newSymbol = mSourceSymbol->clone();
1447  QString label = mLabelFormat.labelForRange( lower, upper );
1448  mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
1449 }
1450 
1451 void QgsGraduatedSymbolRendererV2::addBreak( double breakValue, bool updateSymbols )
1452 {
1454  while ( it.hasNext() )
1455  {
1456  QgsRendererRangeV2 range = it.next();
1457  if ( range.lowerValue() < breakValue && range.upperValue() > breakValue )
1458  {
1460  newRange.setLowerValue( breakValue );
1461  newRange.setUpperValue( range.upperValue() );
1462  newRange.setLabel( mLabelFormat.labelForRange( newRange ) );
1463  newRange.setSymbol( mSourceSymbol->clone() );
1464 
1465  //update old range
1466  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
1467  range.setUpperValue( breakValue );
1468  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range.lowerValue(), breakValue ) );
1469  it.setValue( range );
1470 
1471  it.insert( newRange );
1472  break;
1473  }
1474  }
1475 
1476  if ( updateSymbols )
1477  {
1478  switch ( mGraduatedMethod )
1479  {
1480  case GraduatedColor:
1482  break;
1483  case GraduatedSize:
1485  break;
1486  }
1487  }
1488 }
1489 
1491 {
1492  mRanges.append( range );
1493 }
1494 
1496 {
1497  mRanges.removeAt( idx );
1498 }
1499 
1501 {
1502  mRanges.clear();
1503 }
1504 
1506 {
1507  if ( updateRanges && labelFormat != mLabelFormat )
1508  {
1509  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1510  {
1511  it->setLabel( labelFormat.labelForRange( *it ) );
1512  }
1513  }
1515 }
1516 
1517 
1519 {
1520  // Find the minimum size of a class
1521  double minClassRange = 0.0;
1522  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1523  {
1524  double range = it->upperValue() - it->lowerValue();
1525  if ( range <= 0.0 )
1526  continue;
1527  if ( minClassRange == 0.0 || range < minClassRange )
1528  minClassRange = range;
1529  }
1530  if ( minClassRange <= 0.0 )
1531  return;
1532 
1533  // Now set the number of decimal places to ensure no more than 20% error in
1534  // representing this range (up to 10% at upper and lower end)
1535 
1536  int ndp = 10;
1537  double nextDpMinRange = 0.0000000099;
1538  while ( ndp > 0 && nextDpMinRange < minClassRange )
1539  {
1540  ndp--;
1541  nextDpMinRange *= 10.0;
1542  }
1543  mLabelFormat.setPrecision( ndp );
1544  if ( updateRanges ) setLabelFormat( mLabelFormat, true );
1545 }
1546 
1548 {
1549  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1550  return;
1551  mRanges.move( from, to );
1552 }
1553 
1555 {
1556  return r1 < r2;
1557 }
1558 
1560 {
1561  return !valueLessThan( r1, r2 );
1562 }
1563 
1565 {
1566  QgsDebugMsg( "Entered" );
1567  if ( order == Qt::AscendingOrder )
1568  {
1569  qSort( mRanges.begin(), mRanges.end(), valueLessThan );
1570  }
1571  else
1572  {
1573  qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1574  }
1575 }
1576 
1578 {
1579  QgsRangeList sortedRanges = mRanges;
1580  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1581 
1582  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1583  if ( it == sortedRanges.constEnd() )
1584  return false;
1585 
1586  if (( *it ).upperValue() < ( *it ).lowerValue() )
1587  return true;
1588 
1589  double prevMax = ( *it ).upperValue();
1590  ++it;
1591 
1592  for ( ; it != sortedRanges.constEnd(); ++it )
1593  {
1594  if (( *it ).upperValue() < ( *it ).lowerValue() )
1595  return true;
1596 
1597  if (( *it ).lowerValue() < prevMax )
1598  return true;
1599 
1600  prevMax = ( *it ).upperValue();
1601  }
1602  return false;
1603 }
1604 
1606 {
1607  QgsRangeList sortedRanges = mRanges;
1608  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1609 
1610  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1611  if ( it == sortedRanges.constEnd() )
1612  return false;
1613 
1614  double prevMax = ( *it ).upperValue();
1615  ++it;
1616 
1617  for ( ; it != sortedRanges.constEnd(); ++it )
1618  {
1619  if ( !qgsDoubleNear(( *it ).lowerValue(), prevMax ) )
1620  return true;
1621 
1622  prevMax = ( *it ).upperValue();
1623  }
1624  return false;
1625 }
1626 
1628 {
1629  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1630 }
1631 
1633 {
1634  return !labelLessThan( r1, r2 );
1635 }
1636 
1638 {
1639  if ( order == Qt::AscendingOrder )
1640  {
1641  qSort( mRanges.begin(), mRanges.end(), labelLessThan );
1642  }
1643  else
1644  {
1645  qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1646  }
1647 }
1648 
1650 {
1651  if ( renderer->type() == "graduatedSymbol" )
1652  {
1653  return dynamic_cast<QgsGraduatedSymbolRendererV2*>( renderer->clone() );
1654  }
1655  if ( renderer->type() == "pointDisplacement" )
1656  {
1657  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1658  if ( pointDisplacementRenderer )
1659  return convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1660  }
1661  if ( renderer->type() == "invertedPolygonRenderer" )
1662  {
1663  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1664  if ( invertedPolygonRenderer )
1665  return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1666  }
1667 
1668  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1669  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1670 
1672  QgsRenderContext context;
1673  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
1674  if ( symbols.size() > 0 )
1675  {
1676  r->setSourceSymbol( symbols.at( 0 )->clone() );
1677  }
1678 
1679  return r;
1680 }
1681 
1683 {
1684  switch ( method )
1685  {
1686  case GraduatedColor: return "GraduatedColor";
1687  case GraduatedSize: return "GraduatedSize";
1688  }
1689  return "";
1690 }
1691 
1692 
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:92
QList< QgsRendererRangeV2 > QgsRangeList
void clear()
static QgsSymbolV2Map loadSymbols(QDomElement &element)
static QList< double > _calcJenksBreaks(QList< double > values, int classes, double minimum, double maximum, int maximumSize=1000)
#define RENDERER_TAG_NAME
Definition: qgsrendererv2.h:48
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 'classes' 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)
SymbolType type() const
Definition: qgssymbolv2.h:95
QScopedPointer< QgsVectorColorRampV2 > mSourceColorRamp
QVariant maximumValue(int index)
Returns maximum value for an attribute column or invalid variant in case of error.
QSet< QString > usedAttributes() const
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's index from name also looks up case-insensitive if there is no match otherwise...
Definition: qgsfield.cpp:382
double rendererScale() const
virtual QgsSymbolV2 * clone() const =0
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
virtual QgsFeatureRendererV2 * clone() const override
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
To be overridden.
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:177
Line symbol.
Definition: qgssymbolv2.h:71
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.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:176
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 minimum value for an attribute column or invalid variant in case of error.
void setValue(const T &value) const
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:268
static const char * graduatedMethodStr(GraduatedMethod method)
QgsPaintEffect * mPaintEffect
void setWidth(double width)
Marker symbol.
Definition: qgssymbolv2.h:70
int size() 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:82
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
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
void resize(int size)
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...
void startRender(QgsRenderContext &context, const QgsFields *fields=0)
bool operator==(const QgsRendererRangeV2LabelFormat &other) const
QgsAttributes attributes() const
Returns the feature's attributes.
Definition: qgsfeature.cpp:92
void setAttribute(const QString &name, const QString &value)
#define DEFAULT_SCALE_METHOD
virtual QgsVectorColorRampV2 * clone() const =0
void swap(QgsRendererRangeV2 &other)
int toInt(bool *ok, int base) const
bool isEmpty() const
bool isEmpty() const
iterator begin()
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)
bool updateRangeLowerValue(int rangeIndex, double value)
void setSize(double size)
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
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)
QgsSymbolV2 * symbolForValue(double value)
static void convertSymbolSizeScale(QgsSymbolV2 *symbol, QgsSymbolV2::ScaleMethod method, const QString &field)
bool labelGreaterThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
QgsFeatureRendererV2 * embeddedRenderer() const
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
QList< double > getDoubleValues(const QString &fieldOrExpression, bool &ok, bool selectedOnly=false, int *nullCount=0)
Fetches all double values from a specified field name or expression.
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.
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.
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.
const QgsFeatureRendererV2 * embeddedRenderer() const
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).
QString mid(int position, int n) const
void copyPaintEffect(QgsFeatureRendererV2 *destRenderer) const
Copies paint effect of this renderer to another renderer.
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:78
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)
double maxSymbolSize() const
return the max symbol size when graduated by size
const QgsRangeList & ranges() const
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const
Return a list of symbology items for the legend.
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)
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
int length() const
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
Sets the source color ramp.
QScopedPointer< QgsSymbolV2 > mSourceSymbol
QList< T > toList() const
void setRenderHints(int hints)
Definition: qgssymbolv2.h:195
static void clearSymbolMap(QgsSymbolV2Map &symbols)
void setTrimTrailingZeroes(bool trimTrailingZeroes)
bool updateRangeUpperValue(int rangeIndex, double value)
QScopedPointer< QgsExpression > mRotation
double toDouble(bool *ok) const
virtual QgsSymbolV2 * originalSymbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return symbol for feature.
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
double ANALYSIS_EXPORT min(double x, double y)
Returns the minimum of two doubles or the first argument if both are equal.
iterator end()
const_iterator constEnd() const
QScopedPointer< QgsExpression > mSizeScale
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
QgsRendererRangeV2 & operator=(QgsRendererRangeV2 range)
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=0)
A vector of attributes.
Definition: qgsfeature.h:109
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.
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)
QgsLegendSymbolListV2 legendSymbolItemsV2() const override
void updateColorRamp(QgsVectorColorRampV2 *ramp=0, bool inverted=false)
Update the color ramp used.
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)