QGIS API Documentation  2.9.0-Master
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 "qgsscaleexpression.h"
25 #include "qgsdatadefined.h"
26 
27 #include "qgsfeature.h"
28 #include "qgsvectorlayer.h"
29 #include "qgslogger.h"
30 #include "qgsvectordataprovider.h"
31 #include "qgsexpression.h"
32 #include <QDomDocument>
33 #include <QDomElement>
34 #include <QSettings> // for legend
35 #include <limits> // for jenks classification
36 #include <ctime>
37 
39  : mLowerValue( 0 )
40  , mUpperValue( 0 )
41  , mSymbol( 0 )
42  , mLabel()
43  , mRender( true )
44 {
45 }
46 
47 QgsRendererRangeV2::QgsRendererRangeV2( double lowerValue, double upperValue, QgsSymbolV2* symbol, QString label, bool render )
48  : mLowerValue( lowerValue )
49  , mUpperValue( upperValue )
50  , mSymbol( symbol )
51  , mLabel( label )
52  , mRender( render )
53 {
54 }
55 
57  : mLowerValue( range.mLowerValue )
58  , mUpperValue( range.mUpperValue )
59  , mSymbol( range.mSymbol.data() ? range.mSymbol->clone() : NULL )
60  , mLabel( range.mLabel )
61  , mRender( range.mRender )
62 {
63 }
64 
65 // cpy and swap idiom, note that the cpy is done with 'pass by value'
67 {
68  swap( range );
69  return *this;
70 }
71 
73 {
74  return
75  lowerValue() < other.lowerValue() ||
76  ( lowerValue() == other.lowerValue() && upperValue() < other.upperValue() );
77 }
78 
79 
81 {
82  qSwap( mLowerValue, other.mLowerValue );
83  qSwap( mUpperValue, other.mUpperValue );
84  qSwap( mSymbol, other.mSymbol );
85  std::swap( mLabel, other.mLabel );
86 }
87 
89 {
90  return mLowerValue;
91 }
92 
94 {
95  return mUpperValue;
96 }
97 
99 {
100  return mSymbol.data();
101 }
102 
104 {
105  return mLabel;
106 }
107 
109 {
110  if ( mSymbol.data() != s ) mSymbol.reset( s );
111 }
112 
113 void QgsRendererRangeV2::setLabel( QString label )
114 {
115  mLabel = label;
116 }
117 
118 void QgsRendererRangeV2::setUpperValue( double upperValue )
119 {
121 }
122 
123 void QgsRendererRangeV2::setLowerValue( double lowerValue )
124 {
126 }
127 
129 {
130  return mRender;
131 }
132 
134 {
135  mRender = render;
136 }
137 
139 {
140  return QString( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel ).arg( mSymbol.data() ? mSymbol->dump() : "(no symbol)" );
141 }
142 
143 void QgsRendererRangeV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
144 {
145  if ( !mSymbol.data() || props.value( "attribute", "" ).isEmpty() )
146  return;
147 
148  QString attrName = props[ "attribute" ];
149 
150  QDomElement ruleElem = doc.createElement( "se:Rule" );
151  element.appendChild( ruleElem );
152 
153  QDomElement nameElem = doc.createElement( "se:Name" );
154  nameElem.appendChild( doc.createTextNode( mLabel ) );
155  ruleElem.appendChild( nameElem );
156 
157  QDomElement descrElem = doc.createElement( "se:Description" );
158  QDomElement titleElem = doc.createElement( "se:Title" );
159  QString descrStr = QString( "range: %1 - %2" ).arg( mLowerValue ).arg( mUpperValue );
160  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
161  descrElem.appendChild( titleElem );
162  ruleElem.appendChild( descrElem );
163 
164  // create the ogc:Filter for the range
165  QString filterFunc = QString( "%1 > %2 AND %1 <= %3" )
166  .arg( attrName.replace( "\"", "\"\"" ) )
167  .arg( mLowerValue ).arg( mUpperValue );
168  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, filterFunc );
169 
170  mSymbol->toSld( doc, ruleElem, props );
171 }
172 
174 
177 
179  mFormat( " %1 - %2 " ),
180  mPrecision( 4 ),
181  mTrimTrailingZeroes( false ),
182  mNumberScale( 1.0 ),
183  mNumberSuffix( "" ),
184  mReTrailingZeroes( "[.,]?0*$" ),
185  mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
186 {
187 }
188 
189 QgsRendererRangeV2LabelFormat::QgsRendererRangeV2LabelFormat( QString format, int precision, bool trimTrailingZeroes ):
190  mReTrailingZeroes( "[.,]?0*$" ),
191  mReNegativeZero( "^\\-0(?:[.,]0*)?$" )
192 {
193  setFormat( format );
194  setPrecision( precision );
195  setTrimTrailingZeroes( trimTrailingZeroes );
196 }
197 
198 
200 {
201  return
202  format() == other.format() &&
203  precision() == other.precision() &&
205 }
206 
208 {
209  return !( *this == other );
210 }
211 
213 {
214  // Limit the range of decimal places to a reasonable range
215  precision = qBound( MinPrecision, precision, MaxPrecision );
217  mNumberScale = 1.0;
218  mNumberSuffix = "";
219  while ( precision < 0 )
220  {
221  precision++;
222  mNumberScale /= 10.0;
223  mNumberSuffix.append( '0' );
224  }
225 }
226 
228 {
229  return labelForRange( range.lowerValue(), range.upperValue() );
230 }
231 
232 QString QgsRendererRangeV2LabelFormat::formatNumber( double value ) const
233 {
234  if ( mPrecision > 0 )
235  {
236  QString valueStr = QString::number( value, 'f', mPrecision );
237  if ( mTrimTrailingZeroes )
238  valueStr = valueStr.replace( mReTrailingZeroes, "" );
239  if ( mReNegativeZero.exactMatch( valueStr ) )
240  valueStr = valueStr.mid( 1 );
241  return valueStr;
242  }
243  else
244  {
245  QString valueStr = QString::number( value * mNumberScale, 'f', 0 );
246  if ( valueStr == "-0" )
247  valueStr = "0";
248  if ( valueStr != "0" )
249  valueStr = valueStr + mNumberSuffix;
250  return valueStr;
251  }
252 }
253 
254 QString QgsRendererRangeV2LabelFormat::labelForRange( double lower, double upper ) const
255 {
256  QString lowerStr = formatNumber( lower );
257  QString upperStr = formatNumber( upper );
258 
259  QString legend( mFormat );
260  return legend.replace( "%1", lowerStr ).replace( "%2", upperStr );
261 }
262 
264 {
265  mFormat = element.attribute( "format",
266  element.attribute( "prefix", " " ) + "%1" +
267  element.attribute( "separator", " - " ) + "%2" +
268  element.attribute( "suffix", " " )
269  );
270  setPrecision( element.attribute( "decimalplaces", "4" ).toInt() );
271  mTrimTrailingZeroes = element.attribute( "trimtrailingzeroes", "false" ) == "true";
272 }
273 
275 {
276  element.setAttribute( "format", mFormat );
277  element.setAttribute( "decimalplaces", mPrecision );
278  element.setAttribute( "trimtrailingzeroes", mTrimTrailingZeroes ? "true" : "false" );
279 }
280 
282 
284  : QgsFeatureRendererV2( "graduatedSymbol" )
285  , mAttrName( attrName )
286  , mRanges( ranges )
287  , mMode( Custom )
288  , mInvertedColorRamp( false )
289  , mScaleMethod( DEFAULT_SCALE_METHOD )
290  , mGraduatedMethod( GraduatedColor )
291  , mAttrNum( -1 )
292  , mCounting( false )
293 
294 {
295  // TODO: check ranges for sanity (NULL symbols, invalid ranges)
296 }
297 
299 {
300  mRanges.clear(); // should delete all the symbols
301 }
302 
304 {
305  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
306  {
307  if ( it->lowerValue() <= value && it->upperValue() >= value )
308  {
309  if ( it->renderState() || mCounting )
310  return it->symbol();
311  else
312  return NULL;
313  }
314  }
315  // the value is out of the range: return NULL instead of symbol
316  return NULL;
317 }
318 
320 {
321  QgsSymbolV2* symbol = originalSymbolForFeature( feature );
322  if ( symbol == NULL )
323  return NULL;
324 
325  if ( !mRotation.data() && !mSizeScale.data() )
326  return symbol; // no data-defined rotation/scaling - just return the symbol
327 
328  // find out rotation, size scale
329  const double rotation = mRotation.data() ? mRotation->evaluate( feature ).toDouble() : 0;
330  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( feature ).toDouble() : 1.;
331 
332  // take a temporary symbol (or create it if doesn't exist)
333  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
334 
335  // modify the temporary symbol and return it
336  if ( tempSymbol->type() == QgsSymbolV2::Marker )
337  {
338  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
339  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
340  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
341  markerSymbol->setScaleMethod( mScaleMethod );
342  }
343  else if ( tempSymbol->type() == QgsSymbolV2::Line )
344  {
345  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
346  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
347  }
348  return tempSymbol;
349 }
350 
352 {
353  QgsAttributes attrs = feature.attributes();
354  QVariant value;
355  if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
356  {
357  value = mExpression->evaluate( &feature );
358  }
359  else
360  {
361  value = attrs[mAttrNum];
362  }
363 
364  // Null values should not be categorized
365  if ( value.isNull() )
366  return NULL;
367 
368  // find the right category
369  return symbolForValue( value.toDouble() );
370 }
371 
373 {
374  mCounting = context.rendererScale() == 0.0;
375 
376  // find out classification attribute index from name
377  mAttrNum = fields.fieldNameIndex( mAttrName );
378 
379  if ( mAttrNum == -1 )
380  {
381  mExpression.reset( new QgsExpression( mAttrName ) );
382  mExpression->prepare( fields );
383  }
384 
385  QgsRangeList::iterator it = mRanges.begin();
386  for ( ; it != mRanges.end(); ++it )
387  {
388  if ( !it->symbol() )
389  continue;
390 
391  it->symbol()->startRender( context, &fields );
392 
393  if ( mRotation.data() || mSizeScale.data() )
394  {
395  QgsSymbolV2* tempSymbol = it->symbol()->clone();
396  tempSymbol->setRenderHints(( mRotation.data() ? QgsSymbolV2::DataDefinedRotation : 0 ) |
398  tempSymbol->startRender( context, &fields );
399  mTempSymbols[ it->symbol()] = tempSymbol;
400  }
401  }
402 }
403 
405 {
406  QgsRangeList::iterator it = mRanges.begin();
407  for ( ; it != mRanges.end(); ++it )
408  {
409  if ( !it->symbol() )
410  continue;
411 
412  it->symbol()->stopRender( context );
413  }
414 
415  // cleanup mTempSymbols
416  QHash<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
417  for ( ; it2 != mTempSymbols.end(); ++it2 )
418  {
419  it2.value()->stopRender( context );
420  delete it2.value();
421  }
422  mTempSymbols.clear();
423 }
424 
426 {
427  QSet<QString> attributes;
428 
429  // mAttrName can contain either attribute name or an expression.
430  // Sometimes it is not possible to distinguish between those two,
431  // e.g. "a - b" can be both a valid attribute name or expression.
432  // Since we do not have access to fields here, try both options.
433  attributes << mAttrName;
434 
435  QgsExpression testExpr( mAttrName );
436  if ( !testExpr.hasParserError() )
437  attributes.unite( testExpr.referencedColumns().toSet() );
438 
439  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
440  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
441 
442  QgsRangeList::const_iterator range_it = mRanges.constBegin();
443  for ( ; range_it != mRanges.constEnd(); ++range_it )
444  {
445  QgsSymbolV2* symbol = range_it->symbol();
446  if ( symbol )
447  {
448  attributes.unite( symbol->usedAttributes() );
449  }
450  }
451  return attributes.toList();
452 }
453 
455 {
456  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
457  return false;
458  mRanges[rangeIndex].setSymbol( symbol );
459  return true;
460 }
461 
462 bool QgsGraduatedSymbolRendererV2::updateRangeLabel( int rangeIndex, QString label )
463 {
464  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
465  return false;
466  mRanges[rangeIndex].setLabel( label );
467  return true;
468 }
469 
470 bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double value )
471 {
472  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
473  return false;
474  QgsRendererRangeV2 &range = mRanges[rangeIndex];
475  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
476  range.setUpperValue( value );
477  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
478  return true;
479 }
480 
481 bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double value )
482 {
483  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
484  return false;
485  QgsRendererRangeV2 &range = mRanges[rangeIndex];
486  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
487  range.setLowerValue( value );
488  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
489  return true;
490 }
491 
493 {
494  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
495  return false;
496  mRanges[rangeIndex].setRenderState( value );
497  return true;
498 }
499 
501 {
502  QString s = QString( "GRADUATED: attr %1\n" ).arg( mAttrName );
503  for ( int i = 0; i < mRanges.count(); i++ )
504  s += mRanges[i].dump();
505  return s;
506 }
507 
509 {
511  r->setMode( mMode );
512  if ( mSourceSymbol.data() )
513  r->setSourceSymbol( mSourceSymbol->clone() );
514  if ( mSourceColorRamp.data() )
515  {
516  r->setSourceColorRamp( mSourceColorRamp->clone() );
518  }
522  r->setScaleMethod( scaleMethod() );
523  r->setLabelFormat( labelFormat() );
525  copyPaintEffect( r );
526  return r;
527 }
528 
529 void QgsGraduatedSymbolRendererV2::toSld( QDomDocument& doc, QDomElement &element ) const
530 {
531  QgsStringMap props;
532  props[ "attribute" ] = mAttrName;
533  props[ "method" ] = graduatedMethodStr( mGraduatedMethod );
534  if ( mRotation.data() )
535  props[ "angle" ] = mRotation->expression();
536  if ( mSizeScale.data() )
537  props[ "scale" ] = mSizeScale->expression();
538 
539  // create a Rule for each range
540  for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
541  {
542  QgsStringMap catProps( props );
543  it->toSld( doc, element, catProps );
544  }
545 }
546 
548 {
549  QgsSymbolV2List lst;
550  for ( int i = 0; i < mRanges.count(); i++ )
551  lst.append( mRanges[i].symbol() );
552  return lst;
553 }
554 
555 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
556 {
557 
558  // Equal interval algorithm
559  //
560  // Returns breaks based on dividing the range ('minimum' to 'maximum')
561  // into 'classes' parts.
562 
563  double step = ( maximum - minimum ) / classes;
564 
565  QList<double> breaks;
566  double value = minimum;
567  for ( int i = 0; i < classes; i++ )
568  {
569  value += step;
570  breaks.append( value );
571  }
572 
573  // floating point arithmetics is not precise:
574  // set the last break to be exactly maximum so we do not miss it
575  breaks[classes-1] = maximum;
576 
577  return breaks;
578 }
579 
580 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
581 {
582  // q-th quantile of a data set:
583  // value where q fraction of data is below and (1-q) fraction is above this value
584  // Xq = (1 - r) * X_NI1 + r * X_NI2
585  // NI1 = (int) (q * (n+1))
586  // NI2 = NI1 + 1
587  // r = q * (n+1) - (int) (q * (n+1))
588  // (indices of X: 1...n)
589 
590  // sort the values first
591  qSort( values );
592 
593  QList<double> breaks;
594 
595  // If there are no values to process: bail out
596  if ( !values.count() )
597  return breaks;
598 
599  int n = values.count();
600  double Xq = n > 0 ? values[0] : 0.0;
601 
602  for ( int i = 1; i < classes; i++ )
603  {
604  if ( n > 1 )
605  {
606  double q = i / ( double ) classes;
607  double a = q * ( n - 1 );
608  int aa = ( int )( a );
609 
610  double r = a - aa;
611  Xq = ( 1 - r ) * values[aa] + r * values[aa+1];
612  }
613  breaks.append( Xq );
614  }
615 
616  breaks.append( values[ n-1 ] );
617 
618  return breaks;
619 }
620 
621 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<double> &labels )
622 {
623 
624  // C++ implementation of the standard deviation class interval algorithm
625  // as implemented in the 'classInt' package available for the R statistical
626  // prgramming language.
627 
628  // Returns breaks based on 'prettyBreaks' of the centred and scaled
629  // values of 'values', and may have a number of classes different from 'classes'.
630 
631  // If there are no values to process: bail out
632  if ( !values.count() )
633  return QList<double>();
634 
635  double mean = 0.0;
636  double stdDev = 0.0;
637  int n = values.count();
638  double minimum = values[0];
639  double maximum = values[0];
640 
641  for ( int i = 0; i < n; i++ )
642  {
643  mean += values[i];
644  minimum = qMin( values[i], minimum ); // could use precomputed max and min
645  maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
646  }
647  mean = mean / ( double ) n;
648 
649  double sd = 0.0;
650  for ( int i = 0; i < n; i++ )
651  {
652  sd = values[i] - mean;
653  stdDev += sd * sd;
654  }
655  stdDev = sqrt( stdDev / n );
656 
657  QList<double> breaks = QgsSymbolLayerV2Utils::prettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
658  for ( int i = 0; i < breaks.count(); i++ )
659  {
660  labels.append( breaks[i] );
661  breaks[i] = ( breaks[i] * stdDev ) + mean;
662  }
663 
664  return breaks;
665 } // _calcStdDevBreaks
666 
667 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
668  double minimum, double maximum,
669  int maximumSize = 1000 )
670 {
671  // Jenks Optimal (Natural Breaks) algorithm
672  // Based on the Jenks algorithm from the 'classInt' package available for
673  // the R statistical prgramming language, and from Python code from here:
674  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
675  // and is based on a JAVA and Fortran code available here:
676  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
677 
678  // Returns class breaks such that classes are internally homogeneous while
679  // assuring heterogeneity among classes.
680 
681  if ( !values.count() )
682  return QList<double>();
683 
684  if ( classes <= 1 )
685  {
686  return QList<double>() << maximum;
687  }
688 
689  if ( classes >= values.size() )
690  {
691  return values;
692  }
693 
694  QVector<double> sample;
695 
696  // if we have lots of values, we need to take a random sample
697  if ( values.size() > maximumSize )
698  {
699  // for now, sample at least maximumSize values or a 10% sample, whichever
700  // is larger. This will produce a more representative sample for very large
701  // layers, but could end up being computationally intensive...
702 
703  sample.resize( qMax( maximumSize, values.size() / 10 ) );
704 
705  QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
706  QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
707 
708  sample[ 0 ] = minimum;
709  sample[ 1 ] = maximum;;
710  for ( int i = 2; i < sample.size(); i++ )
711  {
712  // pick a random integer from 0 to n
713  double r = qrand();
714  int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
715  sample[ i ] = values[ j ];
716  }
717  }
718  else
719  {
720  sample = values.toVector();
721  }
722 
723  int n = sample.size();
724 
725  // sort the sample values
726  qSort( sample );
727 
728  QVector< QVector<int> > matrixOne( n + 1 );
729  QVector< QVector<double> > matrixTwo( n + 1 );
730 
731  for ( int i = 0; i <= n; i++ )
732  {
733  matrixOne[i].resize( classes + 1 );
734  matrixTwo[i].resize( classes + 1 );
735  }
736 
737  for ( int i = 1; i <= classes; i++ )
738  {
739  matrixOne[0][i] = 1;
740  matrixOne[1][i] = 1;
741  matrixTwo[0][i] = 0.0;
742  for ( int j = 2; j <= n; j++ )
743  {
744  matrixTwo[j][i] = std::numeric_limits<double>::max();
745  }
746  }
747 
748  for ( int l = 2; l <= n; l++ )
749  {
750  double s1 = 0.0;
751  double s2 = 0.0;
752  int w = 0;
753 
754  double v = 0.0;
755 
756  for ( int m = 1; m <= l; m++ )
757  {
758  int i3 = l - m + 1;
759 
760  double val = sample[ i3 - 1 ];
761 
762  s2 += val * val;
763  s1 += val;
764  w++;
765 
766  v = s2 - ( s1 * s1 ) / ( double ) w;
767  int i4 = i3 - 1;
768  if ( i4 != 0 )
769  {
770  for ( int j = 2; j <= classes; j++ )
771  {
772  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
773  {
774  matrixOne[l][j] = i4;
775  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
776  }
777  }
778  }
779  }
780  matrixOne[l][1] = 1;
781  matrixTwo[l][1] = v;
782  }
783 
784  QVector<double> breaks( classes );
785  breaks[classes-1] = sample[n-1];
786 
787  for ( int j = classes, k = n; j >= 2; j-- )
788  {
789  int id = matrixOne[k][j] - 1;
790  breaks[j - 2] = sample[id];
791  k = matrixOne[k][j] - 1;
792  }
793 
794  return breaks.toList();
795 } //_calcJenksBreaks
796 
797 
799  QgsVectorLayer* vlayer,
800  QString attrName,
801  int classes,
802  Mode mode,
803  QgsSymbolV2* symbol,
804  QgsVectorColorRampV2* ramp,
805  bool inverted,
807 )
808 {
810  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
811  r->setSourceSymbol( symbol->clone() );
812  r->setSourceColorRamp( ramp->clone() );
813  r->setInvertedColorRamp( inverted );
814  r->setMode( mode );
815  r->setLabelFormat( labelFormat );
816  r->updateClasses( vlayer, mode, classes );
817  return r;
818 }
819 
821 {
822  bool ok;
823  return vlayer->getDoubleValues( mAttrName, ok );
824 }
825 
827 {
828  if ( mAttrName.isEmpty() )
829  return;
830 
831  setMode( mode );
832  // Custom classes are not recalculated
833  if ( mode == Custom )
834  return;
835 
836  if ( nclasses < 1 )
837  nclasses = 1;
838 
839  QList<double> values;
840  bool valuesLoaded = false;
841  double minimum;
842  double maximum;
843 
844  int attrNum = vlayer->fieldNameIndex( mAttrName );
845 
846  bool ok;
847  if ( attrNum == -1 )
848  {
849  values = vlayer->getDoubleValues( mAttrName, ok );
850  if ( !ok || values.isEmpty() )
851  return;
852 
853  qSort( values ); // vmora: is wondering if O( n log(n) ) is really necessary here, min and max are O( n )
854  minimum = values.first();
855  maximum = values.last();
856  valuesLoaded = true;
857  }
858  else
859  {
860  minimum = vlayer->minimumValue( attrNum ).toDouble();
861  maximum = vlayer->maximumValue( attrNum ).toDouble();
862  }
863 
864  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
865  QList<double> breaks;
866  QList<double> labels;
867  if ( mode == EqualInterval )
868  {
869  breaks = _calcEqualIntervalBreaks( minimum, maximum, nclasses );
870  }
871  else if ( mode == Pretty )
872  {
873  breaks = QgsSymbolLayerV2Utils::prettyBreaks( minimum, maximum, nclasses );
874  }
875  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
876  {
877  // get values from layer
878  if ( !valuesLoaded )
879  {
880  values = vlayer->getDoubleValues( mAttrName, ok );
881  }
882 
883  // calculate the breaks
884  if ( mode == Quantile )
885  {
886  breaks = _calcQuantileBreaks( values, nclasses );
887  }
888  else if ( mode == Jenks )
889  {
890  breaks = _calcJenksBreaks( values, nclasses, minimum, maximum );
891  }
892  else if ( mode == StdDev )
893  {
894  breaks = _calcStdDevBreaks( values, nclasses, labels );
895  }
896  }
897  else
898  {
899  Q_ASSERT( false );
900  }
901 
902  double lower, upper = minimum;
903  QString label;
905 
906  // "breaks" list contains all values at class breaks plus maximum as last break
907 
908  int i = 0;
909  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
910  {
911  lower = upper; // upper border from last interval
912  upper = *it;
913 
914  // Label - either StdDev label or default label for a range
915  if ( mode == StdDev )
916  {
917  if ( i == 0 )
918  {
919  label = "< " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
920  }
921  else if ( i == labels.count() - 1 )
922  {
923  label = ">= " + QString::number( labels[i-1], 'f', 2 ) + " Std Dev";
924  }
925  else
926  {
927  label = QString::number( labels[i-1], 'f', 2 ) + " Std Dev" + " - " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
928  }
929  }
930  else
931  {
932  label = mLabelFormat.labelForRange( lower, upper );
933  }
934  QgsSymbolV2* newSymbol = mSourceSymbol ? mSourceSymbol->clone() : QgsSymbolV2::defaultSymbol( vlayer->geometryType() );
935  addClass( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
936  }
938 }
939 
940 
942 {
943  QDomElement symbolsElem = element.firstChildElement( "symbols" );
944  if ( symbolsElem.isNull() )
945  return NULL;
946 
947  QDomElement rangesElem = element.firstChildElement( "ranges" );
948  if ( rangesElem.isNull() )
949  return NULL;
950 
951  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
953 
954  QDomElement rangeElem = rangesElem.firstChildElement();
955  while ( !rangeElem.isNull() )
956  {
957  if ( rangeElem.tagName() == "range" )
958  {
959  double lowerValue = rangeElem.attribute( "lower" ).toDouble();
960  double upperValue = rangeElem.attribute( "upper" ).toDouble();
961  QString symbolName = rangeElem.attribute( "symbol" );
962  QString label = rangeElem.attribute( "label" );
963  bool render = rangeElem.attribute( "render", "true" ) != "false";
964  if ( symbolMap.contains( symbolName ) )
965  {
966  QgsSymbolV2* symbol = symbolMap.take( symbolName );
967  ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label, render ) );
968  }
969  }
970  rangeElem = rangeElem.nextSiblingElement();
971  }
972 
973  QString attrName = element.attribute( "attr" );
974 
975  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
976 
977  QString attrMethod = element.attribute( "graduatedMethod" );
978  if ( attrMethod.length() )
979  {
980  if ( attrMethod == graduatedMethodStr( GraduatedColor ) )
982  else if ( attrMethod == graduatedMethodStr( GraduatedSize ) )
984  }
985 
986 
987  // delete symbols if there are any more
989 
990  // try to load source symbol (optional)
991  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
992  if ( !sourceSymbolElem.isNull() )
993  {
994  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
995  if ( sourceSymbolMap.contains( "0" ) )
996  {
997  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
998  }
999  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
1000  }
1001 
1002  // try to load color ramp (optional)
1003  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
1004  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
1005  {
1006  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
1007  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
1008  if ( !invertedColorRampElem.isNull() )
1009  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
1010  }
1011 
1012  // try to load mode
1013  QDomElement modeElem = element.firstChildElement( "mode" );
1014  if ( !modeElem.isNull() )
1015  {
1016  QString modeString = modeElem.attribute( "name" );
1017  if ( modeString == "equal" )
1018  r->setMode( EqualInterval );
1019  else if ( modeString == "quantile" )
1020  r->setMode( Quantile );
1021  else if ( modeString == "jenks" )
1022  r->setMode( Jenks );
1023  else if ( modeString == "stddev" )
1024  r->setMode( StdDev );
1025  else if ( modeString == "pretty" )
1026  r->setMode( Pretty );
1027  }
1028 
1029  QDomElement rotationElem = element.firstChildElement( "rotation" );
1030  if ( !rotationElem.isNull() )
1031  r->setRotationField( rotationElem.attribute( "field" ) );
1032 
1033  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
1034  if ( !sizeScaleElem.isNull() )
1035  r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
1036  r->setScaleMethod( QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ) );
1037 
1038  QDomElement labelFormatElem = element.firstChildElement( "labelformat" );
1039  if ( ! labelFormatElem.isNull() )
1040  {
1042  labelFormat.setFromDomElement( labelFormatElem );
1043  r->setLabelFormat( labelFormat );
1044  }
1045  // TODO: symbol levels
1046  return r;
1047 }
1048 
1049 QDomElement QgsGraduatedSymbolRendererV2::save( QDomDocument& doc )
1050 {
1051  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1052  rendererElem.setAttribute( "type", "graduatedSymbol" );
1053  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
1054  rendererElem.setAttribute( "attr", mAttrName );
1055  rendererElem.setAttribute( "graduatedMethod", graduatedMethodStr( mGraduatedMethod ) );
1056 
1057  // ranges
1058  int i = 0;
1060  QDomElement rangesElem = doc.createElement( "ranges" );
1061  QgsRangeList::const_iterator it = mRanges.constBegin();
1062  for ( ; it != mRanges.constEnd(); ++it )
1063  {
1064  const QgsRendererRangeV2& range = *it;
1065  QString symbolName = QString::number( i );
1066  symbols.insert( symbolName, range.symbol() );
1067 
1068  QDomElement rangeElem = doc.createElement( "range" );
1069  rangeElem.setAttribute( "lower", QString::number( range.lowerValue(), 'f' ) );
1070  rangeElem.setAttribute( "upper", QString::number( range.upperValue(), 'f' ) );
1071  rangeElem.setAttribute( "symbol", symbolName );
1072  rangeElem.setAttribute( "label", range.label() );
1073  rangeElem.setAttribute( "render", range.renderState() ? "true" : "false" );
1074  rangesElem.appendChild( rangeElem );
1075  i++;
1076  }
1077 
1078  rendererElem.appendChild( rangesElem );
1079 
1080  // save symbols
1081  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
1082  rendererElem.appendChild( symbolsElem );
1083 
1084  // save source symbol
1085  if ( mSourceSymbol.data() )
1086  {
1087  QgsSymbolV2Map sourceSymbols;
1088  sourceSymbols.insert( "0", mSourceSymbol.data() );
1089  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
1090  rendererElem.appendChild( sourceSymbolElem );
1091  }
1092 
1093  // save source color ramp
1094  if ( mSourceColorRamp.data() )
1095  {
1096  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
1097  rendererElem.appendChild( colorRampElem );
1098  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
1099  invertedElem.setAttribute( "value", mInvertedColorRamp );
1100  rendererElem.appendChild( invertedElem );
1101  }
1102 
1103  // save mode
1104  QString modeString;
1105  if ( mMode == EqualInterval )
1106  modeString = "equal";
1107  else if ( mMode == Quantile )
1108  modeString = "quantile";
1109  else if ( mMode == Jenks )
1110  modeString = "jenks";
1111  else if ( mMode == StdDev )
1112  modeString = "stddev";
1113  else if ( mMode == Pretty )
1114  modeString = "pretty";
1115  if ( !modeString.isEmpty() )
1116  {
1117  QDomElement modeElem = doc.createElement( "mode" );
1118  modeElem.setAttribute( "name", modeString );
1119  rendererElem.appendChild( modeElem );
1120  }
1121 
1122  QDomElement rotationElem = doc.createElement( "rotation" );
1123  if ( mRotation.data() )
1124  rotationElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) );
1125  rendererElem.appendChild( rotationElem );
1126 
1127  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
1128  if ( mSizeScale.data() )
1129  sizeScaleElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mSizeScale.data() ) );
1130  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
1131  rendererElem.appendChild( sizeScaleElem );
1132 
1133  QDomElement labelFormatElem = doc.createElement( "labelformat" );
1134  mLabelFormat.saveToDomElement( labelFormatElem );
1135  rendererElem.appendChild( labelFormatElem );
1136 
1137  if ( mPaintEffect )
1138  mPaintEffect->saveProperties( doc, rendererElem );
1139 
1140  return rendererElem;
1141 }
1142 
1144 {
1146  int count = ranges().count();
1147  for ( int i = 0; i < count; i++ )
1148  {
1149  const QgsRendererRangeV2& range = ranges()[i];
1150  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
1151  lst << qMakePair( range.label(), pix );
1152  }
1153  return lst;
1154 }
1155 
1157 {
1158  QgsLegendSymbolListV2 list;
1159  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
1160  {
1161  // check that all symbols that have the same size expression
1162  QgsDataDefined ddSize;
1163  foreach ( QgsRendererRangeV2 range, mRanges )
1164  {
1165  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( range.symbol() );
1166  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
1167  {
1168  // no common size expression
1170  }
1171  else
1172  {
1173  ddSize = symbol->dataDefinedSize();
1174  }
1175  }
1176 
1177  if ( !ddSize.isActive() || !ddSize.useExpression() )
1178  {
1180  }
1181 
1182  QgsScaleExpression exp( ddSize.expressionString() );
1183  if ( exp.type() != QgsScaleExpression::Unknown )
1184  {
1185  QgsLegendSymbolItemV2 title( NULL, exp.baseExpression(), "" );
1186  list << title;
1187  foreach ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
1188  {
1189  QgsLegendSymbolItemV2 si( mSourceSymbol.data(), QString::number( v ), "" );
1190  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
1191  s->setColor( QColor( 0, 0, 0 ) );
1192  s->setDataDefinedSize( QgsDataDefined() );
1193  s->setSize( exp.size( v ) );
1194  list << si;
1195  }
1196  // now list the graduated symbols
1198  foreach ( QgsLegendSymbolItemV2 item, list2 )
1199  list << item;
1200  return list;
1201  }
1202  }
1203 
1205 }
1206 
1208 {
1209  Q_UNUSED( scaleDenominator );
1210  QgsLegendSymbolList lst;
1211 
1212  foreach ( const QgsRendererRangeV2& range, mRanges )
1213  {
1214  if ( rule.isEmpty() || range.label() == rule )
1215  {
1216  lst << qMakePair( range.label(), range.symbol() );
1217  }
1218  }
1219  return lst;
1220 }
1221 
1223 {
1224  return mSourceSymbol.data();
1225 }
1227 {
1228  mSourceSymbol.reset( sym );
1229 }
1230 
1232 {
1233  return mSourceColorRamp.data();
1234 }
1235 
1237 {
1238  mSourceColorRamp.reset( ramp );
1239 }
1240 
1242 {
1243  double min = DBL_MAX;
1244  for ( int i = 0; i < mRanges.count(); i++ )
1245  {
1246  double sz = 0;
1247  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1248  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1249  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1250  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1251  min = qMin( sz, min );
1252  }
1253  return min;
1254 }
1255 
1257 {
1258  double max = DBL_MIN;
1259  for ( int i = 0; i < mRanges.count(); i++ )
1260  {
1261  double sz = 0;
1262  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1263  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1264  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1265  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1266  max = qMax( sz, max );
1267  }
1268  return max;
1269 }
1270 
1271 void QgsGraduatedSymbolRendererV2::setSymbolSizes( double minSize, double maxSize )
1272 {
1273  for ( int i = 0; i < mRanges.count(); i++ )
1274  {
1275  QScopedPointer<QgsSymbolV2> symbol( mRanges[i].symbol() ? mRanges[i].symbol()->clone() : 0 );
1276  const double size = mRanges.count() > 1
1277  ? minSize + i * ( maxSize - minSize ) / ( mRanges.count() - 1 )
1278  : .5 * ( maxSize + minSize );
1279  if ( symbol->type() == QgsSymbolV2::Marker )
1280  static_cast< QgsMarkerSymbolV2 * >( symbol.data() )->setSize( size );
1281  if ( symbol->type() == QgsSymbolV2::Line )
1282  static_cast< QgsLineSymbolV2 * >( symbol.data() )->setWidth( size );
1283  updateRangeSymbol( i, symbol.take() );
1284  }
1285 }
1286 
1288 {
1289  int i = 0;
1290  if ( ramp )
1291  {
1292  setSourceColorRamp( ramp );
1293  setInvertedColorRamp( inverted );
1294  }
1295 
1296  if ( mSourceColorRamp )
1297  {
1298  foreach ( QgsRendererRangeV2 range, mRanges )
1299  {
1300  QgsSymbolV2 *symbol = range.symbol() ? range.symbol()->clone() : 0;
1301  if ( symbol )
1302  {
1303  double colorValue;
1304  if ( inverted )
1305  colorValue = ( mRanges.count() > 1 ? ( double )( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
1306  else
1307  colorValue = ( mRanges.count() > 1 ? ( double ) i / ( mRanges.count() - 1 ) : 0 );
1308  symbol->setColor( mSourceColorRamp->color( colorValue ) );
1309  }
1310  updateRangeSymbol( i, symbol );
1311  ++i;
1312  }
1313  }
1314 
1315 }
1316 
1318 {
1319  if ( !sym )
1320  return;
1321 
1322  int i = 0;
1323  foreach ( QgsRendererRangeV2 range, mRanges )
1324  {
1325  QScopedPointer<QgsSymbolV2> symbol( sym->clone() );
1327  {
1328  symbol->setColor( range.symbol()->color() );
1329  }
1330  else if ( mGraduatedMethod == GraduatedSize )
1331  {
1332  if ( symbol->type() == QgsSymbolV2::Marker )
1333  static_cast<QgsMarkerSymbolV2 *>( symbol.data() )->setSize(
1334  static_cast<QgsMarkerSymbolV2 *>( range.symbol() )->size() );
1335  else if ( symbol->type() == QgsSymbolV2::Line )
1336  static_cast<QgsLineSymbolV2 *>( symbol.data() )->setWidth(
1337  static_cast<QgsLineSymbolV2 *>( range.symbol() )->width() );
1338  }
1339  updateRangeSymbol( i, symbol.take() );
1340  ++i;
1341  }
1342  setSourceSymbol( sym->clone() );
1343 }
1344 
1345 void QgsGraduatedSymbolRendererV2::setRotationField( QString fieldOrExpression )
1346 {
1348 }
1349 
1351 {
1352  return mRotation.data() ? QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) : QString();
1353 }
1354 
1355 void QgsGraduatedSymbolRendererV2::setSizeScaleField( QString fieldOrExpression )
1356 {
1358 }
1359 
1361 {
1363 }
1364 
1366 {
1368  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1369  {
1370  if ( it->symbol() )
1371  setScaleMethodToSymbol( it->symbol(), scaleMethod );
1372  }
1373 }
1374 
1376 {
1377  return true;
1378 }
1379 
1381 {
1382  bool ok;
1383  int index = key.toInt( &ok );
1384  if ( ok && index >= 0 && index < mRanges.size() )
1385  return mRanges[ index ].renderState();
1386  else
1387  return true;
1388 }
1389 
1391 {
1392  bool ok;
1393  int index = key.toInt( &ok );
1394  if ( ok )
1395  updateRangeRenderState( index, state );
1396 }
1397 
1398 
1400 {
1401  QgsSymbolV2* newSymbol = symbol->clone();
1402  QString label = "0.0 - 0.0";
1403  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1404 }
1405 
1406 void QgsGraduatedSymbolRendererV2::addClass( double lower, double upper )
1407 {
1408  QgsSymbolV2* newSymbol = mSourceSymbol->clone();
1409  QString label = mLabelFormat.labelForRange( lower, upper );
1410  mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
1411 }
1412 
1413 void QgsGraduatedSymbolRendererV2::addBreak( double breakValue, bool updateSymbols )
1414 {
1415  QMutableListIterator< QgsRendererRangeV2 > it( mRanges );
1416  while ( it.hasNext() )
1417  {
1418  QgsRendererRangeV2 range = it.next();
1419  if ( range.lowerValue() < breakValue && range.upperValue() > breakValue )
1420  {
1422  newRange.setLowerValue( breakValue );
1423  newRange.setUpperValue( range.upperValue() );
1424  newRange.setLabel( mLabelFormat.labelForRange( newRange ) );
1425  newRange.setSymbol( mSourceSymbol->clone() );
1426 
1427  //update old range
1428  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
1429  range.setUpperValue( breakValue );
1430  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range.lowerValue(), breakValue ) );
1431  it.setValue( range );
1432 
1433  it.insert( newRange );
1434  break;
1435  }
1436  }
1437 
1438  if ( updateSymbols && mGraduatedMethod == GraduatedColor )
1439  {
1441  }
1442 }
1443 
1445 {
1446  mRanges.append( range );
1447 }
1448 
1450 {
1451  mRanges.removeAt( idx );
1452 }
1453 
1455 {
1456  mRanges.clear();
1457 }
1458 
1460 {
1461  if ( updateRanges && labelFormat != mLabelFormat )
1462  {
1463  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1464  {
1465  it->setLabel( labelFormat.labelForRange( *it ) );
1466  }
1467  }
1469 }
1470 
1471 
1473 {
1474  // Find the minimum size of a class
1475  double minClassRange = 0.0;
1476  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1477  {
1478  double range = it->upperValue() - it->lowerValue();
1479  if ( range <= 0.0 )
1480  continue;
1481  if ( minClassRange == 0.0 || range < minClassRange )
1482  minClassRange = range;
1483  }
1484  if ( minClassRange <= 0.0 )
1485  return;
1486 
1487  // Now set the number of decimal places to ensure no more than 20% error in
1488  // representing this range (up to 10% at upper and lower end)
1489 
1490  int ndp = 10;
1491  double nextDpMinRange = 0.0000000099;
1492  while ( ndp > 0 && nextDpMinRange < minClassRange )
1493  {
1494  ndp--;
1495  nextDpMinRange *= 10.0;
1496  }
1497  mLabelFormat.setPrecision( ndp );
1498  if ( updateRanges ) setLabelFormat( mLabelFormat, true );
1499 }
1500 
1502 {
1503  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1504  return;
1505  mRanges.move( from, to );
1506 }
1507 
1509 {
1510  return r1 < r2;
1511 }
1512 
1514 {
1515  return !valueLessThan( r1, r2 );
1516 }
1517 
1519 {
1520  QgsDebugMsg( "Entered" );
1521  if ( order == Qt::AscendingOrder )
1522  {
1523  qSort( mRanges.begin(), mRanges.end(), valueLessThan );
1524  }
1525  else
1526  {
1527  qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1528  }
1529 }
1530 
1532 {
1533  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1534 }
1535 
1537 {
1538  return !labelLessThan( r1, r2 );
1539 }
1540 
1542 {
1543  if ( order == Qt::AscendingOrder )
1544  {
1545  qSort( mRanges.begin(), mRanges.end(), labelLessThan );
1546  }
1547  else
1548  {
1549  qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1550  }
1551 }
1552 
1554 {
1555  if ( renderer->type() == "graduatedSymbol" )
1556  {
1557  return dynamic_cast<QgsGraduatedSymbolRendererV2*>( renderer->clone() );
1558  }
1559  if ( renderer->type() == "pointDisplacement" )
1560  {
1561  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1562  if ( pointDisplacementRenderer )
1563  return convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1564  }
1565  if ( renderer->type() == "invertedPolygonRenderer" )
1566  {
1567  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1568  if ( invertedPolygonRenderer )
1569  return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1570  }
1571 
1572  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1573  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1574 
1576  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols();
1577  if ( symbols.size() > 0 )
1578  {
1579  r->setSourceSymbol( symbols.at( 0 )->clone() );
1580  }
1581 
1582  return r;
1583 }
1584 
1586 {
1587  switch ( method )
1588  {
1589  case GraduatedColor: return "GraduatedColor";
1590  case GraduatedSize: return "GraduatedSize";
1591  }
1592  return "";
1593 }
1594 
1595 
QMap< QString, QgsSymbolV2 * > QgsSymbolV2Map
Definition: qgsrendererv2.h:40
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:86
QList< QgsRendererRangeV2 > QgsRangeList
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 setLowerValue(double lowerValue)
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.
Definition: qgsexpression.h:93
QStringList referencedColumns() const
Get list of columns referenced by the expression.
void setRotationField(QString fieldOrExpression) override
sets rotation field of renderer (if supported by the renderer)
void addBreak(double breakValue, bool updateSymbols=true)
Add a breakpoint by splitting existing classes so that the specified value becomes a break between tw...
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)
static QgsGraduatedSymbolRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsGraduatedSymbolRendererV2 from an existing renderer.
static QgsVectorColorRampV2 * loadColorRamp(QDomElement &element)
void updateClasses(QgsVectorLayer *vlayer, Mode mode, int nclasses)
Recalculate classes for a layer.
bool updateRangeLabel(int rangeIndex, QString label)
QList< QgsSymbolV2 * > QgsSymbolV2List
Definition: qgsrendererv2.h:39
static QList< double > _calcStdDevBreaks(QList< double > values, int classes, QList< double > &labels)
virtual QString dump() const override
for debugging
SymbolType type() const
Definition: qgssymbolv2.h:86
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
void updateSymbols(QgsSymbolV2 *sym)
Update all the symbols but leave breaks and colors.
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
QString formatNumber(double value) const
int fieldNameIndex(const QString &fieldName) const
Look up field's index from name - case insensitive TODO: sort out case sensitive (indexFromName()) vs...
Definition: qgsfield.cpp:350
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).
bool updateRangeRenderState(int rangeIndex, bool render)
QString labelForRange(double lower, double upper) const
void setSizeScaleField(QString fieldOrExpression)
double minSymbolSize() const
return the min symbol size when graduated by size
Container of fields for a vector layer.
Definition: qgsfield.h:173
virtual QgsSymbolV2 * originalSymbolForFeature(QgsFeature &feature) override
Return symbol for feature.
QList< double > getDoubleValues(const QString &fieldOrExpression, bool &ok, bool selectedOnly=false)
Fetches all double values from a specified field name or expression.
QString expressionString() const
QHash< QgsSymbolV2 *, QgsSymbolV2 * > mTempSymbols
temporary symbols, used for data-defined rotation and scaling
void calculateLabelPrecision(bool updateRanges=true)
Reset the label decimal places to a numberbased on the minimum class interval.
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, QString rule=QString()) override
return a list of item text / symbol
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:119
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
virtual QgsSymbolV2 * symbolForFeature(QgsFeature &feature) override
to be overridden
QVariant minimumValue(int index)
Returns minimum value for an attribute column or invalid variant in case of error.
QMap< QString, QString > QgsStringMap
Definition: qgis.h:438
virtual bool legendSymbolItemChecked(QString key) override
item in symbology was checked
static const char * graduatedMethodStr(GraduatedMethod method)
QgsPaintEffect * mPaintEffect
void setWidth(double width)
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
virtual void stopRender(QgsRenderContext &context) override
virtual QgsSymbolV2List symbols() override
for symbol levels
QScopedPointer< QgsExpression > mExpression
void setColor(const QColor &color)
virtual QgsFeatureRendererV2 * clone() const =0
void setGraduatedMethod(GraduatedMethod method)
set the method used for graduation (either size or color)
static QDomElement saveColorRamp(QString name, QgsVectorColorRampV2 *ramp, QDomDocument &doc)
QgsSymbolV2::ScaleMethod scaleMethod() const
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
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:90
#define DEFAULT_SCALE_METHOD
virtual QgsVectorColorRampV2 * clone() const =0
void swap(QgsRendererRangeV2 &other)
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, QString function)
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, QString tagName, QDomDocument &doc)
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.
QgsGraduatedSymbolRendererV2(QString attrName=QString(), QgsRangeList ranges=QgsRangeList())
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)
bool labelGreaterThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
QList< QPair< QString, QPixmap > > QgsLegendSymbologyList
QgsFeatureRendererV2 * embeddedRenderer() const
bool useExpression() const
bool valueLessThan(const QgsRendererRangeV2 &r1, const QgsRendererRangeV2 &r2)
int min(int a, int b)
Definition: util.h:93
static QgsGraduatedSymbolRendererV2 * createRenderer(QgsVectorLayer *vlayer, QString attrName, int classes, Mode mode, QgsSymbolV2 *symbol, QgsVectorColorRampV2 *ramp, bool inverted=false, QgsRendererRangeV2LabelFormat legendFormat=QgsRendererRangeV2LabelFormat())
const QgsRendererRangeV2LabelFormat & labelFormat() const
Return the label format used to generate default classification labels.
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
A renderer that automatically displaces points with the same position.
virtual bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
void setUsingSymbolLevels(bool usingSymbolLevels)
static QList< double > _calcEqualIntervalBreaks(double minimum, double maximum, int classes)
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)
void copyPaintEffect(QgsFeatureRendererV2 *destRenderer) const
Copies paint effect of this renderer to another renderer.
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.
QVector< QVariant > QgsAttributes
Definition: qgsfeature.h:106
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)
static QString fieldOrExpressionFromExpression(QgsExpression *expression)
Return a field name if the whole expression is just a name of the field .
bool usingSymbolLevels() const
void setScaleMethodToSymbol(QgsSymbolV2 *symbol, int scaleMethod)
bool updateRangeSymbol(int rangeIndex, QgsSymbolV2 *symbol)
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
QScopedPointer< QgsSymbolV2 > mSourceSymbol
void setRenderHints(int hints)
Definition: qgssymbolv2.h:162
static void clearSymbolMap(QgsSymbolV2Map &symbols)
void setTrimTrailingZeroes(bool trimTrailingZeroes)
bool updateRangeUpperValue(int rangeIndex, double value)
static QgsSymbolV2::ScaleMethod decodeScaleMethod(QString str)
QScopedPointer< QgsExpression > mRotation
static QgsFeatureRendererV2 * create(QDomElement &element)
create renderer from XML element
QList< QgsLegendSymbolItemV2 > QgsLegendSymbolListV2
virtual QgsLegendSymbologyList legendSymbologyItems(QSize iconSize) override
return a list of symbology items for the legend
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
QScopedPointer< QgsExpression > mSizeScale
QgsRendererRangeV2 & operator=(QgsRendererRangeV2 range)
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=0)
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.
double size
Definition: qgssvgcache.cpp:77
QList< QPair< QString, QgsSymbolV2 * > > QgsLegendSymbolList
Definition: qgsrendererv2.h:43
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
int max(int a, int b)
Definition: util.h:87
bool isActive() const
virtual void checkLegendSymbolItem(QString key, bool state=true) override
item in symbology was checked
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)
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
QColor color() const