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  , mAttrNum( -1 )
290  , mCounting( false )
291 
292 {
293  // TODO: check ranges for sanity (NULL symbols, invalid ranges)
294 }
295 
297 {
298  mRanges.clear(); // should delete all the symbols
299 }
300 
302 {
303  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
304  {
305  if ( it->lowerValue() <= value && it->upperValue() >= value )
306  {
307  if ( it->renderState() || mCounting )
308  return it->symbol();
309  else
310  return NULL;
311  }
312  }
313  // the value is out of the range: return NULL instead of symbol
314  return NULL;
315 }
316 
318 {
319  QgsSymbolV2* symbol = originalSymbolForFeature( feature );
320  if ( symbol == NULL )
321  return NULL;
322 
323  if ( !mRotation.data() && !mSizeScale.data() )
324  return symbol; // no data-defined rotation/scaling - just return the symbol
325 
326  // find out rotation, size scale
327  const double rotation = mRotation.data() ? mRotation->evaluate( feature ).toDouble() : 0;
328  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( feature ).toDouble() : 1.;
329 
330  // take a temporary symbol (or create it if doesn't exist)
331  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
332 
333  // modify the temporary symbol and return it
334  if ( tempSymbol->type() == QgsSymbolV2::Marker )
335  {
336  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
337  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
338  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
339  markerSymbol->setScaleMethod( mScaleMethod );
340  }
341  else if ( tempSymbol->type() == QgsSymbolV2::Line )
342  {
343  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
344  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
345  }
346  return tempSymbol;
347 }
348 
350 {
351  const QgsAttributes& attrs = feature.attributes();
352  QVariant value;
353  if ( mAttrNum < 0 || mAttrNum >= attrs.count() )
354  {
355  value = mExpression->evaluate( &feature );
356  }
357  else
358  {
359  value = attrs[mAttrNum];
360  }
361 
362  // Null values should not be categorized
363  if ( value.isNull() )
364  return NULL;
365 
366  // find the right category
367  return symbolForValue( value.toDouble() );
368 }
369 
371 {
372  mCounting = context.rendererScale() == 0.0;
373 
374  // find out classification attribute index from name
375  mAttrNum = fields.fieldNameIndex( mAttrName );
376 
377  if ( mAttrNum == -1 )
378  {
379  mExpression.reset( new QgsExpression( mAttrName ) );
380  mExpression->prepare( fields );
381  }
382 
383  QgsRangeList::iterator it = mRanges.begin();
384  for ( ; it != mRanges.end(); ++it )
385  {
386  if ( !it->symbol() )
387  continue;
388 
389  it->symbol()->startRender( context, &fields );
390 
391  if ( mRotation.data() || mSizeScale.data() )
392  {
393  QgsSymbolV2* tempSymbol = it->symbol()->clone();
394  tempSymbol->setRenderHints(( mRotation.data() ? QgsSymbolV2::DataDefinedRotation : 0 ) |
396  tempSymbol->startRender( context, &fields );
397  mTempSymbols[ it->symbol()] = tempSymbol;
398  }
399  }
400 }
401 
403 {
404  QgsRangeList::iterator it = mRanges.begin();
405  for ( ; it != mRanges.end(); ++it )
406  {
407  if ( !it->symbol() )
408  continue;
409 
410  it->symbol()->stopRender( context );
411  }
412 
413  // cleanup mTempSymbols
414  QHash<QgsSymbolV2*, QgsSymbolV2*>::iterator it2 = mTempSymbols.begin();
415  for ( ; it2 != mTempSymbols.end(); ++it2 )
416  {
417  it2.value()->stopRender( context );
418  delete it2.value();
419  }
420  mTempSymbols.clear();
421 }
422 
424 {
425  QSet<QString> attributes;
426 
427  // mAttrName can contain either attribute name or an expression.
428  // Sometimes it is not possible to distinguish between those two,
429  // e.g. "a - b" can be both a valid attribute name or expression.
430  // Since we do not have access to fields here, try both options.
431  attributes << mAttrName;
432 
433  QgsExpression testExpr( mAttrName );
434  if ( !testExpr.hasParserError() )
435  attributes.unite( testExpr.referencedColumns().toSet() );
436 
437  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
438  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
439 
440  QgsRangeList::const_iterator range_it = mRanges.constBegin();
441  for ( ; range_it != mRanges.constEnd(); ++range_it )
442  {
443  QgsSymbolV2* symbol = range_it->symbol();
444  if ( symbol )
445  {
446  attributes.unite( symbol->usedAttributes() );
447  }
448  }
449  return attributes.toList();
450 }
451 
453 {
454  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
455  return false;
456  mRanges[rangeIndex].setSymbol( symbol );
457  return true;
458 }
459 
460 bool QgsGraduatedSymbolRendererV2::updateRangeLabel( int rangeIndex, QString label )
461 {
462  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
463  return false;
464  mRanges[rangeIndex].setLabel( label );
465  return true;
466 }
467 
468 bool QgsGraduatedSymbolRendererV2::updateRangeUpperValue( int rangeIndex, double value )
469 {
470  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
471  return false;
472  QgsRendererRangeV2 &range = mRanges[rangeIndex];
473  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
474  range.setUpperValue( value );
475  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
476  return true;
477 }
478 
479 bool QgsGraduatedSymbolRendererV2::updateRangeLowerValue( int rangeIndex, double value )
480 {
481  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
482  return false;
483  QgsRendererRangeV2 &range = mRanges[rangeIndex];
484  bool isDefaultLabel = range.label() == mLabelFormat.labelForRange( range );
485  range.setLowerValue( value );
486  if ( isDefaultLabel ) range.setLabel( mLabelFormat.labelForRange( range ) );
487  return true;
488 }
489 
491 {
492  if ( rangeIndex < 0 || rangeIndex >= mRanges.size() )
493  return false;
494  mRanges[rangeIndex].setRenderState( value );
495  return true;
496 }
497 
499 {
500  QString s = QString( "GRADUATED: attr %1\n" ).arg( mAttrName );
501  for ( int i = 0; i < mRanges.count(); i++ )
502  s += mRanges[i].dump();
503  return s;
504 }
505 
507 {
509  r->setMode( mMode );
510  if ( mSourceSymbol.data() )
511  r->setSourceSymbol( mSourceSymbol->clone() );
512  if ( mSourceColorRamp.data() )
513  {
514  r->setSourceColorRamp( mSourceColorRamp->clone() );
516  }
520  r->setScaleMethod( scaleMethod() );
521  r->setLabelFormat( labelFormat() );
522  copyPaintEffect( r );
523  return r;
524 }
525 
526 void QgsGraduatedSymbolRendererV2::toSld( QDomDocument& doc, QDomElement &element ) const
527 {
528  QgsStringMap props;
529  props[ "attribute" ] = mAttrName;
530  if ( mRotation.data() )
531  props[ "angle" ] = mRotation->expression();
532  if ( mSizeScale.data() )
533  props[ "scale" ] = mSizeScale->expression();
534 
535  // create a Rule for each range
536  for ( QgsRangeList::const_iterator it = mRanges.constBegin(); it != mRanges.constEnd(); ++it )
537  {
538  QgsStringMap catProps( props );
539  it->toSld( doc, element, catProps );
540  }
541 }
542 
544 {
545  QgsSymbolV2List lst;
546  for ( int i = 0; i < mRanges.count(); i++ )
547  lst.append( mRanges[i].symbol() );
548  return lst;
549 }
550 
551 static QList<double> _calcEqualIntervalBreaks( double minimum, double maximum, int classes )
552 {
553 
554  // Equal interval algorithm
555  //
556  // Returns breaks based on dividing the range ('minimum' to 'maximum')
557  // into 'classes' parts.
558 
559  double step = ( maximum - minimum ) / classes;
560 
561  QList<double> breaks;
562  double value = minimum;
563  for ( int i = 0; i < classes; i++ )
564  {
565  value += step;
566  breaks.append( value );
567  }
568 
569  // floating point arithmetics is not precise:
570  // set the last break to be exactly maximum so we do not miss it
571  breaks[classes-1] = maximum;
572 
573  return breaks;
574 }
575 
576 static QList<double> _calcQuantileBreaks( QList<double> values, int classes )
577 {
578  // q-th quantile of a data set:
579  // value where q fraction of data is below and (1-q) fraction is above this value
580  // Xq = (1 - r) * X_NI1 + r * X_NI2
581  // NI1 = (int) (q * (n+1))
582  // NI2 = NI1 + 1
583  // r = q * (n+1) - (int) (q * (n+1))
584  // (indices of X: 1...n)
585 
586  // sort the values first
587  qSort( values );
588 
589  QList<double> breaks;
590 
591  // If there are no values to process: bail out
592  if ( !values.count() )
593  return breaks;
594 
595  int n = values.count();
596  double Xq = n > 0 ? values[0] : 0.0;
597 
598  for ( int i = 1; i < classes; i++ )
599  {
600  if ( n > 1 )
601  {
602  double q = i / ( double ) classes;
603  double a = q * ( n - 1 );
604  int aa = ( int )( a );
605 
606  double r = a - aa;
607  Xq = ( 1 - r ) * values[aa] + r * values[aa+1];
608  }
609  breaks.append( Xq );
610  }
611 
612  breaks.append( values[ n-1 ] );
613 
614  return breaks;
615 }
616 
617 static QList<double> _calcPrettyBreaks( double minimum, double maximum, int classes )
618 {
619 
620  // C++ implementation of R's pretty algorithm
621  // Based on code for determining optimal tick placement for statistical graphics
622  // from the R statistical programming language.
623  // Code ported from R implementation from 'labeling' R package
624  //
625  // Computes a sequence of about 'classes' equally spaced round values
626  // which cover the range of values from 'minimum' to 'maximum'.
627  // The values are chosen so that they are 1, 2 or 5 times a power of 10.
628 
629  QList<double> breaks;
630  if ( classes < 1 )
631  {
632  breaks.append( maximum );
633  return breaks;
634  }
635 
636  int minimumCount = ( int ) classes / 3;
637  double shrink = 0.75;
638  double highBias = 1.5;
639  double adjustBias = 0.5 + 1.5 * highBias;
640  int divisions = classes;
641  double h = highBias;
642  double cell;
643  int U;
644  bool small = false;
645  double dx = maximum - minimum;
646 
647  if ( dx == 0 && maximum == 0 )
648  {
649  cell = 1.0;
650  small = true;
651  U = 1;
652  }
653  else
654  {
655  cell = qMax( qAbs( minimum ), qAbs( maximum ) );
656  if ( adjustBias >= 1.5 * h + 0.5 )
657  {
658  U = 1 + ( 1.0 / ( 1 + h ) );
659  }
660  else
661  {
662  U = 1 + ( 1.5 / ( 1 + adjustBias ) );
663  }
664  small = dx < ( cell * U * qMax( 1, divisions ) * 1e-07 * 3.0 );
665  }
666 
667  if ( small )
668  {
669  if ( cell > 10 )
670  {
671  cell = 9 + cell / 10;
672  cell = cell * shrink;
673  }
674  if ( minimumCount > 1 )
675  {
676  cell = cell / minimumCount;
677  }
678  }
679  else
680  {
681  cell = dx;
682  if ( divisions > 1 )
683  {
684  cell = cell / divisions;
685  }
686  }
687  if ( cell < 20 * 1e-07 )
688  {
689  cell = 20 * 1e-07;
690  }
691 
692  double base = pow( 10.0, floor( log10( cell ) ) );
693  double unit = base;
694  if (( 2 * base ) - cell < h *( cell - unit ) )
695  {
696  unit = 2.0 * base;
697  if (( 5 * base ) - cell < adjustBias *( cell - unit ) )
698  {
699  unit = 5.0 * base;
700  if (( 10.0 * base ) - cell < h *( cell - unit ) )
701  {
702  unit = 10.0 * base;
703  }
704  }
705  }
706  // Maybe used to correct for the epsilon here??
707  int start = floor( minimum / unit + 1e-07 );
708  int end = ceil( maximum / unit - 1e-07 );
709 
710  // Extend the range out beyond the data. Does this ever happen??
711  while ( start * unit > minimum + ( 1e-07 * unit ) )
712  {
713  start = start - 1;
714  }
715  while ( end * unit < maximum - ( 1e-07 * unit ) )
716  {
717  end = end + 1;
718  }
719  QgsDebugMsg( QString( "pretty classes: %1" ).arg( end ) );
720 
721  // If we don't have quite enough labels, extend the range out
722  // to make more (these labels are beyond the data :( )
723  int k = floor( 0.5 + end - start );
724  if ( k < minimumCount )
725  {
726  k = minimumCount - k;
727  if ( start >= 0 )
728  {
729  end = end + k / 2;
730  start = start - k / 2 + k % 2;
731  }
732  else
733  {
734  start = start - k / 2;
735  end = end + k / 2 + k % 2;
736  }
737  }
738  double minimumBreak = start * unit;
739  //double maximumBreak = end * unit;
740  int count = end - start;
741 
742  for ( int i = 1; i < count + 1; i++ )
743  {
744  breaks.append( minimumBreak + i * unit );
745  }
746 
747  if ( breaks.isEmpty() )
748  return breaks;
749 
750  if ( breaks.first() < minimum )
751  {
752  breaks[0] = minimum;
753  }
754  if ( breaks.last() > maximum )
755  {
756  breaks[breaks.count()-1] = maximum;
757  }
758 
759  return breaks;
760 } // _calcPrettyBreaks
761 
762 
763 static QList<double> _calcStdDevBreaks( QList<double> values, int classes, QList<double> &labels )
764 {
765 
766  // C++ implementation of the standard deviation class interval algorithm
767  // as implemented in the 'classInt' package available for the R statistical
768  // prgramming language.
769 
770  // Returns breaks based on '_calcPrettyBreaks' of the centred and scaled
771  // values of 'values', and may have a number of classes different from 'classes'.
772 
773  // If there are no values to process: bail out
774  if ( !values.count() )
775  return QList<double>();
776 
777  double mean = 0.0;
778  double stdDev = 0.0;
779  int n = values.count();
780  double minimum = values[0];
781  double maximum = values[0];
782 
783  for ( int i = 0; i < n; i++ )
784  {
785  mean += values[i];
786  minimum = qMin( values[i], minimum ); // could use precomputed max and min
787  maximum = qMax( values[i], maximum ); // but have to go through entire list anyway
788  }
789  mean = mean / ( double ) n;
790 
791  double sd = 0.0;
792  for ( int i = 0; i < n; i++ )
793  {
794  sd = values[i] - mean;
795  stdDev += sd * sd;
796  }
797  stdDev = sqrt( stdDev / n );
798 
799  QList<double> breaks = _calcPrettyBreaks(( minimum - mean ) / stdDev, ( maximum - mean ) / stdDev, classes );
800  for ( int i = 0; i < breaks.count(); i++ )
801  {
802  labels.append( breaks[i] );
803  breaks[i] = ( breaks[i] * stdDev ) + mean;
804  }
805 
806  return breaks;
807 } // _calcStdDevBreaks
808 
809 static QList<double> _calcJenksBreaks( QList<double> values, int classes,
810  double minimum, double maximum,
811  int maximumSize = 1000 )
812 {
813  // Jenks Optimal (Natural Breaks) algorithm
814  // Based on the Jenks algorithm from the 'classInt' package available for
815  // the R statistical prgramming language, and from Python code from here:
816  // http://danieljlewis.org/2010/06/07/jenks-natural-breaks-algorithm-in-python/
817  // and is based on a JAVA and Fortran code available here:
818  // https://stat.ethz.ch/pipermail/r-sig-geo/2006-March/000811.html
819 
820  // Returns class breaks such that classes are internally homogeneous while
821  // assuring heterogeneity among classes.
822 
823  if ( !values.count() )
824  return QList<double>();
825 
826  if ( classes <= 1 )
827  {
828  return QList<double>() << maximum;
829  }
830 
831  if ( classes >= values.size() )
832  {
833  return values;
834  }
835 
836  QVector<double> sample;
837 
838  // if we have lots of values, we need to take a random sample
839  if ( values.size() > maximumSize )
840  {
841  // for now, sample at least maximumSize values or a 10% sample, whichever
842  // is larger. This will produce a more representative sample for very large
843  // layers, but could end up being computationally intensive...
844 
845  sample.resize( qMax( maximumSize, values.size() / 10 ) );
846 
847  QgsDebugMsg( QString( "natural breaks (jenks) sample size: %1" ).arg( sample.size() ) );
848  QgsDebugMsg( QString( "values:%1" ).arg( values.size() ) );
849 
850  sample[ 0 ] = minimum;
851  sample[ 1 ] = maximum;;
852  for ( int i = 2; i < sample.size(); i++ )
853  {
854  // pick a random integer from 0 to n
855  double r = qrand();
856  int j = floor( r / RAND_MAX * ( values.size() - 1 ) );
857  sample[ i ] = values[ j ];
858  }
859  }
860  else
861  {
862  sample = values.toVector();
863  }
864 
865  int n = sample.size();
866 
867  // sort the sample values
868  qSort( sample );
869 
870  QVector< QVector<int> > matrixOne( n + 1 );
871  QVector< QVector<double> > matrixTwo( n + 1 );
872 
873  for ( int i = 0; i <= n; i++ )
874  {
875  matrixOne[i].resize( classes + 1 );
876  matrixTwo[i].resize( classes + 1 );
877  }
878 
879  for ( int i = 1; i <= classes; i++ )
880  {
881  matrixOne[0][i] = 1;
882  matrixOne[1][i] = 1;
883  matrixTwo[0][i] = 0.0;
884  for ( int j = 2; j <= n; j++ )
885  {
886  matrixTwo[j][i] = std::numeric_limits<double>::max();
887  }
888  }
889 
890  for ( int l = 2; l <= n; l++ )
891  {
892  double s1 = 0.0;
893  double s2 = 0.0;
894  int w = 0;
895 
896  double v = 0.0;
897 
898  for ( int m = 1; m <= l; m++ )
899  {
900  int i3 = l - m + 1;
901 
902  double val = sample[ i3 - 1 ];
903 
904  s2 += val * val;
905  s1 += val;
906  w++;
907 
908  v = s2 - ( s1 * s1 ) / ( double ) w;
909  int i4 = i3 - 1;
910  if ( i4 != 0 )
911  {
912  for ( int j = 2; j <= classes; j++ )
913  {
914  if ( matrixTwo[l][j] >= v + matrixTwo[i4][j - 1] )
915  {
916  matrixOne[l][j] = i4;
917  matrixTwo[l][j] = v + matrixTwo[i4][j - 1];
918  }
919  }
920  }
921  }
922  matrixOne[l][1] = 1;
923  matrixTwo[l][1] = v;
924  }
925 
926  QVector<double> breaks( classes );
927  breaks[classes-1] = sample[n-1];
928 
929  for ( int j = classes, k = n; j >= 2; j-- )
930  {
931  int id = matrixOne[k][j] - 1;
932  breaks[j - 2] = sample[id];
933  k = matrixOne[k][j] - 1;
934  }
935 
936  return breaks.toList();
937 } //_calcJenksBreaks
938 
939 
941  QgsVectorLayer* vlayer,
942  QString attrName,
943  int classes,
944  Mode mode,
945  QgsSymbolV2* symbol,
946  QgsVectorColorRampV2* ramp,
947  bool inverted,
949 )
950 {
952  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
953  r->setSourceSymbol( symbol->clone() );
954  r->setSourceColorRamp( ramp->clone() );
955  r->setInvertedColorRamp( inverted );
956  r->setMode( mode );
957  r->setLabelFormat( labelFormat );
958  r->updateClasses( vlayer, mode, classes );
959  return r;
960 }
961 
963 {
964  QList<double> values;
965  QScopedPointer<QgsExpression> expression;
966  int attrNum = vlayer->fieldNameIndex( mAttrName );
967 
968  if ( attrNum == -1 )
969  {
970  // try to use expression
971  expression.reset( new QgsExpression( mAttrName ) );
972  if ( expression->hasParserError() || !expression->prepare( vlayer->pendingFields() ) )
973  return values; // should have a means to report errors
974  }
975 
976  QgsFeature f;
977  QStringList lst;
978  if ( expression.isNull() )
979  lst.append( mAttrName );
980  else
981  lst = expression->referencedColumns();
982 
984  .setFlags(( expression && expression->needsGeometry() ) ?
987  .setSubsetOfAttributes( lst, vlayer->pendingFields() ) );
988 
989  // create list of non-null attribute values
990  while ( fit.nextFeature( f ) )
991  {
992  QVariant v = expression ? expression->evaluate( f ) : f.attribute( attrNum );
993  if ( !v.isNull() )
994  values.append( v.toDouble() );
995  }
996  return values;
997 }
998 
1000 {
1001  if ( mAttrName.isEmpty() )
1002  return;
1003 
1004  setMode( mode );
1005  // Custom classes are not recalculated
1006  if ( mode == Custom )
1007  return;
1008 
1009  if ( nclasses < 1 )
1010  nclasses = 1;
1011 
1012  QList<double> values;
1013  bool valuesLoaded = false;
1014  double minimum;
1015  double maximum;
1016 
1017  int attrNum = vlayer->fieldNameIndex( mAttrName );
1018 
1019  if ( attrNum == -1 )
1020  {
1021  values = getDataValues( vlayer );
1022  if ( values.isEmpty() )
1023  return;
1024 
1025  qSort( values );
1026  minimum = values.first();
1027  maximum = values.last();
1028  valuesLoaded = true;
1029  }
1030  else
1031  {
1032  minimum = vlayer->minimumValue( attrNum ).toDouble();
1033  maximum = vlayer->maximumValue( attrNum ).toDouble();
1034  }
1035 
1036  QgsDebugMsg( QString( "min %1 // max %2" ).arg( minimum ).arg( maximum ) );
1037  QList<double> breaks;
1038  QList<double> labels;
1039  if ( mode == EqualInterval )
1040  {
1041  breaks = _calcEqualIntervalBreaks( minimum, maximum, nclasses );
1042  }
1043  else if ( mode == Pretty )
1044  {
1045  breaks = _calcPrettyBreaks( minimum, maximum, nclasses );
1046  }
1047  else if ( mode == Quantile || mode == Jenks || mode == StdDev )
1048  {
1049  // get values from layer
1050  if ( !valuesLoaded )
1051  {
1052  values = getDataValues( vlayer );
1053  }
1054 
1055  // calculate the breaks
1056  if ( mode == Quantile )
1057  {
1058  breaks = _calcQuantileBreaks( values, nclasses );
1059  }
1060  else if ( mode == Jenks )
1061  {
1062  breaks = _calcJenksBreaks( values, nclasses, minimum, maximum );
1063  }
1064  else if ( mode == StdDev )
1065  {
1066  breaks = _calcStdDevBreaks( values, nclasses, labels );
1067  }
1068  }
1069  else
1070  {
1071  Q_ASSERT( false );
1072  }
1073 
1074  double lower, upper = minimum;
1075  QString label;
1076  deleteAllClasses();
1077 
1078  // "breaks" list contains all values at class breaks plus maximum as last break
1079 
1080  int i = 0;
1081  for ( QList<double>::iterator it = breaks.begin(); it != breaks.end(); ++it, ++i )
1082  {
1083  lower = upper; // upper border from last interval
1084  upper = *it;
1085 
1086  // Label - either StdDev label or default label for a range
1087  if ( mode == StdDev )
1088  {
1089  if ( i == 0 )
1090  {
1091  label = "< " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
1092  }
1093  else if ( i == labels.count() - 1 )
1094  {
1095  label = ">= " + QString::number( labels[i-1], 'f', 2 ) + " Std Dev";
1096  }
1097  else
1098  {
1099  label = QString::number( labels[i-1], 'f', 2 ) + " Std Dev" + " - " + QString::number( labels[i], 'f', 2 ) + " Std Dev";
1100  }
1101  }
1102  else
1103  {
1104  label = mLabelFormat.labelForRange( lower, upper );
1105  }
1106  QgsSymbolV2* newSymbol = mSourceSymbol ? mSourceSymbol->clone() : QgsSymbolV2::defaultSymbol( vlayer->geometryType() );
1107  addClass( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
1108  }
1110 }
1111 
1112 
1114 {
1115  QDomElement symbolsElem = element.firstChildElement( "symbols" );
1116  if ( symbolsElem.isNull() )
1117  return NULL;
1118 
1119  QDomElement rangesElem = element.firstChildElement( "ranges" );
1120  if ( rangesElem.isNull() )
1121  return NULL;
1122 
1123  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
1125 
1126  QDomElement rangeElem = rangesElem.firstChildElement();
1127  while ( !rangeElem.isNull() )
1128  {
1129  if ( rangeElem.tagName() == "range" )
1130  {
1131  double lowerValue = rangeElem.attribute( "lower" ).toDouble();
1132  double upperValue = rangeElem.attribute( "upper" ).toDouble();
1133  QString symbolName = rangeElem.attribute( "symbol" );
1134  QString label = rangeElem.attribute( "label" );
1135  bool render = rangeElem.attribute( "render", "true" ) != "false";
1136  if ( symbolMap.contains( symbolName ) )
1137  {
1138  QgsSymbolV2* symbol = symbolMap.take( symbolName );
1139  ranges.append( QgsRendererRangeV2( lowerValue, upperValue, symbol, label, render ) );
1140  }
1141  }
1142  rangeElem = rangeElem.nextSiblingElement();
1143  }
1144 
1145  QString attrName = element.attribute( "attr" );
1146 
1147  QgsGraduatedSymbolRendererV2* r = new QgsGraduatedSymbolRendererV2( attrName, ranges );
1148 
1149  // delete symbols if there are any more
1151 
1152  // try to load source symbol (optional)
1153  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
1154  if ( !sourceSymbolElem.isNull() )
1155  {
1156  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
1157  if ( sourceSymbolMap.contains( "0" ) )
1158  {
1159  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
1160  }
1161  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
1162  }
1163 
1164  // try to load color ramp (optional)
1165  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
1166  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
1167  {
1168  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
1169  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
1170  if ( !invertedColorRampElem.isNull() )
1171  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
1172  }
1173 
1174  // try to load mode
1175  QDomElement modeElem = element.firstChildElement( "mode" );
1176  if ( !modeElem.isNull() )
1177  {
1178  QString modeString = modeElem.attribute( "name" );
1179  if ( modeString == "equal" )
1180  r->setMode( EqualInterval );
1181  else if ( modeString == "quantile" )
1182  r->setMode( Quantile );
1183  else if ( modeString == "jenks" )
1184  r->setMode( Jenks );
1185  else if ( modeString == "stddev" )
1186  r->setMode( StdDev );
1187  else if ( modeString == "pretty" )
1188  r->setMode( Pretty );
1189  }
1190 
1191  QDomElement rotationElem = element.firstChildElement( "rotation" );
1192  if ( !rotationElem.isNull() )
1193  r->setRotationField( rotationElem.attribute( "field" ) );
1194 
1195  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
1196  if ( !sizeScaleElem.isNull() )
1197  r->setSizeScaleField( sizeScaleElem.attribute( "field" ) );
1198  r->setScaleMethod( QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ) );
1199 
1200  QDomElement labelFormatElem = element.firstChildElement( "labelformat" );
1201  if ( ! labelFormatElem.isNull() )
1202  {
1204  labelFormat.setFromDomElement( labelFormatElem );
1205  r->setLabelFormat( labelFormat );
1206  }
1207  // TODO: symbol levels
1208  return r;
1209 }
1210 
1211 QDomElement QgsGraduatedSymbolRendererV2::save( QDomDocument& doc )
1212 {
1213  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1214  rendererElem.setAttribute( "type", "graduatedSymbol" );
1215  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
1216  rendererElem.setAttribute( "attr", mAttrName );
1217 
1218  // ranges
1219  int i = 0;
1221  QDomElement rangesElem = doc.createElement( "ranges" );
1222  QgsRangeList::const_iterator it = mRanges.constBegin();
1223  for ( ; it != mRanges.constEnd(); ++it )
1224  {
1225  const QgsRendererRangeV2& range = *it;
1226  QString symbolName = QString::number( i );
1227  symbols.insert( symbolName, range.symbol() );
1228 
1229  QDomElement rangeElem = doc.createElement( "range" );
1230  rangeElem.setAttribute( "lower", QString::number( range.lowerValue(), 'f' ) );
1231  rangeElem.setAttribute( "upper", QString::number( range.upperValue(), 'f' ) );
1232  rangeElem.setAttribute( "symbol", symbolName );
1233  rangeElem.setAttribute( "label", range.label() );
1234  rangeElem.setAttribute( "render", range.renderState() ? "true" : "false" );
1235  rangesElem.appendChild( rangeElem );
1236  i++;
1237  }
1238 
1239  rendererElem.appendChild( rangesElem );
1240 
1241  // save symbols
1242  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
1243  rendererElem.appendChild( symbolsElem );
1244 
1245  // save source symbol
1246  if ( mSourceSymbol.data() )
1247  {
1248  QgsSymbolV2Map sourceSymbols;
1249  sourceSymbols.insert( "0", mSourceSymbol.data() );
1250  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
1251  rendererElem.appendChild( sourceSymbolElem );
1252  }
1253 
1254  // save source color ramp
1255  if ( mSourceColorRamp.data() )
1256  {
1257  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
1258  rendererElem.appendChild( colorRampElem );
1259  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
1260  invertedElem.setAttribute( "value", mInvertedColorRamp );
1261  rendererElem.appendChild( invertedElem );
1262  }
1263 
1264  // save mode
1265  QString modeString;
1266  if ( mMode == EqualInterval )
1267  modeString = "equal";
1268  else if ( mMode == Quantile )
1269  modeString = "quantile";
1270  else if ( mMode == Jenks )
1271  modeString = "jenks";
1272  else if ( mMode == StdDev )
1273  modeString = "stddev";
1274  else if ( mMode == Pretty )
1275  modeString = "pretty";
1276  if ( !modeString.isEmpty() )
1277  {
1278  QDomElement modeElem = doc.createElement( "mode" );
1279  modeElem.setAttribute( "name", modeString );
1280  rendererElem.appendChild( modeElem );
1281  }
1282 
1283  QDomElement rotationElem = doc.createElement( "rotation" );
1284  if ( mRotation.data() )
1285  rotationElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) );
1286  rendererElem.appendChild( rotationElem );
1287 
1288  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
1289  if ( mSizeScale.data() )
1290  sizeScaleElem.setAttribute( "field", QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mSizeScale.data() ) );
1291  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
1292  rendererElem.appendChild( sizeScaleElem );
1293 
1294  QDomElement labelFormatElem = doc.createElement( "labelformat" );
1295  mLabelFormat.saveToDomElement( labelFormatElem );
1296  rendererElem.appendChild( labelFormatElem );
1297 
1298  if ( mPaintEffect )
1299  mPaintEffect->saveProperties( doc, rendererElem );
1300 
1301  return rendererElem;
1302 }
1303 
1305 {
1307  int count = ranges().count();
1308  for ( int i = 0; i < count; i++ )
1309  {
1310  const QgsRendererRangeV2& range = ranges()[i];
1311  QPixmap pix = QgsSymbolLayerV2Utils::symbolPreviewPixmap( range.symbol(), iconSize );
1312  lst << qMakePair( range.label(), pix );
1313  }
1314  return lst;
1315 }
1316 
1318 {
1319  Q_UNUSED( scaleDenominator );
1320  QgsLegendSymbolList lst;
1321 
1322  foreach ( const QgsRendererRangeV2& range, mRanges )
1323  {
1324  if ( rule.isEmpty() || range.label() == rule )
1325  {
1326  lst << qMakePair( range.label(), range.symbol() );
1327  }
1328  }
1329  return lst;
1330 }
1331 
1333 {
1334  return mSourceSymbol.data();
1335 }
1337 {
1338  mSourceSymbol.reset( sym );
1339 }
1340 
1342 {
1343  return mSourceColorRamp.data();
1344 }
1345 
1347 {
1348  mSourceColorRamp.reset( ramp );
1349 }
1350 
1352 {
1353  int i = 0;
1354  if ( ramp )
1355  {
1356  setSourceColorRamp( ramp );
1357  setInvertedColorRamp( inverted );
1358  }
1359 
1360  if ( mSourceColorRamp )
1361  {
1362  foreach ( QgsRendererRangeV2 range, mRanges )
1363  {
1364  QgsSymbolV2 *symbol = range.symbol() ? range.symbol()->clone() : 0;
1365  if ( symbol )
1366  {
1367  double colorValue;
1368  if ( inverted )
1369  colorValue = ( mRanges.count() > 1 ? ( double )( mRanges.count() - i - 1 ) / ( mRanges.count() - 1 ) : 0 );
1370  else
1371  colorValue = ( mRanges.count() > 1 ? ( double ) i / ( mRanges.count() - 1 ) : 0 );
1372  symbol->setColor( mSourceColorRamp->color( colorValue ) );
1373  }
1374  updateRangeSymbol( i, symbol );
1375  ++i;
1376  }
1377  }
1378 
1379 }
1380 
1382 {
1383  if ( !sym )
1384  return;
1385 
1386  int i = 0;
1387  foreach ( QgsRendererRangeV2 range, mRanges )
1388  {
1389  QgsSymbolV2 *symbol = sym->clone();
1390  symbol->setColor( range.symbol()->color() );
1391  updateRangeSymbol( i, symbol );
1392  ++i;
1393  }
1394  setSourceSymbol( sym->clone() );
1395 }
1396 
1397 void QgsGraduatedSymbolRendererV2::setRotationField( QString fieldOrExpression )
1398 {
1400 }
1401 
1403 {
1404  return mRotation.data() ? QgsSymbolLayerV2Utils::fieldOrExpressionFromExpression( mRotation.data() ) : QString();
1405 }
1406 
1407 void QgsGraduatedSymbolRendererV2::setSizeScaleField( QString fieldOrExpression )
1408 {
1410 }
1411 
1413 {
1415 }
1416 
1418 {
1420  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1421  {
1422  if ( it->symbol() )
1423  setScaleMethodToSymbol( it->symbol(), scaleMethod );
1424  }
1425 }
1426 
1428 {
1429  return true;
1430 }
1431 
1433 {
1434  bool ok;
1435  int index = key.toInt( &ok );
1436  if ( ok && index >= 0 && index < mRanges.size() )
1437  return mRanges[ index ].renderState();
1438  else
1439  return true;
1440 }
1441 
1443 {
1444  bool ok;
1445  int index = key.toInt( &ok );
1446  if ( ok )
1447  updateRangeRenderState( index, state );
1448 }
1449 
1450 
1452 {
1453  QgsSymbolV2* newSymbol = symbol->clone();
1454  QString label = "0.0 - 0.0";
1455  mRanges.insert( 0, QgsRendererRangeV2( 0.0, 0.0, newSymbol, label ) );
1456 }
1457 
1458 void QgsGraduatedSymbolRendererV2::addClass( double lower, double upper )
1459 {
1460  QgsSymbolV2* newSymbol = mSourceSymbol->clone();
1461  QString label = mLabelFormat.labelForRange( lower, upper );
1462  mRanges.append( QgsRendererRangeV2( lower, upper, newSymbol, label ) );
1463 }
1464 
1466 {
1467  mRanges.append( range );
1468 }
1469 
1471 {
1472  mRanges.removeAt( idx );
1473 }
1474 
1476 {
1477  mRanges.clear();
1478 }
1479 
1481 {
1482  if ( updateRanges && labelFormat != mLabelFormat )
1483  {
1484  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1485  {
1486  it->setLabel( labelFormat.labelForRange( *it ) );
1487  }
1488  }
1490 }
1491 
1492 
1494 {
1495  // Find the minimum size of a class
1496  double minClassRange = 0.0;
1497  for ( QgsRangeList::iterator it = mRanges.begin(); it != mRanges.end(); ++it )
1498  {
1499  double range = it->upperValue() - it->lowerValue();
1500  if ( range <= 0.0 )
1501  continue;
1502  if ( minClassRange == 0.0 || range < minClassRange )
1503  minClassRange = range;
1504  }
1505  if ( minClassRange <= 0.0 )
1506  return;
1507 
1508  // Now set the number of decimal places to ensure no more than 20% error in
1509  // representing this range (up to 10% at upper and lower end)
1510 
1511  int ndp = 10;
1512  double nextDpMinRange = 0.0000000099;
1513  while ( ndp > 0 && nextDpMinRange < minClassRange )
1514  {
1515  ndp--;
1516  nextDpMinRange *= 10.0;
1517  }
1518  mLabelFormat.setPrecision( ndp );
1519  if ( updateRanges ) setLabelFormat( mLabelFormat, true );
1520 }
1521 
1523 {
1524  if ( from < 0 || from >= mRanges.size() || to < 0 || to >= mRanges.size() )
1525  return;
1526  mRanges.move( from, to );
1527 }
1528 
1530 {
1531  return r1 < r2;
1532 }
1533 
1535 {
1536  return !valueLessThan( r1, r2 );
1537 }
1538 
1540 {
1541  QgsDebugMsg( "Entered" );
1542  if ( order == Qt::AscendingOrder )
1543  {
1544  qSort( mRanges.begin(), mRanges.end(), valueLessThan );
1545  }
1546  else
1547  {
1548  qSort( mRanges.begin(), mRanges.end(), valueGreaterThan );
1549  }
1550 }
1551 
1553 {
1554  return QString::localeAwareCompare( r1.label(), r2.label() ) < 0;
1555 }
1556 
1558 {
1559  return !labelLessThan( r1, r2 );
1560 }
1561 
1563 {
1564  if ( order == Qt::AscendingOrder )
1565  {
1566  qSort( mRanges.begin(), mRanges.end(), labelLessThan );
1567  }
1568  else
1569  {
1570  qSort( mRanges.begin(), mRanges.end(), labelGreaterThan );
1571  }
1572 }
1573 
1575 {
1576  if ( renderer->type() == "graduatedSymbol" )
1577  {
1578  return dynamic_cast<QgsGraduatedSymbolRendererV2*>( renderer->clone() );
1579  }
1580  if ( renderer->type() == "pointDisplacement" )
1581  {
1582  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1583  if ( pointDisplacementRenderer )
1584  return convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1585  }
1586  if ( renderer->type() == "invertedPolygonRenderer" )
1587  {
1588  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1589  if ( invertedPolygonRenderer )
1590  return convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1591  }
1592 
1593  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1594  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1595 
1597  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols();
1598  if ( symbols.size() > 0 )
1599  {
1600  r->setSourceSymbol( symbols.at( 0 )->clone() );
1601  }
1602 
1603  return r;
1604 }
QMap< QString, QgsSymbolV2 * > QgsSymbolV2Map
Definition: qgsrendererv2.h:40
Class for parsing and evaluation of expressions (formerly called "search strings").
Definition: qgsexpression.h:87
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)
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
Definition: qgsexpression.h:94
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.
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
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
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
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)
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)
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
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