QGIS API Documentation  2.10.1-Pisa
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
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 
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 
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 
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 
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  {
382  mExpression->prepare( fields );
383  }
384 
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();
398  tempSymbol->startRender( context, &fields );
399  mTempSymbols[ it->symbol()] = tempSymbol;
400  }
401  }
402 }
403 
405 {
407  for ( ; it != mRanges.end(); ++it )
408  {
409  if ( !it->symbol() )
410  continue;
411 
412  it->symbol()->stopRender( context );
413  }
414 
415  // cleanup mTempSymbols
417  for ( ; it2 != mTempSymbols.end(); ++it2 )
418  {
419  it2.value()->stopRender( context );
420  delete it2.value();
421  }
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 
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 
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 
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 
941 {
942  QDomElement symbolsElem = element.firstChildElement( "symbols" );
943  if ( symbolsElem.isNull() )
944  return NULL;
945 
946  QDomElement rangesElem = element.firstChildElement( "ranges" );
947  if ( rangesElem.isNull() )
948  return NULL;
949 
950  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
952 
953  QDomElement rangeElem = rangesElem.firstChildElement();
954  while ( !rangeElem.isNull() )
955  {
956  if ( rangeElem.tagName() == "range" )
957  {
958  double lowerValue = rangeElem.attribute( "lower" ).toDouble();
959  double upperValue = rangeElem.attribute( "upper" ).toDouble();
960  QString symbolName = rangeElem.attribute( "symbol" );
961  QString label = rangeElem.attribute( "label" );
962  bool render = rangeElem.attribute( "render", "true" ) != "false";
963  if ( symbolMap.contains( symbolName ) )
964  {
965  QgsSymbolV2* symbol = symbolMap.take( symbolName );
966  ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label, render ) );
967  }
968  }
969  rangeElem = rangeElem.nextSiblingElement();
970  }
971 
972  QString attrName = element.attribute( "attr" );
973 
974  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
975 
976  QString attrMethod = element.attribute( "graduatedMethod" );
977  if ( attrMethod.length() )
978  {
979  if ( attrMethod == graduatedMethodStr( GraduatedColor ) )
981  else if ( attrMethod == graduatedMethodStr( GraduatedSize ) )
983  }
984 
985 
986  // delete symbols if there are any more
988 
989  // try to load source symbol (optional)
990  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
991  if ( !sourceSymbolElem.isNull() )
992  {
993  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
994  if ( sourceSymbolMap.contains( "0" ) )
995  {
996  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
997  }
998  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
999  }
1000 
1001  // try to load color ramp (optional)
1002  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
1003  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
1004  {
1005  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
1006  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
1007  if ( !invertedColorRampElem.isNull() )
1008  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
1009  }
1010 
1011  // try to load mode
1012  QDomElement modeElem = element.firstChildElement( "mode" );
1013  if ( !modeElem.isNull() )
1014  {
1015  QString modeString = modeElem.attribute( "name" );
1016  if ( modeString == "equal" )
1017  r->setMode( EqualInterval );
1018  else if ( modeString == "quantile" )
1019  r->setMode( Quantile );
1020  else if ( modeString == "jenks" )
1021  r->setMode( Jenks );
1022  else if ( modeString == "stddev" )
1023  r->setMode( StdDev );
1024  else if ( modeString == "pretty" )
1025  r->setMode( Pretty );
1026  }
1027 
1028  QDomElement rotationElem = element.firstChildElement( "rotation" );
1029  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
1030  {
1031  for ( QgsRangeList::iterator it = r->mRanges.begin(); it != r->mRanges.end(); ++it )
1032  {
1033  convertSymbolRotation( it->symbol(), rotationElem.attribute( "field" ) );
1034  }
1035  if ( r->mSourceSymbol.data() )
1036  {
1037  convertSymbolRotation( r->mSourceSymbol.data(), rotationElem.attribute( "field" ) );
1038  }
1039  }
1040 
1041  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
1042  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
1043  {
1044  for ( QgsRangeList::iterator it = r->mRanges.begin(); it != r->mRanges.end(); ++it )
1045  {
1046  convertSymbolSizeScale( it->symbol(),
1047  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
1048  sizeScaleElem.attribute( "field" ) );
1049  }
1050  if ( r->mSourceSymbol.data() && r->mSourceSymbol->type() == QgsSymbolV2::Marker )
1051  {
1053  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
1054  sizeScaleElem.attribute( "field" ) );
1055  }
1056  }
1057 
1058  QDomElement labelFormatElem = element.firstChildElement( "labelformat" );
1059  if ( ! labelFormatElem.isNull() )
1060  {
1062  labelFormat.setFromDomElement( labelFormatElem );
1063  r->setLabelFormat( labelFormat );
1064  }
1065  // TODO: symbol levels
1066  return r;
1067 }
1068 
1070 {
1071  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1072  rendererElem.setAttribute( "type", "graduatedSymbol" );
1073  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
1074  rendererElem.setAttribute( "attr", mAttrName );
1075  rendererElem.setAttribute( "graduatedMethod", graduatedMethodStr( mGraduatedMethod ) );
1076 
1077  // ranges
1078  int i = 0;
1080  QDomElement rangesElem = doc.createElement( "ranges" );
1082  for ( ; it != mRanges.constEnd(); ++it )
1083  {
1084  const QgsRendererRangeV2& range = *it;
1085  QString symbolName = QString::number( i );
1086  symbols.insert( symbolName, range.symbol() );
1087 
1088  QDomElement rangeElem = doc.createElement( "range" );
1089  rangeElem.setAttribute( "lower", QString::number( range.lowerValue(), 'f' ) );
1090  rangeElem.setAttribute( "upper", QString::number( range.upperValue(), 'f' ) );
1091  rangeElem.setAttribute( "symbol", symbolName );
1092  rangeElem.setAttribute( "label", range.label() );
1093  rangeElem.setAttribute( "render", range.renderState() ? "true" : "false" );
1094  rangesElem.appendChild( rangeElem );
1095  i++;
1096  }
1097 
1098  rendererElem.appendChild( rangesElem );
1099 
1100  // save symbols
1101  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
1102  rendererElem.appendChild( symbolsElem );
1103 
1104  // save source symbol
1105  if ( mSourceSymbol.data() )
1106  {
1107  QgsSymbolV2Map sourceSymbols;
1108  sourceSymbols.insert( "0", mSourceSymbol.data() );
1109  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
1110  rendererElem.appendChild( sourceSymbolElem );
1111  }
1112 
1113  // save source color ramp
1114  if ( mSourceColorRamp.data() )
1115  {
1116  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
1117  rendererElem.appendChild( colorRampElem );
1118  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
1119  invertedElem.setAttribute( "value", mInvertedColorRamp );
1120  rendererElem.appendChild( invertedElem );
1121  }
1122 
1123  // save mode
1124  QString modeString;
1125  if ( mMode == EqualInterval )
1126  modeString = "equal";
1127  else if ( mMode == Quantile )
1128  modeString = "quantile";
1129  else if ( mMode == Jenks )
1130  modeString = "jenks";
1131  else if ( mMode == StdDev )
1132  modeString = "stddev";
1133  else if ( mMode == Pretty )
1134  modeString = "pretty";
1135  if ( !modeString.isEmpty() )
1136  {
1137  QDomElement modeElem = doc.createElement( "mode" );
1138  modeElem.setAttribute( "name", modeString );
1139  rendererElem.appendChild( modeElem );
1140  }
1141 
1142  QDomElement rotationElem = doc.createElement( "rotation" );
1143  if ( mRotation.data() )
1145  rendererElem.appendChild( rotationElem );
1146 
1147  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
1148  if ( mSizeScale.data() )
1150  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
1151  rendererElem.appendChild( sizeScaleElem );
1152 
1153  QDomElement labelFormatElem = doc.createElement( "labelformat" );
1154  mLabelFormat.saveToDomElement( labelFormatElem );
1155  rendererElem.appendChild( labelFormatElem );
1156 
1157  if ( mPaintEffect )
1158  mPaintEffect->saveProperties( doc, rendererElem );
1159 
1160  return rendererElem;
1161 }
1162 
1164 {
1166  int count = ranges().count();
1167  for ( int i = 0; i < count; i++ )
1168  {
1169  const QgsRendererRangeV2& range = ranges()[i];
1170  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
1171  lst << qMakePair( range.label(), pix );
1172  }
1173  return lst;
1174 }
1175 
1177 {
1178  QgsLegendSymbolListV2 list;
1179  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
1180  {
1181  // check that all symbols that have the same size expression
1182  QgsDataDefined ddSize;
1183  foreach ( QgsRendererRangeV2 range, mRanges )
1184  {
1185  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( range.symbol() );
1186  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
1187  {
1188  // no common size expression
1190  }
1191  else
1192  {
1193  ddSize = symbol->dataDefinedSize();
1194  }
1195  }
1196 
1197  if ( !ddSize.isActive() || !ddSize.useExpression() )
1198  {
1200  }
1201 
1202  QgsScaleExpression exp( ddSize.expressionString() );
1203  if ( exp.type() != QgsScaleExpression::Unknown )
1204  {
1205  QgsLegendSymbolItemV2 title( NULL, exp.baseExpression(), "" );
1206  list << title;
1207  foreach ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
1208  {
1210  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
1212  s->setSize( exp.size( v ) );
1213  list << si;
1214  }
1215  // now list the graduated symbols
1217  foreach ( QgsLegendSymbolItemV2 item, list2 )
1218  list << item;
1219  return list;
1220  }
1221  }
1222 
1224 }
1225 
1227 {
1228  Q_UNUSED( scaleDenominator );
1229  QgsLegendSymbolList lst;
1230 
1231  foreach ( const QgsRendererRangeV2& range, mRanges )
1232  {
1233  if ( rule.isEmpty() || range.label() == rule )
1234  {
1235  lst << qMakePair( range.label(), range.symbol() );
1236  }
1237  }
1238  return lst;
1239 }
1240 
1242 {
1243  return mSourceSymbol.data();
1244 }
1246 {
1247  mSourceSymbol.reset( sym );
1248 }
1249 
1251 {
1252  return mSourceColorRamp.data();
1253 }
1254 
1256 {
1257  mSourceColorRamp.reset( ramp );
1258 }
1259 
1261 {
1262  double min = DBL_MAX;
1263  for ( int i = 0; i < mRanges.count(); i++ )
1264  {
1265  double sz = 0;
1266  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1267  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1268  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1269  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1270  min = qMin( sz, min );
1271  }
1272  return min;
1273 }
1274 
1276 {
1277  double max = DBL_MIN;
1278  for ( int i = 0; i < mRanges.count(); i++ )
1279  {
1280  double sz = 0;
1281  if ( mRanges[i].symbol()->type() == QgsSymbolV2::Marker )
1282  sz = static_cast< QgsMarkerSymbolV2 * >( mRanges[i].symbol() )->size();
1283  else if ( mRanges[i].symbol()->type() == QgsSymbolV2::Line )
1284  sz = static_cast< QgsLineSymbolV2 * >( mRanges[i].symbol() )->width();
1285  max = qMax( sz, max );
1286  }
1287  return max;
1288 }
1289 
1290 void QgsGraduatedSymbolRendererV2::setSymbolSizes( double minSize, double maxSize )
1291 {
1292  for ( int i = 0; i < mRanges.count(); i++ )
1293  {
1294  QScopedPointer<QgsSymbolV2> symbol( mRanges[i].symbol() ? mRanges[i].symbol()->clone() : 0 );
1295  const double size = mRanges.count() > 1
1296  ? minSize + i * ( maxSize - minSize ) / ( mRanges.count() - 1 )
1297  : .5 * ( maxSize + minSize );
1298  if ( symbol->type() == QgsSymbolV2::Marker )
1299  static_cast< QgsMarkerSymbolV2 * >( symbol.data() )->setSize( size );
1300  if ( symbol->type() == QgsSymbolV2::Line )
1301  static_cast< QgsLineSymbolV2 * >( symbol.data() )->setWidth( size );
1302  updateRangeSymbol( i, symbol.take() );
1303  }
1304 }
1305 
1307 {
1308  int i = 0;
1309  if ( ramp )
1310  {
1311  setSourceColorRamp( ramp );
1312  setInvertedColorRamp( inverted );
1313  }
1314 
1315  if ( mSourceColorRamp )
1316  {
1317  foreach ( QgsRendererRangeV2 range, mRanges )
1318  {
1319  QgsSymbolV2 *symbol = range.symbol() ? range.symbol()->clone() : 0;
1320  if ( symbol )
1321  {
1322  double colorValue;
1323  if ( inverted )
1324  colorValue = ( mRanges.count() > 1 ? ( double )( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
1325  else
1326  colorValue = ( mRanges.count() > 1 ? ( double ) i / ( mRanges.count() - 1 ) : 0 );
1327  symbol->setColor( mSourceColorRamp->color( colorValue ) );
1328  }
1329  updateRangeSymbol( i, symbol );
1330  ++i;
1331  }
1332  }
1333 
1334 }
1335 
1337 {
1338  if ( !sym )
1339  return;
1340 
1341  int i = 0;
1342  foreach ( QgsRendererRangeV2 range, mRanges )
1343  {
1344  QScopedPointer<QgsSymbolV2> symbol( sym->clone() );
1346  {
1347  symbol->setColor( range.symbol()->color() );
1348  }
1349  else if ( mGraduatedMethod == GraduatedSize )
1350  {
1351  if ( symbol->type() == QgsSymbolV2::Marker )
1352  static_cast<QgsMarkerSymbolV2 *>( symbol.data() )->setSize(
1353  static_cast<QgsMarkerSymbolV2 *>( range.symbol() )->size() );
1354  else if ( symbol->type() == QgsSymbolV2::Line )
1355  static_cast<QgsLineSymbolV2 *>( symbol.data() )->setWidth(
1356  static_cast<QgsLineSymbolV2 *>( range.symbol() )->width() );
1357  }
1358  updateRangeSymbol( i, symbol.take() );
1359  ++i;
1360  }
1361  setSourceSymbol( sym->clone() );
1362 }
1363 
1365 {
1367 }
1368 
1370 {
1372 }
1373 
1375 {
1377 }
1378 
1380 {
1382 }
1383 
1385 {
1387  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1388  {
1389  if ( it->symbol() )
1390  setScaleMethodToSymbol( it->symbol(), scaleMethod );
1391  }
1392 }
1393 
1395 {
1396  return true;
1397 }
1398 
1400 {
1401  bool ok;
1402  int index = key.toInt( &ok );
1403  if ( ok && index >= 0 && index < mRanges.size() )
1404  return mRanges[ index ].renderState();
1405  else
1406  return true;
1407 }
1408 
1410 {
1411  bool ok;
1412  int index = key.toInt( &ok );
1413  if ( ok )
1414  updateRangeRenderState( index, state );
1415 }
1416 
1417 
1419 {
1420  QgsSymbolV2* newSymbol = symbol->clone();
1421  QString label = "0.0 - 0.0";
1422  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1423 }
1424 
1425 void QgsGraduatedSymbolRendererV2::addClass( double lower, double upper )
1426 {
1427  QgsSymbolV2* newSymbol = mSourceSymbol->clone();
1428  QString label = mLabelFormat.labelForRange( lower, upper );
1429  mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
1430 }
1431 
1432 void QgsGraduatedSymbolRendererV2::addBreak( double breakValue, bool updateSymbols )
1433 {
1435  while ( it.hasNext() )
1436  {
1437  QgsRendererRangeV2 range = it.next();
1438  if ( range.lowerValue() < breakValue && range.upperValue() > breakValue )
1439  {
1441  newRange.setLowerValue( breakValue );
1442  newRange.setUpperValue( range.upperValue() );
1443  newRange.setLabel( mLabelFormat.labelForRange( newRange ) );
1444  newRange.setSymbol( mSourceSymbol->clone() );
1445 
1446  //update old range
1447  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
1448  range.setUpperValue( breakValue );
1449  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range.lowerValue(), breakValue ) );
1450  it.setValue( range );
1451 
1452  it.insert( newRange );
1453  break;
1454  }
1455  }
1456 
1457  if ( updateSymbols )
1458  {
1459  switch ( mGraduatedMethod )
1460  {
1461  case GraduatedColor:
1463  break;
1464  case GraduatedSize:
1466  break;
1467  }
1468  }
1469 }
1470 
1472 {
1473  mRanges.append( range );
1474 }
1475 
1477 {
1478  mRanges.removeAt( idx );
1479 }
1480 
1482 {
1483  mRanges.clear();
1484 }
1485 
1487 {
1488  if ( updateRanges && labelFormat != mLabelFormat )
1489  {
1490  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1491  {
1492  it->setLabel( labelFormat.labelForRange( *it ) );
1493  }
1494  }
1496 }
1497 
1498 
1500 {
1501  // Find the minimum size of a class
1502  double minClassRange = 0.0;
1503  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1504  {
1505  double range = it->upperValue() - it->lowerValue();
1506  if ( range <= 0.0 )
1507  continue;
1508  if ( minClassRange == 0.0 || range < minClassRange )
1509  minClassRange = range;
1510  }
1511  if ( minClassRange <= 0.0 )
1512  return;
1513 
1514  // Now set the number of decimal places to ensure no more than 20% error in
1515  // representing this range (up to 10% at upper and lower end)
1516 
1517  int ndp = 10;
1518  double nextDpMinRange = 0.0000000099;
1519  while ( ndp > 0 && nextDpMinRange < minClassRange )
1520  {
1521  ndp--;
1522  nextDpMinRange *= 10.0;
1523  }
1524  mLabelFormat.setPrecision( ndp );
1525  if ( updateRanges ) setLabelFormat( mLabelFormat, true );
1526 }
1527 
1529 {
1530  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1531  return;
1532  mRanges.move( from, to );
1533 }
1534 
1536 {
1537  return r1 < r2;
1538 }
1539 
1541 {
1542  return !valueLessThan( r1, r2 );
1543 }
1544 
1546 {
1547  QgsDebugMsg( "Entered" );
1548  if ( order == Qt::AscendingOrder )
1549  {
1550  qSort( mRanges.begin(), mRanges.end(), valueLessThan );
1551  }
1552  else
1553  {
1554  qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1555  }
1556 }
1557 
1559 {
1560  QgsRangeList sortedRanges = mRanges;
1561  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1562 
1563  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1564  if ( it == sortedRanges.constEnd() )
1565  return false;
1566 
1567  if (( *it ).upperValue() < ( *it ).lowerValue() )
1568  return true;
1569 
1570  double prevMax = ( *it ).upperValue();
1571  it++;
1572 
1573  for ( ; it != sortedRanges.constEnd(); ++it )
1574  {
1575  if (( *it ).upperValue() < ( *it ).lowerValue() )
1576  return true;
1577 
1578  if (( *it ).lowerValue() < prevMax )
1579  return true;
1580 
1581  prevMax = ( *it ).upperValue();
1582  }
1583  return false;
1584 }
1585 
1587 {
1588  QgsRangeList sortedRanges = mRanges;
1589  qSort( sortedRanges.begin(), sortedRanges.end(), valueLessThan );
1590 
1591  QgsRangeList::const_iterator it = sortedRanges.constBegin();
1592  if ( it == sortedRanges.constEnd() )
1593  return false;
1594 
1595  double prevMax = ( *it ).upperValue();
1596  it++;
1597 
1598  for ( ; it != sortedRanges.constEnd(); ++it )
1599  {
1600  if ( !qgsDoubleNear(( *it ).lowerValue(), prevMax ) )
1601  return true;
1602 
1603  prevMax = ( *it ).upperValue();
1604  }
1605  return false;
1606 }
1607 
1609 {
1610  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1611 }
1612 
1614 {
1615  return !labelLessThan( r1, r2 );
1616 }
1617 
1619 {
1620  if ( order == Qt::AscendingOrder )
1621  {
1622  qSort( mRanges.begin(), mRanges.end(), labelLessThan );
1623  }
1624  else
1625  {
1626  qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1627  }
1628 }
1629 
1631 {
1632  if ( renderer->type() == "graduatedSymbol" )
1633  {
1634  return dynamic_cast<QgsGraduatedSymbolRendererV2*>( renderer->clone() );
1635  }
1636  if ( renderer->type() == "pointDisplacement" )
1637  {
1638  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1639  if ( pointDisplacementRenderer )
1640  return convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1641  }
1642  if ( renderer->type() == "invertedPolygonRenderer" )
1643  {
1644  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1645  if ( invertedPolygonRenderer )
1646  return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1647  }
1648 
1649  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1650  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1651 
1653  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols();
1654  if ( symbols.size() > 0 )
1655  {
1656  r->setSourceSymbol( symbols.at( 0 )->clone() );
1657  }
1658 
1659  return r;
1660 }
1661 
1663 {
1664  switch ( method )
1665  {
1666  case GraduatedColor: return "GraduatedColor";
1667  case GraduatedSize: return "GraduatedSize";
1668  }
1669  return "";
1670 }
1671 
1672 
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:86
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
QString & append(QChar ch)
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)
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.
bool updateRangeLabel(int rangeIndex, QString label)
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: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
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
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:354
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)
const T & at(int i) const
void removeAt(int i)
QString labelForRange(double lower, double upper) const
void setSizeScaleField(QString fieldOrExpression)
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:173
virtual QgsSymbolV2 * originalSymbolForFeature(QgsFeature &feature) override
Return symbol for feature.
QString expressionString() const
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.
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:162
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
virtual QgsSymbolV2 * symbolForFeature(QgsFeature &feature) override
to be overridden
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
virtual bool legendSymbolItemChecked(QString key) override
item in symbology was checked
bool qgsDoubleNear(double a, double b, double epsilon=4 *DBL_EPSILON)
Definition: qgis.h:350
static const char * graduatedMethodStr(GraduatedMethod method)
QgsPaintEffect * mPaintEffect
void setWidth(double width)
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
virtual void stopRender(QgsRenderContext &context) override
virtual QgsSymbolV2List symbols() override
for symbol levels
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
void setGraduatedMethod(GraduatedMethod method)
set the method used for graduation (either size or color)
void append(const T &value)
static QDomElement saveColorRamp(QString name, QgsVectorColorRampV2 *ramp, QDomDocument &doc)
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:90
void setAttribute(const QString &name, const QString &value)
#define DEFAULT_SCALE_METHOD
virtual QgsVectorColorRampV2 * clone() const =0
void swap(QgsRendererRangeV2 &other)
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, QString function)
int toInt(bool *ok, int base) const
bool isEmpty() const
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, QString tagName, QDomDocument &doc)
bool isEmpty() const
iterator begin()
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()
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)
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
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
int min(int a, int b)
Definition: util.h:93
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.
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)
bool hasNext() const
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)
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
QString mid(int position, int n) const
void copyPaintEffect(QgsFeatureRendererV2 *destRenderer) const
Copies paint effect of this renderer to another renderer.
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.
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)
int length() const
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
QScopedPointer< QgsSymbolV2 > mSourceSymbol
QList< T > toList() const
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
double toDouble(bool *ok) const
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
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
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
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
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
static void convertSymbolRotation(QgsSymbolV2 *symbol, const QString &field)
QColor color() const