Quantum GIS API Documentation  1.7.5-Wroclaw
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
qgsgraduatedsymbolrendererv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2 
3 qgsgraduatedsymbolrendererv2.cpp - Graduated Symbol Renderer Version 2
4 
5 ***************************************************************************
6 * *
7 * This program is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU General Public License as published by *
9 * the Free Software Foundation; either version 2 of the License, or *
10 * (at your option) any later version. *
11 * *
12 ***************************************************************************/
13 
15 
16 #include "qgssymbolv2.h"
17 #include "qgssymbollayerv2utils.h"
18 #include "qgsvectorcolorrampv2.h"
19 
20 #include "qgsfeature.h"
21 #include "qgsvectorlayer.h"
22 #include "qgslogger.h"
23 #include "qgsvectordataprovider.h"
24 
25 #include <QDomDocument>
26 #include <QDomElement>
27 #include <QSettings> // for legend
28 #include <limits> // for jenks classification
29 #include <cmath> // for pretty classification
30 #include <ctime>
31 
32 QgsRendererRangeV2::QgsRendererRangeV2( double lowerValue, double upperValue, QgsSymbolV2* symbol, QString label )
33  : mLowerValue( lowerValue )
34  , mUpperValue( upperValue )
35  , mSymbol( symbol )
36  , mLabel( label )
37 {
38 }
39 
41  : mLowerValue( range.mLowerValue )
42  , mUpperValue( range.mUpperValue )
43  , mLabel( range.mLabel )
44 {
45  mSymbol = range.mSymbol->clone();
46 }
47 
49 {
50  delete mSymbol;
51 }
52 
54 {
55  return mLowerValue;
56 }
57 
59 {
60  return mUpperValue;
61 }
62 
64 {
65  return mSymbol;
66 }
67 
69 {
70  return mLabel;
71 }
72 
74 {
75  if ( mSymbol == s )
76  return;
77  delete mSymbol;
78  mSymbol = s;
79 }
80 
81 void QgsRendererRangeV2::setLabel( QString label )
82 {
83  mLabel = label;
84 }
85 
86 void QgsRendererRangeV2::setUpperValue( double upperValue )
87 {
89 }
90 
91 void QgsRendererRangeV2::setLowerValue( double lowerValue )
92 {
94 }
95 
97 {
98  return QString( "%1 - %2::%3::%4\n" ).arg( mLowerValue ).arg( mUpperValue ).arg( mLabel ).arg( mSymbol->dump() );
99 }
100 
102 
104  : QgsFeatureRendererV2( "graduatedSymbol" ),
105  mAttrName( attrName ),
106  mRanges( ranges ),
107  mMode( Custom ),
108  mSourceSymbol( NULL ),
109  mSourceColorRamp( NULL ),
110  mRotationFieldIdx( -1 ),
111  mSizeScaleFieldIdx( -1 )
112 {
113  // TODO: check ranges for sanity (NULL symbols, invalid ranges)
114 }
115 
117 {
118  mRanges.clear(); // should delete all the symbols
119  delete mSourceSymbol;
120  delete mSourceColorRamp;
121 }
122 
124 {
125  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
126  {
127  if ( it->lowerValue() <= value && it->upperValue() >= value )
128  return it->symbol();
129  }
130  // the value is out of the range: return NULL instead of symbol
131  return NULL;
132 }
133 
135 {
136  const QgsAttributeMap& attrMap = feature.attributeMap();
137  QgsAttributeMap::const_iterator ita = attrMap.find( mAttrNum );
138  if ( ita == attrMap.end() )
139  {
140  QgsDebugMsg( "attribute required by renderer not found: " + mAttrName + "(index " + QString::number( mAttrNum ) + ")" );
141  return NULL;
142  }
143 
144  // find the right category
145  QgsSymbolV2* symbol = symbolForValue( ita->toDouble() );
146  if ( symbol == NULL )
147  return NULL;
148 
149  if ( mRotationFieldIdx == -1 && mSizeScaleFieldIdx == -1 )
150  return symbol; // no data-defined rotation/scaling - just return the symbol
151 
152  // find out rotation, size scale
153  double rotation = 0;
154  double sizeScale = 1;
155  if ( mRotationFieldIdx != -1 )
156  rotation = attrMap[mRotationFieldIdx].toDouble();
157  if ( mSizeScaleFieldIdx != -1 )
158  sizeScale = attrMap[mSizeScaleFieldIdx].toDouble();
159 
160  // take a temporary symbol (or create it if doesn't exist)
161  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
162 
163  // modify the temporary symbol and return it
164  if ( tempSymbol->type() == QgsSymbolV2::Marker )
165  {
166  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
167  if ( mRotationFieldIdx != -1 )
168  markerSymbol->setAngle( rotation );
169  if ( mSizeScaleFieldIdx != -1 )
170  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
171  }
172  else if ( tempSymbol->type() == QgsSymbolV2::Line )
173  {
174  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
175  if ( mSizeScaleFieldIdx != -1 )
176  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
177  }
178  return tempSymbol;
179 }
180 
182 {
183  // find out classification attribute index from name
184  mAttrNum = vlayer ? vlayer->fieldNameIndex( mAttrName ) : -1;
185 
186  mRotationFieldIdx = ( mRotationField.isEmpty() ? -1 : vlayer->fieldNameIndex( mRotationField ) );
187  mSizeScaleFieldIdx = ( mSizeScaleField.isEmpty() ? -1 : vlayer->fieldNameIndex( mSizeScaleField ) );
188 
189  QgsRangeList::iterator it = mRanges.begin();
190  for ( ; it != mRanges.end(); ++it )
191  {
192  it->symbol()->startRender( context );
193 
194  if ( mRotationFieldIdx != -1 || mSizeScaleFieldIdx != -1 )
195  {
196  QgsSymbolV2* tempSymbol = it->symbol()->clone();
199  tempSymbol->startRender( context );
200  mTempSymbols[ it->symbol()] = tempSymbol;
201  }
202  }
203 }
204 
206 {
207  QgsRangeList::iterator it = mRanges.begin();
208  for ( ; it != mRanges.end(); ++it )
209  it->symbol()->stopRender( context );
210 
211  // cleanup mTempSymbols
212 #if QT_VERSION < 0x40600
213  QMap<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
214 #else
215  QHash<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
216 #endif
217  for ( ; it2 != mTempSymbols.end(); ++it2 )
218  {
219  it2.value()->stopRender( context );
220  delete it2.value();
221  }
222  mTempSymbols.clear();
223 }
224 
226 {
227  QList<QString> lst;
228  lst.append( mAttrName );
229  if ( !mRotationField.isEmpty() )
230  lst.append( mRotationField );
231  if ( !mSizeScaleField.isEmpty() )
232  lst.append( mSizeScaleField );
233  return lst;
234 }
235 
237 {
238  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
239  return false;
240  mRanges[rangeIndex].setSymbol( symbol );
241  return true;
242 }
243 
244 bool QgsGraduatedSymbolRendererV2::updateRangeLabel( int rangeIndex, QString label )
245 {
246  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
247  return false;
248  mRanges[rangeIndex].setLabel( label );
249  return true;
250 }
251 
252 bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double value )
253 {
254  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
255  return false;
256  mRanges[rangeIndex].setUpperValue( value );
257  return true;
258 }
259 
260 bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double value )
261 {
262  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
263  return false;
264  mRanges[rangeIndex].setLowerValue( value );
265  return true;
266 }
267 
269 {
270  QString s = QString( "GRADUATED: attr %1\n" ).arg( mAttrName );
271  for ( int i = 0; i < mRanges.count(); i++ )
272  s += mRanges[i].dump();
273  return s;
274 }
275 
277 {
279  r->setMode( mMode );
280  if ( mSourceSymbol )
282  if ( mSourceColorRamp )
287  return r;
288 }
289 
291 {
292  QgsSymbolV2List lst;
293  for ( int i = 0; i < mRanges.count(); i++ )
294  lst.append( mRanges[i].symbol() );
295  return lst;
296 }
297 
298 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
299 {
300 
301  // Equal interval algorithm
302  //
303  // Returns breaks based on dividing the range ('minimum' to 'maximum')
304  // into 'classes' parts.
305 
306  double step = ( maximum - minimum ) / classes;
307 
308  QList<double> breaks;
309  double value = minimum;
310  for ( int i = 0; i < classes; i++ )
311  {
312  value += step;
313  breaks.append( value );
314  }
315  return breaks;
316 }
317 
318 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
319 {
320 
321  // q-th quantile of a data set:
322  // value where q fraction of data is below and (1-q) fraction is above this value
323  // Xq = (1 - r) * X_NI1 + r * X_NI2
324  // NI1 = (int) (q * (n+1))
325  // NI2 = NI1 + 1
326  // r = q * (n+1) - (int) (q * (n+1))
327  // (indices of X: 1...n)
328 
329  // sort the values first
330  qSort( values );
331 
332  QList<double> breaks;
333 
334  int n = values.count();
335  double Xq = n > 0 ? values[0] : 0.0;
336 
337  for ( int i = 1; i < classes; i++ )
338  {
339  if ( n > 1 )
340  {
341  double q = i / ( double ) classes;
342  double a = q * ( n - 1 );
343  int aa = ( int )( a );
344 
345  double r = a - aa;
346  Xq = ( 1 - r ) * values[aa] + r * values[aa+1];
347  }
348  breaks.append( Xq );
349  }
350 
351  breaks.append( values[ n-1 ] );
352 
353  return breaks;
354 }
355 
356 static QList<double> _calcPrettyBreaks( double minimum, double maximum, int classes )
357 {
358 
359  // C++ implementation of R's pretty algorithm
360  // Based on code for determining optimal tick placement for statistical graphics
361  // from the R statistical programming language.
362  // Code ported from R implementation from 'labeling' R package
363  //
364  // Computes a sequence of about 'classes' equally spaced round values
365  // which cover the range of values from 'minimum' to 'maximum'.
366  // The values are chosen so that they are 1, 2 or 5 times a power of 10.
367 
368  QList<double> breaks;
369  if ( classes < 1 )
370  {
371  breaks.append( maximum );
372  return breaks;
373  }
374 
375  int minimumCount = ( int ) classes / 3;
376  double shrink = 0.75;
377  double highBias = 1.5;
378  double adjustBias = 0.5 + 1.5 * highBias;
379  int divisions = classes;
380  double h = highBias;
381  double cell;
382  int U;
383  bool small = false;
384  double dx = maximum - minimum;
385 
386  if ( dx == 0 && maximum == 0 )
387  {
388  cell = 1.0;
389  small = true;
390  U = 1;
391  }
392  else
393  {
394  cell = qMax( qAbs( minimum ), qAbs( maximum ) );
395  if ( adjustBias >= 1.5 * h + 0.5 )
396  {
397  U = 1 + ( 1.0 / ( 1 + h ) );
398  }
399  else
400  {
401  U = 1 + ( 1.5 / ( 1 + adjustBias ) );
402  }
403  small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 );
404  }
405 
406  if ( small )
407  {
408  if ( cell > 10 )
409  {
410  cell = 9 + cell / 10;
411  cell = cell * shrink;
412  }
413  if ( minimumCount > 1 )
414  {
415  cell = cell / minimumCount;
416  }
417  }
418  else
419  {
420  cell = dx;
421  if ( divisions > 1 )
422  {
423  cell = cell / divisions;
424  }
425  }
426  if ( cell < 20 * 1e-07 )
427  {
428  cell = 20 * 1e-07;
429  }
430 
431  double base = pow( 10.0, floor( log10( cell ) ) );
432  double unit = base;
433  if (( 2 * base ) - cell < h *( cell - unit ) )
434  {
435  unit = 2.0 * base;
436  if (( 5 * base ) - cell < adjustBias *( cell - unit ) )
437  {
438  unit = 5.0 * base;
439  if (( 10.0 * base ) - cell < h *( cell - unit ) )
440  {
441  unit = 10.0 * base;
442  }
443  }
444  }
445  // Maybe used to correct for the epsilon here??
446  int start = floor( minimum / unit + 1e-07 );
447  int end = ceil( maximum / unit - 1e-07 );
448 
449  // Extend the range out beyond the data. Does this ever happen??
450  while ( start * unit > minimum + ( 1e-07 * unit ) )
451  {
452  start = start - 1;
453  }
454  while ( end * unit < maximum - ( 1e-07 * unit ) )
455  {
456  end = end + 1;
457  }
458  QgsDebugMsg( QString( "pretty classes: %1" ).arg( end ) );
459 
460  // If we don't have quite enough labels, extend the range out
461  // to make more (these labels are beyond the data :( )
462  int k = floor( 0.5 + end - start );
463  if ( k < minimumCount )
464  {
465  k = minimumCount - k;
466  if ( start >= 0 )
467  {
468  end = end + k / 2;
469  start = start - k / 2 + k % 2;
470  }
471  else
472  {
473  start = start - k / 2;
474  end = end + k / 2 + k % 2;
475  }
476  divisions = minimumCount;
477  }
478  else
479  {
480  divisions = k;
481  }
482  double minimumBreak = start * unit;
483  double maximumBreak = end * unit;
484  int count = ceil( maximumBreak - minimumBreak ) / unit;
485 
486  for ( int i = 1; i < count + 1; i++ )
487  {
488  breaks.append( minimumBreak + i * unit );
489  }
490 
491  if ( breaks.first() < minimum )
492  {
493  breaks[0] = minimum;
494  }
495  if ( breaks.last() > maximum )
496  {
497  breaks[breaks.count()-1] = maximum;
498  }
499  return breaks;
500 } // _calcPrettyBreaks
501 
502 
503 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<int> &labels )
504 {
505 
506  // C++ implementation of the standard deviation class interval algorithm
507  // as implemented in the 'classInt' package available for the R statistical
508  // prgramming language.
509 
510  // Returns breaks based on '_calcPrettyBreaks' of the centred and scaled
511  // values of 'values', and may have a number of classes different from 'classes'.
512 
513  double mean = 0.0;
514  double stdDev = 0.0;
515  int n = values.count();
516  double minimum = values[0];
517  double maximum = values[0];
518 
519  for ( int i = 0; i < n; i++ )
520  {
521  mean += values[i];
522  minimum = qMin( values[i], minimum ); // could use precomputed max and min
523  maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
524  }
525  mean = mean / ( double ) n;
526 
527  double sd = 0.0;
528  for ( int i = 0; i < n; i++ )
529  {
530  sd = values[i] - mean;
531  stdDev += sd * sd;
532  }
533  stdDev = sqrt( stdDev / n );
534 
535  QList<double> breaks = _calcPrettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
536  for ( int i = 0; i < breaks.count(); i++ )
537  {
538  labels.append(( int ) breaks[i] );
539  breaks[i] = ( breaks[i] * stdDev ) + mean;
540  }
541 
542  return breaks;
543 } // _calcStdDevBreaks
544 
545 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
546  double minimum, double maximum,
547  int maximumSize = 1000 )
548 {
549  // Jenks Optimal (Natural Breaks) algorithm
550  // Based on the Jenks algorithm from the 'classInt' package available for
551  // the R statistical prgramming language, and from Python code from here:
552  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
553  // and is based on a JAVA and Fortran code available here:
554  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
555 
556  // Returns class breaks such that classes are internally homogeneous while
557  // assuring heterogeneity among classes.
558 
559  if ( classes < 1 )
560  {
561  return QList<double>() << maximum;
562  }
563 
564  if ( classes >= values.size() )
565  {
566  return values;
567  }
568 
569  QVector<double> sample;
570 
571  // if we have lots of values, we need to take a random sample
572  if ( values.size() > maximumSize )
573  {
574  // for now, sample at least maximumSize values or a 10% sample, whichever
575  // is larger. This will produce a more representative sample for very large
576  // layers, but could end up being computationally intensive...
577 
578  qsrand( time( 0 ) );
579 
580  sample.resize( qMax( maximumSize, values.size() / 10 ) );
581 
582  QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
583  QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
584 
585  sample[ 0 ] = minimum;
586  sample[ 1 ] = maximum;;
587  for ( int i = 2; i < sample.size(); i++ )
588  {
589  // pick a random integer from 0 to n
590  double r = qrand();
591  int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
592  sample[ i ] = values[ j ];
593  }
594  }
595  else
596  {
597  sample = values.toVector();
598  }
599 
600  int n = sample.size();
601 
602  // sort the sample values
603  qSort( sample );
604 
605  QVector< QVector<int> > matrixOne( n + 1 );
606  QVector< QVector<double> > matrixTwo( n + 1 );
607 
608  for ( int i = 0; i <= n; i++ )
609  {
610  matrixOne[i].resize( classes + 1 );
611  matrixTwo[i].resize( classes + 1 );
612  }
613 
614  for ( int i = 1; i <= classes; i++ )
615  {
616  matrixOne[0][i] = 1;
617  matrixOne[1][i] = 1;
618  matrixTwo[0][i] = 0.0;
619  for ( int j = 2; j <= n; j++ )
620  {
621  matrixTwo[j][i] = std::numeric_limits<double>::max();
622  }
623  }
624 
625  for ( int l = 2; l <= n; l++ )
626  {
627  double s1 = 0.0;
628  double s2 = 0.0;
629  int w = 0;
630 
631  double v = 0.0;
632 
633  for ( int m = 1; m <= l; m++ )
634  {
635  int i3 = l - m + 1;
636 
637  double val = sample[ i3 - 1 ];
638 
639  s2 += val * val;
640  s1 += val;
641  w++;
642 
643  v = s2 - ( s1 * s1 ) / ( double ) w;
644  int i4 = i3 - 1;
645  if ( i4 != 0 )
646  {
647  for ( int j = 2; j <= classes; j++ )
648  {
649  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
650  {
651  matrixOne[l][j] = i4;
652  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
653  }
654  }
655  }
656  }
657  matrixOne[l][1] = 1;
658  matrixTwo[l][1] = v;
659  }
660 
661  QVector<double> breaks( classes );
662  breaks[classes-1] = sample[n-1];
663 
664  for ( int j = classes, k = n; j >= 2; j-- )
665  {
666  int id = matrixOne[k][j] - 1;
667  breaks[j - 2] = sample[id];
668  k = matrixOne[k][j] - 1;
669  }
670 
671  return breaks.toList();
672 } //_calcJenksBreaks
673 
675  QgsVectorLayer* vlayer,
676  QString attrName,
677  int classes,
678  Mode mode,
679  QgsSymbolV2* symbol,
680  QgsVectorColorRampV2* ramp )
681 {
682 
683  int attrNum = vlayer->fieldNameIndex( attrName );
684 
685  double minimum = vlayer->minimumValue( attrNum ).toDouble();
686  double maximum = vlayer->maximumValue( attrNum ).toDouble();
687  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
688 
689  QList<double> breaks;
690  QList<int> labels;
691  if ( mode == EqualInterval )
692  {
693  breaks = _calcEqualIntervalBreaks( minimum, maximum, classes );
694  }
695  else if ( mode == Pretty )
696  {
697  breaks = _calcPrettyBreaks( minimum, maximum, classes );
698  }
699  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
700  {
701  // get values from layer
702  QList<double> values;
703  QgsFeature f;
704  QgsAttributeList lst;
705  lst.append( attrNum );
706  vlayer->select( lst, QgsRectangle(), false );
707  while ( vlayer->nextFeature( f ) )
708  values.append( f.attributeMap()[attrNum].toDouble() );
709  // calculate the breaks
710  if ( mode == Quantile )
711  {
712  breaks = _calcQuantileBreaks( values, classes );
713  }
714  else if ( mode == Jenks )
715  {
716  breaks = _calcJenksBreaks( values, classes, minimum, maximum );
717  }
718  else if ( mode == StdDev )
719  {
720  breaks = _calcStdDevBreaks( values, classes, labels );
721  }
722  }
723  else
724  {
725  Q_ASSERT( false );
726  }
727 
729  double lower, upper = minimum;
730  QString label;
731 
732  // "breaks" list contains all values at class breaks plus maximum as last break
733  int i = 0;
734  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
735  {
736  lower = upper; // upper border from last interval
737  upper = *it;
738  if ( mode == StdDev )
739  {
740  if ( i == 0 )
741  {
742  label = "< " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
743  }
744  else if ( i == labels.count() - 1 )
745  {
746  label = ">= " + QString::number( labels[i-1], 'i', 0 ) + " Std Dev";
747  }
748  else
749  {
750  label = QString::number( labels[i-1], 'i', 0 ) + " Std Dev" + " - " + QString::number( labels[i], 'i', 0 ) + " Std Dev";
751  }
752  }
753  else
754  {
755  label = QString::number( lower, 'f', 4 ) + " - " + QString::number( upper, 'f', 4 );
756  }
757 
758  QgsSymbolV2* newSymbol = symbol->clone();
759  newSymbol->setColor( ramp->color(( double ) i / ( breaks.count() - 1 ) ) ); // color from (0 / cl-1) to (cl-1 / cl-1)
760 
761  ranges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
762  }
763 
764  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
765  r->setSourceSymbol( symbol->clone() );
766  r->setSourceColorRamp( ramp->clone() );
767  r->setMode( mode );
768  return r;
769 }
770 
772 {
773  QDomElement symbolsElem = element.firstChildElement( "symbols" );
774  if ( symbolsElem.isNull() )
775  return NULL;
776 
777  QDomElement rangesElem = element.firstChildElement( "ranges" );
778  if ( rangesElem.isNull() )
779  return NULL;
780 
781  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
783 
784  QDomElement rangeElem = rangesElem.firstChildElement();
785  while ( !rangeElem.isNull() )
786  {
787  if ( rangeElem.tagName() == "range" )
788  {
789  double lowerValue = rangeElem.attribute( "lower" ).toDouble();
790  double upperValue = rangeElem.attribute( "upper" ).toDouble();
791  QString symbolName = rangeElem.attribute( "symbol" );
792  QString label = rangeElem.attribute( "label" );
793  if ( symbolMap.contains( symbolName ) )
794  {
795  QgsSymbolV2* symbol = symbolMap.take( symbolName );
796  ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label ) );
797  }
798  }
799  rangeElem = rangeElem.nextSiblingElement();
800  }
801 
802  QString attrName = element.attribute( "attr" );
803 
804  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
805 
806  // delete symbols if there are any more
808 
809  // try to load source symbol (optional)
810  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
811  if ( !sourceSymbolElem.isNull() )
812  {
813  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
814  if ( sourceSymbolMap.contains( "0" ) )
815  {
816  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
817  }
818  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
819  }
820 
821  // try to load color ramp (optional)
822  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
823  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
824  {
825  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
826  }
827 
828  // try to load mode
829  QDomElement modeElem = element.firstChildElement( "mode" );
830  if ( !modeElem.isNull() )
831  {
832  QString modeString = modeElem.attribute( "name" );
833  if ( modeString == "equal" )
834  r->setMode( EqualInterval );
835  else if ( modeString == "quantile" )
836  r->setMode( Quantile );
837  else if ( modeString == "jenks" )
838  r->setMode( Jenks );
839  else if ( modeString == "stddev" )
840  r->setMode( StdDev );
841  else if ( modeString == "pretty" )
842  r->setMode( Pretty );
843  }
844 
845  QDomElement rotationElem = element.firstChildElement( "rotation" );
846  if ( !rotationElem.isNull() )
847  r->setRotationField( rotationElem.attribute( "field" ) );
848 
849  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
850  if ( !sizeScaleElem.isNull() )
851  r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
852 
853  // TODO: symbol levels
854  return r;
855 }
856 
857 QDomElement QgsGraduatedSymbolRendererV2::save( QDomDocument& doc )
858 {
859  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
860  rendererElem.setAttribute( "type", "graduatedSymbol" );
861  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
862  rendererElem.setAttribute( "attr", mAttrName );
863 
864  // ranges
865  int i = 0;
867  QDomElement rangesElem = doc.createElement( "ranges" );
868  QgsRangeList::const_iterator it = mRanges.constBegin();
869  for ( ; it != mRanges.end(); it++ )
870  {
871  const QgsRendererRangeV2& range = *it;
872  QString symbolName = QString::number( i );
873  symbols.insert( symbolName, range.symbol() );
874 
875  QDomElement rangeElem = doc.createElement( "range" );
876  rangeElem.setAttribute( "lower", range.lowerValue() );
877  rangeElem.setAttribute( "upper", range.upperValue() );
878  rangeElem.setAttribute( "symbol", symbolName );
879  rangeElem.setAttribute( "label", range.label() );
880  rangesElem.appendChild( rangeElem );
881  i++;
882  }
883 
884  rendererElem.appendChild( rangesElem );
885 
886  // save symbols
887  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
888  rendererElem.appendChild( symbolsElem );
889 
890  // save source symbol
891  if ( mSourceSymbol )
892  {
893  QgsSymbolV2Map sourceSymbols;
894  sourceSymbols.insert( "0", mSourceSymbol );
895  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
896  rendererElem.appendChild( sourceSymbolElem );
897  }
898 
899  // save source color ramp
900  if ( mSourceColorRamp )
901  {
902  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp, doc );
903  rendererElem.appendChild( colorRampElem );
904  }
905 
906  // save mode
907  QString modeString;
908  if ( mMode == EqualInterval )
909  modeString = "equal";
910  else if ( mMode == Quantile )
911  modeString = "quantile";
912  else if ( mMode == Jenks )
913  modeString = "jenks";
914  else if ( mMode == StdDev )
915  modeString = "stddev";
916  else if ( mMode == Pretty )
917  modeString = "pretty";
918  if ( !modeString.isEmpty() )
919  {
920  QDomElement modeElem = doc.createElement( "mode" );
921  modeElem.setAttribute( "name", modeString );
922  rendererElem.appendChild( modeElem );
923  }
924 
925  QDomElement rotationElem = doc.createElement( "rotation" );
926  rotationElem.setAttribute( "field", mRotationField );
927  rendererElem.appendChild( rotationElem );
928 
929  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
930  sizeScaleElem.setAttribute( "field", mSizeScaleField );
931  rendererElem.appendChild( sizeScaleElem );
932 
933  return rendererElem;
934 }
935 
937 {
938  QSettings settings;
939  bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
940 
942  if ( showClassifiers )
943  {
944  lst << qMakePair( classAttribute(), QPixmap() );
945  }
946 
947  int count = ranges().count();
948  for ( int i = 0; i < count; i++ )
949  {
950  const QgsRendererRangeV2& range = ranges()[i];
951  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
952  lst << qMakePair( range.label(), pix );
953  }
954  return lst;
955 }
956 
958 {
959  QSettings settings;
960  bool showClassifiers = settings.value( "/qgis/showLegendClassifiers", false ).toBool();
961 
963  if ( showClassifiers )
964  {
965  lst << qMakePair( classAttribute(), ( QgsSymbolV2* )0 );
966  }
967 
968  QgsRangeList::const_iterator rangeIt = mRanges.constBegin();
969  for ( ; rangeIt != mRanges.constEnd(); ++rangeIt )
970  {
971  lst << qMakePair( rangeIt->label(), rangeIt->symbol() );
972  }
973  return lst;
974 }
975 
977 {
978  return mSourceSymbol;
979 }
981 {
982  delete mSourceSymbol;
983  mSourceSymbol = sym;
984 }
985 
987 {
988  return mSourceColorRamp;
989 }
991 {
992  delete mSourceColorRamp;
993  mSourceColorRamp = ramp;
994 }
995 
997 {
998  QgsSymbolV2* newSymbol = symbol->clone();
999  QString label = "0.0 - 0.0";
1000  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1001 
1002 }
1003 
1005 {
1006  mRanges.removeAt( idx );
1007 }