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