QGIS API Documentation  2.2.0-Valmiera
 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  qgsgraduatedsymbolrendererv2.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
16 
17 #include "qgssymbolv2.h"
18 #include "qgssymbollayerv2utils.h"
19 #include "qgsvectorcolorrampv2.h"
20 
21 #include "qgsfeature.h"
22 #include "qgsvectorlayer.h"
23 #include "qgslogger.h"
24 #include "qgsvectordataprovider.h"
25 #include "qgsexpression.h"
26 #include <QDomDocument>
27 #include <QDomElement>
28 #include <QSettings> // for legend
29 #include <limits> // for jenks classification
30 #include <cmath> // for pretty classification
31 #include <ctime>
32 
34  : mLowerValue( 0 ), mUpperValue( 0 ), mSymbol( 0 ), mLabel()
35 {
36 }
37 
38 QgsRendererRangeV2::QgsRendererRangeV2( double lowerValue, double upperValue, QgsSymbolV2* symbol, QString label )
39  : mLowerValue( lowerValue )
40  , mUpperValue( upperValue )
41  , mSymbol( symbol )
42  , mLabel( label )
43 {
44 }
45 
47  : mLowerValue( range.mLowerValue )
48  , mUpperValue( range.mUpperValue )
49  , mSymbol( range.mSymbol.data() ? range.mSymbol->clone() : NULL )
50  , mLabel( range.mLabel )
51 {
52 }
53 
54 // cpy and swap idiom, note that the cpy is done with 'pass by value'
56 {
57  swap( range );
58  return *this;
59 }
60 
62 {
63  qSwap( mLowerValue, other.mLowerValue );
64  qSwap( mUpperValue, other.mUpperValue );
65  qSwap( mSymbol, other.mSymbol );
66  std::swap( mLabel, other.mLabel );
67 }
68 
70 {
71  return mLowerValue;
72 }
73 
75 {
76  return mUpperValue;
77 }
78 
80 {
81  return mSymbol.data();
82 }
83 
85 {
86  return mLabel;
87 }
88 
90 {
91  if ( mSymbol.data() != s ) mSymbol.reset( s );
92 }
93 
94 void QgsRendererRangeV2::setLabel( QString label )
95 {
96  mLabel = label;
97 }
98 
99 void QgsRendererRangeV2::setUpperValue( double upperValue )
100 {
102 }
103 
104 void QgsRendererRangeV2::setLowerValue( double lowerValue )
105 {
107 }
108 
110 {
111  return QString( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel ).arg( mSymbol->dump() );
112 }
113 
114 void QgsRendererRangeV2::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
115 {
116  if ( !mSymbol.data() || props.value( "attribute", "" ).isEmpty() )
117  return;
118 
119  QString attrName = props[ "attribute" ];
120 
121  QDomElement ruleElem = doc.createElement( "se:Rule" );
122  element.appendChild( ruleElem );
123 
124  QDomElement nameElem = doc.createElement( "se:Name" );
125  nameElem.appendChild( doc.createTextNode( mLabel ) );
126  ruleElem.appendChild( nameElem );
127 
128  QDomElement descrElem = doc.createElement( "se:Description" );
129  QDomElement titleElem = doc.createElement( "se:Title" );
130  QString descrStr = QString( "range: %1 - %2" ).arg( mLowerValue ).arg( mUpperValue );
131  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
132  descrElem.appendChild( titleElem );
133  ruleElem.appendChild( descrElem );
134 
135  // create the ogc:Filter for the range
136  QString filterFunc = QString( "%1 > %2 AND %1 <= %3" )
137  .arg( attrName.replace( "\"", "\"\"" ) )
138  .arg( mLowerValue ).arg( mUpperValue );
139  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, filterFunc );
140 
141  mSymbol->toSld( doc, ruleElem, props );
142 }
143 
145 
147  : QgsFeatureRendererV2( "graduatedSymbol" ),
148  mAttrName( attrName ),
149  mRanges( ranges ),
150  mMode( Custom ),
151  mInvertedColorRamp( false ),
152  mScaleMethod( DEFAULT_SCALE_METHOD )
153 {
154  // TODO: check ranges for sanity (NULL symbols, invalid ranges)
155 }
156 
158 {
159  mRanges.clear(); // should delete all the symbols
160 }
161 
163 {
164  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
165  {
166  if ( it->lowerValue() <= value && it->upperValue() >= value )
167  return it->symbol();
168  }
169  // the value is out of the range: return NULL instead of symbol
170  return NULL;
171 }
172 
174 {
175  const QgsAttributes& attrs = feature.attributes();
176  QVariant value;
177  if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
178  {
179  value = mExpression->evaluate( &feature );
180  }
181  else
182  {
183  value = attrs[mAttrNum];
184  }
185 
186  // Null values should not be categorized
187  if ( value.isNull() )
188  return NULL;
189 
190  // find the right category
191  QgsSymbolV2* symbol = symbolForValue( value.toDouble() );
192  if ( symbol == NULL )
193  return NULL;
194 
195  if ( !mRotation.data() && !mSizeScale.data() )
196  return symbol; // no data-defined rotation/scaling - just return the symbol
197 
198  // find out rotation, size scale
199  const double rotation = mRotation.data() ? mRotation->evaluate( feature ).toDouble() : 0;
200  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( feature ).toDouble() : 1.;
201 
202  // take a temporary symbol (or create it if doesn't exist)
203  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
204 
205  // modify the temporary symbol and return it
206  if ( tempSymbol->type() == QgsSymbolV2::Marker )
207  {
208  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
209  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
210  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
211  markerSymbol->setScaleMethod( mScaleMethod );
212  }
213  else if ( tempSymbol->type() == QgsSymbolV2::Line )
214  {
215  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
216  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
217  }
218  return tempSymbol;
219 }
220 
222 {
223  // find out classification attribute index from name
224  mAttrNum = vlayer ? vlayer->fieldNameIndex( mAttrName ) : -1;
225 
226  if ( mAttrNum == -1 )
227  {
228  mExpression.reset( new QgsExpression( mAttrName ) );
229  mExpression->prepare( vlayer->pendingFields() );
230  }
231 
232  QgsRangeList::iterator it = mRanges.begin();
233  for ( ; it != mRanges.end(); ++it )
234  {
235  it->symbol()->startRender( context, vlayer );
236 
237  if ( mRotation.data() || mSizeScale.data() )
238  {
239  QgsSymbolV2* tempSymbol = it->symbol()->clone();
240  tempSymbol->setRenderHints(( mRotation.data() ? QgsSymbolV2::DataDefinedRotation : 0 ) |
242  tempSymbol->startRender( context, vlayer );
243  mTempSymbols[ it->symbol()] = tempSymbol;
244  }
245  }
246 }
247 
249 {
250  QgsRangeList::iterator it = mRanges.begin();
251  for ( ; it != mRanges.end(); ++it )
252  it->symbol()->stopRender( context );
253 
254  // cleanup mTempSymbols
255  QHash<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
256  for ( ; it2 != mTempSymbols.end(); ++it2 )
257  {
258  it2.value()->stopRender( context );
259  delete it2.value();
260  }
261  mTempSymbols.clear();
262 }
263 
265 {
266  QSet<QString> attributes;
267 
269  {
270  attributes.unite( exp->referencedColumns().toSet() );
271  delete exp;
272  }
273 
274  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
275  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
276 
277  QgsRangeList::const_iterator range_it = mRanges.constBegin();
278  for ( ; range_it != mRanges.constEnd(); ++range_it )
279  {
280  QgsSymbolV2* symbol = range_it->symbol();
281  if ( symbol )
282  {
283  attributes.unite( symbol->usedAttributes() );
284  }
285  }
286  return attributes.toList();
287 }
288 
290 {
291  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
292  return false;
293  mRanges[rangeIndex].setSymbol( symbol );
294  return true;
295 }
296 
297 bool QgsGraduatedSymbolRendererV2::updateRangeLabel( int rangeIndex, QString label )
298 {
299  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
300  return false;
301  mRanges[rangeIndex].setLabel( label );
302  return true;
303 }
304 
305 bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double value )
306 {
307  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
308  return false;
309  mRanges[rangeIndex].setUpperValue( value );
310  return true;
311 }
312 
313 bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double value )
314 {
315  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
316  return false;
317  mRanges[rangeIndex].setLowerValue( value );
318  return true;
319 }
320 
322 {
323  QString s = QString( "GRADUATED: attr %1\n" ).arg( mAttrName );
324  for ( int i = 0; i < mRanges.count(); i++ )
325  s += mRanges[i].dump();
326  return s;
327 }
328 
330 {
332  r->setMode( mMode );
333  if ( mSourceSymbol.data() )
334  r->setSourceSymbol( mSourceSymbol->clone() );
335  if ( mSourceColorRamp.data() )
336  {
337  r->setSourceColorRamp( mSourceColorRamp->clone() );
339  }
343  r->setScaleMethod( scaleMethod() );
344  return r;
345 }
346 
347 void QgsGraduatedSymbolRendererV2::toSld( QDomDocument& doc, QDomElement &element ) const
348 {
349  QgsStringMap props;
350  props[ "attribute" ] = mAttrName;
351  if ( mRotation.data() )
352  props[ "angle" ] = mRotation->expression();
353  if ( mSizeScale.data() )
354  props[ "scale" ] = mSizeScale->expression();
355 
356  // create a Rule for each range
357  for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
358  {
359  QgsStringMap catProps( props );
360  it->toSld( doc, element, catProps );
361  }
362 }
363 
365 {
366  QgsSymbolV2List lst;
367  for ( int i = 0; i < mRanges.count(); i++ )
368  lst.append( mRanges[i].symbol() );
369  return lst;
370 }
371 
372 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
373 {
374 
375  // Equal interval algorithm
376  //
377  // Returns breaks based on dividing the range ('minimum' to 'maximum')
378  // into 'classes' parts.
379 
380  double step = ( maximum - minimum ) / classes;
381 
382  QList<double> breaks;
383  double value = minimum;
384  for ( int i = 0; i < classes; i++ )
385  {
386  value += step;
387  breaks.append( value );
388  }
389 
390  // floating point arithmetics is not precise:
391  // set the last break to be exactly maximum so we do not miss it
392  breaks[classes-1] = maximum;
393 
394  return breaks;
395 }
396 
397 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
398 {
399  // q-th quantile of a data set:
400  // value where q fraction of data is below and (1-q) fraction is above this value
401  // Xq = (1 - r) * X_NI1 + r * X_NI2
402  // NI1 = (int) (q * (n+1))
403  // NI2 = NI1 + 1
404  // r = q * (n+1) - (int) (q * (n+1))
405  // (indices of X: 1...n)
406 
407  // sort the values first
408  qSort( values );
409 
410  QList<double> breaks;
411 
412  // If there are no values to process: bail out
413  if ( !values.count() )
414  return breaks;
415 
416  int n = values.count();
417  double Xq = n > 0 ? values[0] : 0.0;
418 
419  for ( int i = 1; i < classes; i++ )
420  {
421  if ( n > 1 )
422  {
423  double q = i / ( double ) classes;
424  double a = q * ( n - 1 );
425  int aa = ( int )( a );
426 
427  double r = a - aa;
428  Xq = ( 1 - r ) * values[aa] + r * values[aa+1];
429  }
430  breaks.append( Xq );
431  }
432 
433  breaks.append( values[ n-1 ] );
434 
435  return breaks;
436 }
437 
438 static QList<double> _calcPrettyBreaks( double minimum, double maximum, int classes )
439 {
440 
441  // C++ implementation of R's pretty algorithm
442  // Based on code for determining optimal tick placement for statistical graphics
443  // from the R statistical programming language.
444  // Code ported from R implementation from 'labeling' R package
445  //
446  // Computes a sequence of about 'classes' equally spaced round values
447  // which cover the range of values from 'minimum' to 'maximum'.
448  // The values are chosen so that they are 1, 2 or 5 times a power of 10.
449 
450  QList<double> breaks;
451  if ( classes < 1 )
452  {
453  breaks.append( maximum );
454  return breaks;
455  }
456 
457  int minimumCount = ( int ) classes / 3;
458  double shrink = 0.75;
459  double highBias = 1.5;
460  double adjustBias = 0.5 + 1.5 * highBias;
461  int divisions = classes;
462  double h = highBias;
463  double cell;
464  int U;
465  bool small = false;
466  double dx = maximum - minimum;
467 
468  if ( dx == 0 && maximum == 0 )
469  {
470  cell = 1.0;
471  small = true;
472  U = 1;
473  }
474  else
475  {
476  cell = qMax( qAbs( minimum ), qAbs( maximum ) );
477  if ( adjustBias >= 1.5 * h + 0.5 )
478  {
479  U = 1 + ( 1.0 / ( 1 + h ) );
480  }
481  else
482  {
483  U = 1 + ( 1.5 / ( 1 + adjustBias ) );
484  }
485  small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 );
486  }
487 
488  if ( small )
489  {
490  if ( cell > 10 )
491  {
492  cell = 9 + cell / 10;
493  cell = cell * shrink;
494  }
495  if ( minimumCount > 1 )
496  {
497  cell = cell / minimumCount;
498  }
499  }
500  else
501  {
502  cell = dx;
503  if ( divisions > 1 )
504  {
505  cell = cell / divisions;
506  }
507  }
508  if ( cell < 20 * 1e-07 )
509  {
510  cell = 20 * 1e-07;
511  }
512 
513  double base = pow( 10.0, floor( log10( cell ) ) );
514  double unit = base;
515  if (( 2 * base ) - cell < h *( cell - unit ) )
516  {
517  unit = 2.0 * base;
518  if (( 5 * base ) - cell < adjustBias *( cell - unit ) )
519  {
520  unit = 5.0 * base;
521  if (( 10.0 * base ) - cell < h *( cell - unit ) )
522  {
523  unit = 10.0 * base;
524  }
525  }
526  }
527  // Maybe used to correct for the epsilon here??
528  int start = floor( minimum / unit + 1e-07 );
529  int end = ceil( maximum / unit - 1e-07 );
530 
531  // Extend the range out beyond the data. Does this ever happen??
532  while ( start * unit > minimum + ( 1e-07 * unit ) )
533  {
534  start = start - 1;
535  }
536  while ( end * unit < maximum - ( 1e-07 * unit ) )
537  {
538  end = end + 1;
539  }
540  QgsDebugMsg( QString( "pretty classes: %1" ).arg( end ) );
541 
542  // If we don't have quite enough labels, extend the range out
543  // to make more (these labels are beyond the data :( )
544  int k = floor( 0.5 + end - start );
545  if ( k < minimumCount )
546  {
547  k = minimumCount - k;
548  if ( start >= 0 )
549  {
550  end = end + k / 2;
551  start = start - k / 2 + k % 2;
552  }
553  else
554  {
555  start = start - k / 2;
556  end = end + k / 2 + k % 2;
557  }
558  }
559  double minimumBreak = start * unit;
560  //double maximumBreak = end * unit;
561  int count = end - start;
562 
563  for ( int i = 1; i < count + 1; i++ )
564  {
565  breaks.append( minimumBreak + i * unit );
566  }
567 
568  if ( breaks.isEmpty() )
569  return breaks;
570 
571  if ( breaks.first() < minimum )
572  {
573  breaks[0] = minimum;
574  }
575  if ( breaks.last() > maximum )
576  {
577  breaks[breaks.count()-1] = maximum;
578  }
579 
580  return breaks;
581 } // _calcPrettyBreaks
582 
583 
584 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<int> &labels )
585 {
586 
587  // C++ implementation of the standard deviation class interval algorithm
588  // as implemented in the 'classInt' package available for the R statistical
589  // prgramming language.
590 
591  // Returns breaks based on '_calcPrettyBreaks' of the centred and scaled
592  // values of 'values', and may have a number of classes different from 'classes'.
593 
594  // If there are no values to process: bail out
595  if ( !values.count() )
596  return QList<double>();
597 
598  double mean = 0.0;
599  double stdDev = 0.0;
600  int n = values.count();
601  double minimum = values[0];
602  double maximum = values[0];
603 
604  for ( int i = 0; i < n; i++ )
605  {
606  mean += values[i];
607  minimum = qMin( values[i], minimum ); // could use precomputed max and min
608  maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
609  }
610  mean = mean / ( double ) n;
611 
612  double sd = 0.0;
613  for ( int i = 0; i < n; i++ )
614  {
615  sd = values[i] - mean;
616  stdDev += sd * sd;
617  }
618  stdDev = sqrt( stdDev / n );
619 
620  QList<double> breaks = _calcPrettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
621  for ( int i = 0; i < breaks.count(); i++ )
622  {
623  labels.append(( int ) breaks[i] );
624  breaks[i] = ( breaks[i] * stdDev ) + mean;
625  }
626 
627  return breaks;
628 } // _calcStdDevBreaks
629 
630 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
631  double minimum, double maximum,
632  int maximumSize = 1000 )
633 {
634  // Jenks Optimal (Natural Breaks) algorithm
635  // Based on the Jenks algorithm from the 'classInt' package available for
636  // the R statistical prgramming language, and from Python code from here:
637  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
638  // and is based on a JAVA and Fortran code available here:
639  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
640 
641  // Returns class breaks such that classes are internally homogeneous while
642  // assuring heterogeneity among classes.
643 
644  if ( !values.count() )
645  return QList<double>();
646 
647  if ( classes <= 1 )
648  {
649  return QList<double>() << maximum;
650  }
651 
652  if ( classes >= values.size() )
653  {
654  return values;
655  }
656 
657  QVector<double> sample;
658 
659  // if we have lots of values, we need to take a random sample
660  if ( values.size() > maximumSize )
661  {
662  // for now, sample at least maximumSize values or a 10% sample, whichever
663  // is larger. This will produce a more representative sample for very large
664  // layers, but could end up being computationally intensive...
665 
666  qsrand( time( 0 ) );
667 
668  sample.resize( qMax( maximumSize, values.size() / 10 ) );
669 
670  QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
671  QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
672 
673  sample[ 0 ] = minimum;
674  sample[ 1 ] = maximum;;
675  for ( int i = 2; i < sample.size(); i++ )
676  {
677  // pick a random integer from 0 to n
678  double r = qrand();
679  int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
680  sample[ i ] = values[ j ];
681  }
682  }
683  else
684  {
685  sample = values.toVector();
686  }
687 
688  int n = sample.size();
689 
690  // sort the sample values
691  qSort( sample );
692 
693  QVector< QVector<int> > matrixOne( n + 1 );
694  QVector< QVector<double> > matrixTwo( n + 1 );
695 
696  for ( int i = 0; i <= n; i++ )
697  {
698  matrixOne[i].resize( classes + 1 );
699  matrixTwo[i].resize( classes + 1 );
700  }
701 
702  for ( int i = 1; i <= classes; i++ )
703  {
704  matrixOne[0][i] = 1;
705  matrixOne[1][i] = 1;
706  matrixTwo[0][i] = 0.0;
707  for ( int j = 2; j <= n; j++ )
708  {
709  matrixTwo[j][i] = std::numeric_limits<double>::max();
710  }
711  }
712 
713  for ( int l = 2; l <= n; l++ )
714  {
715  double s1 = 0.0;
716  double s2 = 0.0;
717  int w = 0;
718 
719  double v = 0.0;
720 
721  for ( int m = 1; m <= l; m++ )
722  {
723  int i3 = l - m + 1;
724 
725  double val = sample[ i3 - 1 ];
726 
727  s2 += val * val;
728  s1 += val;
729  w++;
730 
731  v = s2 - ( s1 * s1 ) / ( double ) w;
732  int i4 = i3 - 1;
733  if ( i4 != 0 )
734  {
735  for ( int j = 2; j <= classes; j++ )
736  {
737  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
738  {
739  matrixOne[l][j] = i4;
740  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
741  }
742  }
743  }
744  }
745  matrixOne[l][1] = 1;
746  matrixTwo[l][1] = v;
747  }
748 
749  QVector<double> breaks( classes );
750  breaks[classes-1] = sample[n-1];
751 
752  for ( int j = classes, k = n; j >= 2; j-- )
753  {
754  int id = matrixOne[k][j] - 1;
755  breaks[j - 2] = sample[id];
756  k = matrixOne[k][j] - 1;
757  }
758 
759  return breaks.toList();
760 } //_calcJenksBreaks
761 
762 
764  QgsVectorLayer* vlayer,
765  QString attrName,
766  int classes,
767  Mode mode,
768  QgsSymbolV2* symbol,
769  QgsVectorColorRampV2* ramp,
770  bool inverted )
771 {
772  if ( classes < 1 )
773  return NULL;
774 
775  int attrNum = vlayer->fieldNameIndex( attrName );
776  double minimum;
777  double maximum;
778 
779  QScopedPointer<QgsExpression> expression;
780 
781  if ( attrNum == -1 )
782  {
783  // try to use expression
784  expression.reset( new QgsExpression( attrName ) );
785  if ( expression->hasParserError() || !expression->prepare( vlayer->pendingFields() ) )
786  return 0; // should have a means to report errors
787 
788  QList<double> values;
789  QgsFeatureIterator fit = vlayer->getFeatures();
790  QgsFeature feature;
791  while ( fit.nextFeature( feature ) )
792  {
793  values << expression->evaluate( feature ).toDouble();
794  }
795  qSort( values );
796  minimum = values.first();
797  maximum = values.last();
798  }
799  else
800  {
801  minimum = vlayer->minimumValue( attrNum ).toDouble();
802  maximum = vlayer->maximumValue( attrNum ).toDouble();
803  }
804 
805  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
806  QList<double> breaks;
807  QList<int> labels;
808  if ( mode == EqualInterval )
809  {
810  breaks = _calcEqualIntervalBreaks( minimum, maximum, classes );
811  }
812  else if ( mode == Pretty )
813  {
814  breaks = _calcPrettyBreaks( minimum, maximum, classes );
815  }
816  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
817  {
818  // get values from layer
819  QList<double> values;
820  QgsFeature f;
821  QStringList lst;
822  if ( expression.isNull() )
823  lst.append( attrName );
824  else
825  lst = expression->referencedColumns();
826 
827  QgsFeatureIterator fit = vlayer->getFeatures( QgsFeatureRequest().setFlags( QgsFeatureRequest::NoGeometry ).setSubsetOfAttributes( lst, vlayer->pendingFields() ) );
828 
829  // create list of non-null attribute values
830  while ( fit.nextFeature( f ) )
831  {
832  QVariant v = expression.isNull() ? f.attribute( attrNum ) : expression->evaluate( f );
833  if ( !v.isNull() )
834  values.append( v.toDouble() );
835  }
836 
837  // calculate the breaks
838  if ( mode == Quantile )
839  {
840  breaks = _calcQuantileBreaks( values, classes );
841  }
842  else if ( mode == Jenks )
843  {
844  breaks = _calcJenksBreaks( values, classes, minimum, maximum );
845  }
846  else if ( mode == StdDev )
847  {
848  breaks = _calcStdDevBreaks( values, classes, labels );
849  }
850  }
851  else
852  {
853  Q_ASSERT( false );
854  }
855 
857  double lower, upper = minimum;
858  QString label;
859 
860  // "breaks" list contains all values at class breaks plus maximum as last break
861  int i = 0;
862  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
863  {
864  lower = upper; // upper border from last interval
865  upper = *it;
866  if ( mode == StdDev )
867  {
868  if ( i == 0 )
869  {
870  label = "< " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
871  }
872  else if ( i == labels.count() - 1 )
873  {
874  label = ">= " + QString::number( labels[i-1], 'i', 0 ) + " Std Dev";
875  }
876  else
877  {
878  label = QString::number( labels[i-1], 'i', 0 ) + " Std Dev" + " - " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
879  }
880  }
881  else
882  {
883  label = QString::number( lower, 'f', 4 ) + " - " + QString::number( upper, 'f', 4 );
884  }
885 
886  QgsSymbolV2* newSymbol = symbol->clone();
887  double colorValue;
888  if ( inverted ) colorValue = ( breaks.count() > 1 ? ( double )( breaks.count() - i - 1 ) / ( breaks.count() - 1 ) : 0 );
889  else colorValue = ( breaks.count() > 1 ? ( double ) i / ( breaks.count() - 1 ) : 0 );
890  newSymbol->setColor( ramp->color( colorValue ) ); // color from (0 / cl-1) to (cl-1 / cl-1)
891 
892  ranges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
893  }
894 
895  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
896  r->setSourceSymbol( symbol->clone() );
897  r->setSourceColorRamp( ramp->clone() );
898  r->setInvertedColorRamp( inverted );
899  r->setMode( mode );
900  return r;
901 }
902 
904 {
905  QDomElement symbolsElem = element.firstChildElement( "symbols" );
906  if ( symbolsElem.isNull() )
907  return NULL;
908 
909  QDomElement rangesElem = element.firstChildElement( "ranges" );
910  if ( rangesElem.isNull() )
911  return NULL;
912 
913  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
915 
916  QDomElement rangeElem = rangesElem.firstChildElement();
917  while ( !rangeElem.isNull() )
918  {
919  if ( rangeElem.tagName() == "range" )
920  {
921  double lowerValue = rangeElem.attribute( "lower" ).toDouble();
922  double upperValue = rangeElem.attribute( "upper" ).toDouble();
923  QString symbolName = rangeElem.attribute( "symbol" );
924  QString label = rangeElem.attribute( "label" );
925  if ( symbolMap.contains( symbolName ) )
926  {
927  QgsSymbolV2* symbol = symbolMap.take( symbolName );
928  ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label ) );
929  }
930  }
931  rangeElem = rangeElem.nextSiblingElement();
932  }
933 
934  QString attrName = element.attribute( "attr" );
935 
936  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
937 
938  // delete symbols if there are any more
940 
941  // try to load source symbol (optional)
942  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
943  if ( !sourceSymbolElem.isNull() )
944  {
945  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
946  if ( sourceSymbolMap.contains( "0" ) )
947  {
948  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
949  }
950  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
951  }
952 
953  // try to load color ramp (optional)
954  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
955  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
956  {
957  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
958  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
959  if ( !invertedColorRampElem.isNull() )
960  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
961  }
962 
963  // try to load mode
964  QDomElement modeElem = element.firstChildElement( "mode" );
965  if ( !modeElem.isNull() )
966  {
967  QString modeString = modeElem.attribute( "name" );
968  if ( modeString == "equal" )
969  r->setMode( EqualInterval );
970  else if ( modeString == "quantile" )
971  r->setMode( Quantile );
972  else if ( modeString == "jenks" )
973  r->setMode( Jenks );
974  else if ( modeString == "stddev" )
975  r->setMode( StdDev );
976  else if ( modeString == "pretty" )
977  r->setMode( Pretty );
978  }
979 
980  QDomElement rotationElem = element.firstChildElement( "rotation" );
981  if ( !rotationElem.isNull() )
982  r->setRotationField( rotationElem.attribute( "field" ) );
983 
984  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
985  if ( !sizeScaleElem.isNull() )
986  r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
987  r->setScaleMethod( QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ) );
988 
989  // TODO: symbol levels
990  return r;
991 }
992 
993 QDomElement QgsGraduatedSymbolRendererV2::save( QDomDocument& doc )
994 {
995  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
996  rendererElem.setAttribute( "type", "graduatedSymbol" );
997  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
998  rendererElem.setAttribute( "attr", mAttrName );
999 
1000  // ranges
1001  int i = 0;
1003  QDomElement rangesElem = doc.createElement( "ranges" );
1004  QgsRangeList::const_iterator it = mRanges.constBegin();
1005  for ( ; it != mRanges.constEnd(); ++it )
1006  {
1007  const QgsRendererRangeV2& range = *it;
1008  QString symbolName = QString::number( i );
1009  symbols.insert( symbolName, range.symbol() );
1010 
1011  QDomElement rangeElem = doc.createElement( "range" );
1012  rangeElem.setAttribute( "lower", QString::number( range.lowerValue(), 'f' ) );
1013  rangeElem.setAttribute( "upper", QString::number( range.upperValue(), 'f' ) );
1014  rangeElem.setAttribute( "symbol", symbolName );
1015  rangeElem.setAttribute( "label", range.label() );
1016  rangesElem.appendChild( rangeElem );
1017  i++;
1018  }
1019 
1020  rendererElem.appendChild( rangesElem );
1021 
1022  // save symbols
1023  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
1024  rendererElem.appendChild( symbolsElem );
1025 
1026  // save source symbol
1027  if ( mSourceSymbol.data() )
1028  {
1029  QgsSymbolV2Map sourceSymbols;
1030  sourceSymbols.insert( "0", mSourceSymbol.data() );
1031  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
1032  rendererElem.appendChild( sourceSymbolElem );
1033  }
1034 
1035  // save source color ramp
1036  if ( mSourceColorRamp.data() )
1037  {
1038  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
1039  rendererElem.appendChild( colorRampElem );
1040  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
1041  invertedElem.setAttribute( "value", mInvertedColorRamp );
1042  rendererElem.appendChild( invertedElem );
1043  }
1044 
1045  // save mode
1046  QString modeString;
1047  if ( mMode == EqualInterval )
1048  modeString = "equal";
1049  else if ( mMode == Quantile )
1050  modeString = "quantile";
1051  else if ( mMode == Jenks )
1052  modeString = "jenks";
1053  else if ( mMode == StdDev )
1054  modeString = "stddev";
1055  else if ( mMode == Pretty )
1056  modeString = "pretty";
1057  if ( !modeString.isEmpty() )
1058  {
1059  QDomElement modeElem = doc.createElement( "mode" );
1060  modeElem.setAttribute( "name", modeString );
1061  rendererElem.appendChild( modeElem );
1062  }
1063 
1064  QDomElement rotationElem = doc.createElement( "rotation" );
1065  if ( mRotation.data() )
1066  rotationElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) );
1067  rendererElem.appendChild( rotationElem );
1068 
1069  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
1070  if ( mSizeScale.data() )
1071  sizeScaleElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mSizeScale.data() ) );
1072  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
1073  rendererElem.appendChild( sizeScaleElem );
1074 
1075  return rendererElem;
1076 }
1077 
1079 {
1080  QSettings settings;
1081  bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
1082 
1084  if ( showClassifiers )
1085  {
1086  lst << qMakePair( classAttribute(), QPixmap() );
1087  }
1088 
1089  int count = ranges().count();
1090  for ( int i = 0; i < count; i++ )
1091  {
1092  const QgsRendererRangeV2& range = ranges()[i];
1093  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
1094  lst << qMakePair( range.label(), pix );
1095  }
1096  return lst;
1097 }
1098 
1100 {
1101  Q_UNUSED( scaleDenominator );
1102  QSettings settings;
1103  bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
1104 
1105  QgsLegendSymbolList lst;
1106  if ( showClassifiers )
1107  {
1108  lst << qMakePair( classAttribute(), ( QgsSymbolV2* )0 );
1109  }
1110 
1111  foreach ( const QgsRendererRangeV2& range, mRanges )
1112  {
1113  if ( rule.isEmpty() || range.label() == rule )
1114  {
1115  QgsSymbolV2* symbol;
1116  if ( !mRotation.data() && !mSizeScale.data() )
1117  {
1118  symbol = range.symbol();
1119  }
1120  else
1121  {
1122  symbol = mTempSymbols[range.symbol()];
1123  }
1124  lst << qMakePair( range.label(), symbol );
1125  }
1126  }
1127  return lst;
1128 }
1129 
1131 {
1132  return mSourceSymbol.data();
1133 }
1135 {
1136  mSourceSymbol.reset( sym );
1137 }
1138 
1140 {
1141  return mSourceColorRamp.data();
1142 }
1143 
1145 {
1146  mSourceColorRamp.reset( ramp );
1147 }
1148 
1150 {
1151  int i = 0;
1152  foreach ( QgsRendererRangeV2 range, mRanges )
1153  {
1154  QgsSymbolV2* symbol = range.symbol()->clone();
1155  double colorValue;
1156  if ( inverted ) colorValue = ( mRanges.count() > 1 ? ( double )( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
1157  else colorValue = ( mRanges.count() > 1 ? ( double ) i / ( mRanges.count() - 1 ) : 0 );
1158  symbol->setColor( ramp->color( colorValue ) );
1159  updateRangeSymbol( i, symbol );
1160  ++i;
1161  }
1162  this->setSourceColorRamp( ramp );
1163  this->setInvertedColorRamp( inverted );
1164 }
1165 
1167 {
1168  int i = 0;
1169  foreach ( QgsRendererRangeV2 range, mRanges )
1170  {
1171  QgsSymbolV2* symbol = sym->clone();
1172  symbol->setColor( range.symbol()->color() );
1173  updateRangeSymbol( i, symbol );
1174  ++i;
1175  }
1176  this->setSourceSymbol( sym->clone() );
1177 }
1178 
1179 void QgsGraduatedSymbolRendererV2::setRotationField( QString fieldOrExpression )
1180 {
1182 }
1183 
1185 {
1186  return mRotation.data() ? QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) : QString();
1187 }
1188 
1189 void QgsGraduatedSymbolRendererV2::setSizeScaleField( QString fieldOrExpression )
1190 {
1192 }
1193 
1195 {
1197 }
1198 
1200 {
1202  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1203  {
1204  setScaleMethodToSymbol( it->symbol(), scaleMethod );
1205  }
1206 }
1207 
1209 {
1210  QgsSymbolV2* newSymbol = symbol->clone();
1211  QString label = "0.0 - 0.0";
1212  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1213 
1214 }
1215 
1217 {
1218  mRanges.append( range );
1219 }
1220 
1222 {
1223  mRanges.removeAt( idx );
1224 }
1225 
1227 {
1228  mRanges.clear();
1229 }
1230 
1232 {
1233  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() ) return;
1234  mRanges.move( from, to );
1235 }
1236 
1238 {
1239  return r1.lowerValue() < r2.lowerValue();
1240 }
1241 
1243 {
1244  return !valueLessThan( r1, r2 );
1245 }
1246 
1248 {
1249  QgsDebugMsg( "Entered" );
1250  if ( order == Qt::AscendingOrder )
1251  {
1252  qSort( mRanges.begin(), mRanges.end(), valueLessThan );
1253  }
1254  else
1255  {
1256  qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1257  }
1258 }
1259 
1261 {
1262  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1263 }
1264 
1266 {
1267  return !labelLessThan( r1, r2 );
1268 }
1269 
1271 {
1272  if ( order == Qt::AscendingOrder )
1273  {
1274  qSort( mRanges.begin(), mRanges.end(), labelLessThan );
1275  }
1276  else
1277  {
1278  qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1279  }
1280 }
1281