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