QGIS API Documentation  2.14.11-Essen
qgscategorizedsymbolrendererv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscategorizedsymbolrendererv2.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include <algorithm>
16 
18 
19 #include "qgssymbolv2.h"
20 #include "qgssymbollayerv2utils.h"
21 #include "qgsvectorcolorrampv2.h"
24 #include "qgspainteffect.h"
25 #include "qgspainteffectregistry.h"
26 #include "qgsscaleexpression.h"
27 #include "qgsdatadefined.h"
28 
29 #include "qgsfeature.h"
30 #include "qgsvectorlayer.h"
31 #include "qgslogger.h"
32 
33 #include <QDomDocument>
34 #include <QDomElement>
35 #include <QSettings> // for legend
36 
38  : mRender( true )
39 {
40 }
41 
43  : mValue( value )
44  , mSymbol( symbol )
45  , mLabel( label )
46  , mRender( render )
47 {
48 }
49 
51  : mValue( cat.mValue )
52  , mSymbol( cat.mSymbol.data() ? cat.mSymbol->clone() : nullptr )
53  , mLabel( cat.mLabel )
54  , mRender( cat.mRender )
55 {
56 }
57 
58 // copy+swap idion, the copy is done through the 'pass by value'
60 {
61  swap( cat );
62  return *this;
63 }
64 
66 {
67  qSwap( mValue, cat.mValue );
68  qSwap( mSymbol, cat.mSymbol );
69  qSwap( mLabel, cat.mLabel );
70 }
71 
73 {
74  return mValue;
75 }
76 
78 {
79  return mSymbol.data();
80 }
81 
83 {
84  return mLabel;
85 }
86 
88 {
89  return mRender;
90 }
91 
93 {
94  mValue = value;
95 }
96 
98 {
99  if ( mSymbol.data() != s ) mSymbol.reset( s );
100 }
101 
103 {
104  mLabel = label;
105 }
106 
108 {
109  mRender = render;
110 }
111 
113 {
114  return QString( "%1::%2::%3:%4\n" ).arg( mValue.toString(), mLabel, mSymbol->dump() ).arg( mRender );
115 }
116 
117 void QgsRendererCategoryV2::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap& props ) const
118 {
119  if ( !mSymbol.data() || props.value( "attribute", "" ).isEmpty() )
120  return;
121 
122  QString attrName = props[ "attribute" ];
123 
124  QDomElement ruleElem = doc.createElement( "se:Rule" );
125  element.appendChild( ruleElem );
126 
127  QDomElement nameElem = doc.createElement( "se:Name" );
128  nameElem.appendChild( doc.createTextNode( mLabel ) );
129  ruleElem.appendChild( nameElem );
130 
131  QDomElement descrElem = doc.createElement( "se:Description" );
132  QDomElement titleElem = doc.createElement( "se:Title" );
133  QString descrStr = QString( "%1 is '%2'" ).arg( attrName, mValue.toString() );
134  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
135  descrElem.appendChild( titleElem );
136  ruleElem.appendChild( descrElem );
137 
138  // create the ogc:Filter for the range
139  QString filterFunc = QString( "%1 = '%2'" )
140  .arg( attrName.replace( '\"', "\"\"" ),
141  mValue.toString().replace( '\'', "''" ) );
142  QgsSymbolLayerV2Utils::createFunctionElement( doc, ruleElem, filterFunc );
143 
144  // add the mix/max scale denoms if we got any from the callers
145  QgsSymbolLayerV2Utils::applyScaleDependency( doc, ruleElem, props );
146 
147  mSymbol->toSld( doc, ruleElem, props );
148 }
149 
151 
153  : QgsFeatureRendererV2( "categorizedSymbol" )
154  , mAttrName( attrName )
155  , mInvertedColorRamp( false )
156  , mScaleMethod( DEFAULT_SCALE_METHOD )
157  , mAttrNum( -1 )
158  , mCounting( false )
159 {
160  //important - we need a deep copy of the categories list, not a shared copy. This is required because
161  //QgsRendererCategoryV2::symbol() is marked const, and so retrieving the symbol via this method does not
162  //trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
163  Q_FOREACH ( const QgsRendererCategoryV2& cat, categories )
164  {
165  if ( !cat.symbol() )
166  {
167  QgsDebugMsg( QString( "invalid symbol in category %1 (%2)! ignoring..." ).arg( cat.value().toString(), cat.label() ) );
168  }
169  mCategories << cat;
170  }
171 }
172 
174 {
175 }
176 
178 {
179  mSymbolHash.clear();
180 
181  for ( int i = 0; i < mCategories.size(); ++i )
182  {
183  const QgsRendererCategoryV2& cat = mCategories.at( i );
184  mSymbolHash.insert( cat.value().toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : skipRender() );
185  }
186 }
187 
189 {
190  static QgsMarkerSymbolV2* skipRender = nullptr;
191  if ( !skipRender )
192  skipRender = new QgsMarkerSymbolV2();
193 
194  return skipRender;
195 }
196 
198 {
199  // TODO: special case for int, double
201  if ( it == mSymbolHash.constEnd() )
202  {
203  if ( mSymbolHash.isEmpty() )
204  {
205  QgsDebugMsg( "there are no hashed symbols!!!" );
206  }
207  else
208  {
209  QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
210  }
211  return nullptr;
212  }
213 
214  return *it;
215 }
216 
218 {
219  QgsSymbolV2* symbol = originalSymbolForFeature( feature, context );
220  if ( !symbol )
221  return nullptr;
222 
223  if ( !mRotation.data() && !mSizeScale.data() )
224  return symbol; // no data-defined rotation/scaling - just return the symbol
225 
226  // find out rotation, size scale
227  const double rotation = mRotation.data() ? mRotation->evaluate( &context.expressionContext() ).toDouble() : 0;
228  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( &context.expressionContext() ).toDouble() : 1.;
229 
230  // take a temporary symbol (or create it if doesn't exist)
231  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
232 
233  // modify the temporary symbol and return it
234  if ( tempSymbol->type() == QgsSymbolV2::Marker )
235  {
236  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
237  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
238  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
239  markerSymbol->setScaleMethod( mScaleMethod );
240  }
241  else if ( tempSymbol->type() == QgsSymbolV2::Line )
242  {
243  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
244  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
245  }
246 
247  return tempSymbol;
248 }
249 
250 
251 QVariant QgsCategorizedSymbolRendererV2::valueForFeature( QgsFeature& feature, QgsRenderContext &context ) const
252 {
253  QgsAttributes attrs = feature.attributes();
254  QVariant value;
255  if ( mAttrNum == -1 )
256  {
257  Q_ASSERT( mExpression.data() );
258 
259  value = mExpression->evaluate( &context.expressionContext() );
260  }
261  else
262  {
263  value = attrs.value( mAttrNum );
264  }
265 
266  return value;
267 }
268 
270 {
271  QVariant value = valueForFeature( feature, context );
272 
273  // find the right symbol for the category
274  QgsSymbolV2 *symbol = symbolForValue( value );
275  if ( symbol == skipRender() )
276  return nullptr;
277 
278  if ( !symbol )
279  {
280  // if no symbol found use default one
281  return symbolForValue( QVariant( "" ) );
282  }
283 
284  return symbol;
285 }
286 
287 
289 {
290  for ( int i = 0; i < mCategories.count(); i++ )
291  {
292  if ( mCategories[i].value() == val )
293  return i;
294  }
295  return -1;
296 }
297 
299 {
300  int idx = -1;
301  for ( int i = 0; i < mCategories.count(); i++ )
302  {
303  if ( mCategories[i].label() == val )
304  {
305  if ( idx != -1 )
306  return -1;
307  else
308  idx = i;
309  }
310  }
311  return idx;
312 }
313 
315 {
316  if ( catIndex < 0 || catIndex >= mCategories.size() )
317  return false;
318  mCategories[catIndex].setValue( value );
319  return true;
320 }
321 
323 {
324  if ( catIndex < 0 || catIndex >= mCategories.size() )
325  return false;
326  mCategories[catIndex].setSymbol( symbol );
327  return true;
328 }
329 
331 {
332  if ( catIndex < 0 || catIndex >= mCategories.size() )
333  return false;
334  mCategories[catIndex].setLabel( label );
335  return true;
336 }
337 
339 {
340  if ( catIndex < 0 || catIndex >= mCategories.size() )
341  return false;
342  mCategories[catIndex].setRenderState( render );
343  return true;
344 }
345 
347 {
348  if ( !cat.symbol() )
349  {
350  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
351  return;
352  }
353 
354  mCategories.append( cat );
355 }
356 
358 {
359  if ( catIndex < 0 || catIndex >= mCategories.size() )
360  return false;
361 
362  mCategories.removeAt( catIndex );
363  return true;
364 }
365 
367 {
368  mCategories.clear();
369 }
370 
372 {
373  if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
374  mCategories.move( from, to );
375 }
376 
378 {
379  return qgsVariantLessThan( c1.value(), c2.value() );
380 }
382 {
383  return qgsVariantGreaterThan( c1.value(), c2.value() );
384 }
385 
387 {
388  if ( order == Qt::AscendingOrder )
389  {
391  }
392  else
393  {
395  }
396 }
397 
399 {
400  return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
401 }
402 
404 {
405  return !labelLessThan( c1, c2 );
406 }
407 
409 {
410  if ( order == Qt::AscendingOrder )
411  {
413  }
414  else
415  {
417  }
418 }
419 
421 {
422  mCounting = context.rendererScale() == 0.0;
423 
424  // make sure that the hash table is up to date
425  rebuildHash();
426 
427  // find out classification attribute index from name
428  mAttrNum = fields.fieldNameIndex( mAttrName );
429  if ( mAttrNum == -1 )
430  {
432  mExpression->prepare( &context.expressionContext() );
433  }
434 
435  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
436  {
437  cat.symbol()->startRender( context, &fields );
438 
439  if ( mRotation.data() || mSizeScale.data() )
440  {
441  QgsSymbolV2* tempSymbol = cat.symbol()->clone();
444  tempSymbol->startRender( context, &fields );
445  mTempSymbols[ cat.symbol()] = tempSymbol;
446  }
447  }
448 
449  Q_FOREACH ( QgsSymbolV2 *symbol, mSymbolHash.values() )
450  {
451  symbol->startRender( context, &fields );
452  }
453 
454  return;
455 }
456 
458 {
459  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
460  {
461  cat.symbol()->stopRender( context );
462  }
463 
464  Q_FOREACH ( QgsSymbolV2 *symbol, mSymbolHash.values() )
465  {
466  symbol->stopRender( context );
467  }
468 
469  // cleanup mTempSymbols
471  for ( ; it2 != mTempSymbols.constEnd(); ++it2 )
472  {
473  it2.value()->stopRender( context );
474  delete it2.value();
475  }
477  mExpression.reset();
478 }
479 
481 {
482  QSet<QString> attributes;
483 
484  // mAttrName can contain either attribute name or an expression.
485  // Sometimes it is not possible to distinguish between those two,
486  // e.g. "a - b" can be both a valid attribute name or expression.
487  // Since we do not have access to fields here, try both options.
488  attributes << mAttrName;
489 
490  QgsExpression testExpr( mAttrName );
491  if ( !testExpr.hasParserError() )
492  attributes.unite( testExpr.referencedColumns().toSet() );
493 
494  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
495  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
496 
498  for ( ; catIt != mCategories.constEnd(); ++catIt )
499  {
500  QgsSymbolV2* catSymbol = catIt->symbol();
501  if ( catSymbol )
502  {
503  attributes.unite( catSymbol->usedAttributes() );
504  }
505  }
506  return attributes.toList();
507 }
508 
510 {
511  QString s = QString( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
512  for ( int i = 0; i < mCategories.count(); i++ )
513  s += mCategories[i].dump();
514  return s;
515 }
516 
518 {
520  if ( mSourceSymbol.data() )
521  r->setSourceSymbol( mSourceSymbol->clone() );
522  if ( mSourceColorRamp.data() )
523  {
524  r->setSourceColorRamp( mSourceColorRamp->clone() );
526  }
529 
530  copyRendererData( r );
531  return r;
532 }
533 
535 {
536  toSld( doc, element, QgsStringMap() );
537 }
538 
540 {
541  QgsStringMap locProps( props );
542  locProps[ "attribute" ] = mAttrName;
543  if ( mRotation.data() )
544  locProps[ "angle" ] = mRotation->expression();
545  if ( mSizeScale.data() )
546  locProps[ "scale" ] = mSizeScale->expression();
547 
548  // create a Rule for each range
550  {
551  QgsStringMap catProps( locProps );
552  it->toSld( doc, element, catProps );
553  }
554 }
555 
557 {
558  int attrNum = fields.fieldNameIndex( mAttrName );
559  bool isExpression = ( attrNum == -1 );
560 
561  bool hasDefault = false;
562  bool defaultActive = false;
563  bool allActive = true;
564  bool noneActive = true;
565 
566  //we need to build lists of both inactive and active values, as either list may be required
567  //depending on whether the default category is active or not
568  QString activeValues;
569  QString inactiveValues;
570 
571  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
572  {
573  if ( cat.value() == "" )
574  {
575  hasDefault = true;
576  defaultActive = cat.renderState();
577  }
578 
579  noneActive = noneActive && !cat.renderState();
580  allActive = allActive && cat.renderState();
581 
582  QVariant::Type valType = isExpression ? cat.value().type() : fields.at( attrNum ).type();
583  QString value = QgsExpression::quotedValue( cat.value(), valType );
584 
585  if ( !cat.renderState() )
586  {
587  if ( cat.value() != "" )
588  {
589  if ( !inactiveValues.isEmpty() )
590  inactiveValues.append( ',' );
591 
592  inactiveValues.append( value );
593  }
594  }
595  else
596  {
597  if ( cat.value() != "" )
598  {
599  if ( !activeValues.isEmpty() )
600  activeValues.append( ',' );
601 
602  activeValues.append( value );
603  }
604  }
605  }
606 
607  QString attr = isExpression ? mAttrName : QString( "\"%1\"" ).arg( mAttrName );
608 
609  if ( allActive && hasDefault )
610  {
611  return QString();
612  }
613  else if ( noneActive )
614  {
615  return "FALSE";
616  }
617  else if ( defaultActive )
618  {
619  return QString( "(%1) NOT IN (%2) OR (%1) IS NULL" ).arg( attr, inactiveValues );
620  }
621  else
622  {
623  return QString( "(%1) IN (%2)" ).arg( attr, activeValues );
624  }
625 }
626 
628 {
629  Q_UNUSED( context );
630  QgsSymbolV2List lst;
631  lst.reserve( mCategories.count() );
632  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
633  {
634  lst.append( cat.symbol() );
635  }
636  return lst;
637 }
638 
640 {
641  QDomElement symbolsElem = element.firstChildElement( "symbols" );
642  if ( symbolsElem.isNull() )
643  return nullptr;
644 
645  QDomElement catsElem = element.firstChildElement( "categories" );
646  if ( catsElem.isNull() )
647  return nullptr;
648 
649  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
650  QgsCategoryList cats;
651 
652  QDomElement catElem = catsElem.firstChildElement();
653  while ( !catElem.isNull() )
654  {
655  if ( catElem.tagName() == "category" )
656  {
657  QVariant value = QVariant( catElem.attribute( "value" ) );
658  QString symbolName = catElem.attribute( "symbol" );
659  QString label = catElem.attribute( "label" );
660  bool render = catElem.attribute( "render" ) != "false";
661  if ( symbolMap.contains( symbolName ) )
662  {
663  QgsSymbolV2* symbol = symbolMap.take( symbolName );
664  cats.append( QgsRendererCategoryV2( value, symbol, label, render ) );
665  }
666  }
667  catElem = catElem.nextSiblingElement();
668  }
669 
670  QString attrName = element.attribute( "attr" );
671 
673 
674  // delete symbols if there are any more
676 
677  // try to load source symbol (optional)
678  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
679  if ( !sourceSymbolElem.isNull() )
680  {
681  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
682  if ( sourceSymbolMap.contains( "0" ) )
683  {
684  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
685  }
686  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
687  }
688 
689  // try to load color ramp (optional)
690  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
691  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
692  {
693  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
694  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
695  if ( !invertedColorRampElem.isNull() )
696  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
697  }
698 
699  QDomElement rotationElem = element.firstChildElement( "rotation" );
700  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
701  {
702  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->mCategories )
703  {
704  convertSymbolRotation( cat.symbol(), rotationElem.attribute( "field" ) );
705  }
706  if ( r->mSourceSymbol.data() )
707  {
708  convertSymbolRotation( r->mSourceSymbol.data(), rotationElem.attribute( "field" ) );
709  }
710  }
711 
712  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
713  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
714  {
715  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->mCategories )
716  {
718  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
719  sizeScaleElem.attribute( "field" ) );
720  }
721  if ( r->mSourceSymbol.data() && r->mSourceSymbol->type() == QgsSymbolV2::Marker )
722  {
724  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
725  sizeScaleElem.attribute( "field" ) );
726  }
727  }
728 
729  // TODO: symbol levels
730  return r;
731 }
732 
734 {
735  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
736  rendererElem.setAttribute( "type", "categorizedSymbol" );
737  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
738  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
739  rendererElem.setAttribute( "attr", mAttrName );
740 
741  // categories
742  int i = 0;
744  QDomElement catsElem = doc.createElement( "categories" );
746  for ( ; it != mCategories.constEnd(); ++it )
747  {
748  const QgsRendererCategoryV2& cat = *it;
749  QString symbolName = QString::number( i );
750  symbols.insert( symbolName, cat.symbol() );
751 
752  QDomElement catElem = doc.createElement( "category" );
753  catElem.setAttribute( "value", cat.value().toString() );
754  catElem.setAttribute( "symbol", symbolName );
755  catElem.setAttribute( "label", cat.label() );
756  catElem.setAttribute( "render", cat.renderState() ? "true" : "false" );
757  catsElem.appendChild( catElem );
758  i++;
759  }
760 
761  rendererElem.appendChild( catsElem );
762 
763  // save symbols
764  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
765  rendererElem.appendChild( symbolsElem );
766 
767  // save source symbol
768  if ( mSourceSymbol.data() )
769  {
770  QgsSymbolV2Map sourceSymbols;
771  sourceSymbols.insert( "0", mSourceSymbol.data() );
772  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
773  rendererElem.appendChild( sourceSymbolElem );
774  }
775 
776  // save source color ramp
777  if ( mSourceColorRamp.data() )
778  {
779  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
780  rendererElem.appendChild( colorRampElem );
781  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
782  invertedElem.setAttribute( "value", mInvertedColorRamp );
783  rendererElem.appendChild( invertedElem );
784  }
785 
786  QDomElement rotationElem = doc.createElement( "rotation" );
787  if ( mRotation.data() )
789  rendererElem.appendChild( rotationElem );
790 
791  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
792  if ( mSizeScale.data() )
794  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
795  rendererElem.appendChild( sizeScaleElem );
796 
798  mPaintEffect->saveProperties( doc, rendererElem );
799 
800  if ( !mOrderBy.isEmpty() )
801  {
802  QDomElement orderBy = doc.createElement( "orderby" );
803  mOrderBy.save( orderBy );
804  rendererElem.appendChild( orderBy );
805  }
806  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
807 
808  return rendererElem;
809 }
810 
812 {
814  int count = categories().count();
815  lst.reserve( count );
816  for ( int i = 0; i < count; i++ )
817  {
818  const QgsRendererCategoryV2& cat = categories()[i];
820  lst << qMakePair( cat.label(), pix );
821  }
822  return lst;
823 }
824 
826 {
827  Q_UNUSED( scaleDenominator );
829 
830  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
831  {
832  if ( rule.isEmpty() || cat.label() == rule )
833  {
834  lst << qMakePair( cat.label(), cat.symbol() );
835  }
836  }
837  return lst;
838 }
839 
841 {
843  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
844  {
845  // check that all symbols that have the same size expression
846  QgsDataDefined ddSize;
847  Q_FOREACH ( const QgsRendererCategoryV2& category, mCategories )
848  {
849  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( category.symbol() );
850  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
851  {
852  // no common size expression
854  }
855  else
856  {
857  ddSize = symbol->dataDefinedSize();
858  }
859  }
860 
861  if ( !ddSize.isActive() || !ddSize.useExpression() )
862  {
864  }
865 
866  QgsScaleExpression exp( ddSize.expressionString() );
867  if ( exp.type() != QgsScaleExpression::Unknown )
868  {
869  QgsLegendSymbolItemV2 title( nullptr, exp.baseExpression(), "" );
870  lst << title;
871  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
872  {
874  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
876  s->setSize( exp.size( v ) );
877  lst << si;
878  }
879  // now list the categorized symbols
881  Q_FOREACH ( const QgsLegendSymbolItemV2& item, list2 )
882  lst << item;
883  return lst;
884  }
885  }
886 
888 }
889 
891 {
892  QString value = valueForFeature( feature, context ).toString();
893  int i = 0;
894 
895  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
896  {
897  if ( value == cat.value() )
898  {
899  if ( cat.renderState() )
900  return QSet< QString >() << QString::number( i );
901  else
902  return QSet< QString >();
903  }
904  i++;
905  }
906 
907  return QSet< QString >();
908 }
909 
911 {
912  return mSourceSymbol.data();
913 }
915 {
916  mSourceSymbol.reset( sym );
917 }
918 
920 {
921  return mSourceColorRamp.data();
922 }
923 
925 {
926  mSourceColorRamp.reset( ramp );
927 }
928 
930 {
931  setSourceColorRamp( ramp );
932  setInvertedColorRamp( inverted );
933  double num = mCategories.count() - 1;
934  double count = 0;
935 
936  QgsRandomColorsV2* randomRamp = dynamic_cast<QgsRandomColorsV2*>( ramp );
937  if ( randomRamp )
938  {
939  //ramp is a random colors ramp, so inform it of the total number of required colors
940  //this allows the ramp to pregenerate a set of visually distinctive colors
941  randomRamp->setTotalColorCount( mCategories.count() );
942  }
943 
944  Q_FOREACH ( const QgsRendererCategoryV2 &cat, mCategories )
945  {
946  double value = count / num;
947  if ( mInvertedColorRamp ) value = 1.0 - value;
948  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
949  count += 1;
950  }
951 }
952 
954 {
955  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
956  {
957  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
958  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
959  }
960 }
961 
963 {
964  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
965  {
966  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
967  QgsDataDefined ddAngle = s->dataDefinedAngle();
968  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
969  }
970 
971  return QString();
972 }
973 
975 {
977 }
978 
980 {
982 }
983 
985 {
986  int i = 0;
987  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
988  {
989  QgsSymbolV2* symbol = sym->clone();
990  symbol->setColor( cat.symbol()->color() );
991  updateCategorySymbol( i, symbol );
992  ++i;
993  }
994  setSourceSymbol( sym->clone() );
995 }
996 
998 {
1001  for ( ; catIt != mCategories.constEnd(); ++catIt )
1002  {
1003  setScaleMethodToSymbol( catIt->symbol(), scaleMethod );
1004  }
1005 }
1006 
1008 {
1009  return true;
1010 }
1011 
1013 {
1014  bool ok;
1015  int index = key.toInt( &ok );
1016  if ( ok && index >= 0 && index < mCategories.size() )
1017  return mCategories.at( index ).renderState();
1018  else
1019  return true;
1020 }
1021 
1023 {
1024  bool ok;
1025  int index = key.toInt( &ok );
1026  if ( ok )
1027  updateCategorySymbol( index, symbol );
1028  else
1029  delete symbol;
1030 }
1031 
1033 {
1034  bool ok;
1035  int index = key.toInt( &ok );
1036  if ( ok )
1037  updateCategoryRenderState( index, state );
1038 }
1039 
1041 {
1042  QgsCategorizedSymbolRendererV2* r = nullptr;
1043  if ( renderer->type() == "categorizedSymbol" )
1044  {
1045  r = dynamic_cast<QgsCategorizedSymbolRendererV2*>( renderer->clone() );
1046  }
1047  else if ( renderer->type() == "pointDisplacement" )
1048  {
1049  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1050  if ( pointDisplacementRenderer )
1051  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1052  }
1053  else if ( renderer->type() == "invertedPolygonRenderer" )
1054  {
1055  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1056  if ( invertedPolygonRenderer )
1057  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1058  }
1059 
1060  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1061  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1062 
1063  if ( !r )
1064  {
1066  QgsRenderContext context;
1067  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
1068  if ( !symbols.isEmpty() )
1069  {
1070  r->setSourceSymbol( symbols.at( 0 )->clone() );
1071  }
1072  }
1073 
1074  r->setOrderBy( renderer->orderBy() );
1075  r->setOrderByEnabled( renderer->orderByEnabled() );
1076 
1077  return r;
1078 }
QgsDataDefined dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
const QgsCategoryList & categories() const
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
Class for parsing and evaluation of expressions (formerly called "search strings").
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
void clear()
static QgsSymbolV2Map loadSymbols(QDomElement &element)
void setValue(const QVariant &value)
void setLabel(const QString &label)
#define RENDERER_TAG_NAME
Definition: qgsrendererv2.h:49
static unsigned index
double rendererScale() const
QString & append(QChar ch)
iterator insert(const Key &key, const T &value)
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
virtual void stopRender(QgsRenderContext &context) override
Needs to be called when a render cycle has finished to clean up.
void setDataDefinedAngle(const QgsDataDefined &dd)
Set data defined angle for whole symbol (including all symbol layers).
int categoryIndexForValue(const QVariant &val)
return index of category with specified value (-1 if not found)
A container class for data source field mapping or expression.
virtual bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
static QList< double > prettyBreaks(double minimum, double maximum, int classes)
Computes a sequence of about &#39;classes&#39; equally spaced round values which cover the range of values fr...
bool contains(const Key &key) const
static QgsVectorColorRampV2 * loadColorRamp(QDomElement &element)
int localeAwareCompare(const QString &other) const
QgsVectorColorRampV2 * sourceColorRamp()
Returns the source color ramp, from which each categories&#39; color is derived.
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, const QgsStringMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
QDomNode appendChild(const QDomNode &newChild)
virtual bool legendSymbolItemChecked(const QString &key) override
item in symbology was checked
QString attribute(const QString &name, const QString &defValue) const
#define QgsDebugMsg(str)
Definition: qgslogger.h:33
void setDataDefinedSize(const QgsDataDefined &dd)
Set data defined size for whole symbol (including all symbol layers).
void setSourceSymbol(QgsSymbolV2 *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each categories&#39; symbo...
void reserve(int alloc)
int categoryIndexForLabel(const QString &val)
return index of category with specified label (-1 if not found or not unique)
static QDomElement saveColorRamp(const QString &name, QgsVectorColorRampV2 *ramp, QDomDocument &doc)
QStringList referencedColumns() const
Get list of columns referenced by the expression.
virtual QgsSymbolV2 * clone() const =0
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
static QgsSymbolV2::ScaleMethod decodeScaleMethod(const QString &str)
const T & at(int i) const
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
void removeAt(int i)
QScopedPointer< QgsSymbolV2 > mSourceSymbol
bool updateCategoryRenderState(int catIndex, bool render)
QDomElement nextSiblingElement(const QString &tagName) const
QScopedPointer< QgsExpression > mRotation
Container of fields for a vector layer.
Definition: qgsfield.h:187
SymbolType type() const
Definition: qgssymbolv2.h:104
Line symbol.
Definition: qgssymbolv2.h:79
const_iterator constFind(const Key &key) const
virtual void setTotalColorCount(const int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
void move(int from, int to)
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const
Return a list of symbology items for the legend.
QSet< T > toSet() const
void moveCategory(int from, int to)
Moves the category at index position from to index position to.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:187
virtual QgsCategorizedSymbolRendererV2 * clone() const override
bool qgsVariantGreaterThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is greater than the second.
Definition: qgis.cpp:335
QScopedPointer< QgsSymbolV2 > mSymbol
QMap< QString, QString > QgsStringMap
Definition: qgis.h:392
QgsPaintEffect * mPaintEffect
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
Sets the source color ramp.
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Compares two QVariant values and returns whether the first is less than the second.
Definition: qgis.cpp:267
void setWidth(double width)
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:385
Marker symbol.
Definition: qgssymbolv2.h:78
int size() const
Q_DECL_DEPRECATED void setRotationField(const QString &fieldOrExpression) override
sets rotation field of renderer (if supported by the renderer)
QHash< QString, QgsSymbolV2 * > mSymbolHash
hashtable for faster access to symbols
virtual QDomElement save(QDomDocument &doc) override
store renderer info to XML element
void reset(T *other)
T value(int i) const
bool useExpression() const
Returns if the field or the expression part is active.
Q_DECL_DEPRECATED QString rotationField() const override
return rotation field name (or empty string if not set or not supported by renderer) ...
QScopedPointer< QgsExpression > mSizeScale
virtual QString filter(const QgsFields &fields=QgsFields()) override
If a renderer does not require all the features this method may be overridden and return an expressio...
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
void updateSymbols(QgsSymbolV2 *sym)
Update all the symbols but leave categories and colors.
void setColor(const QColor &color)
QgsSymbolV2 * symbolForValue(const QVariant &value)
virtual QgsFeatureRendererV2 * clone() const =0
QgsSymbolV2::ScaleMethod scaleMethod() const
QList< QgsRendererCategoryV2 > QgsCategoryList
QString number(int n, int base)
int count(const T &value) const
void append(const T &value)
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props) const
Creates a DOM element representing the category in SLD format.
const QgsFeatureRendererV2 * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
QgsLegendSymbolListV2 legendSymbolItemsV2() const override
const_iterator constEnd() const
void startRender(QgsRenderContext &context, const QgsFields *fields=nullptr)
bool isNull() const
virtual void toSld(QDomDocument &doc, QDomElement &element) const override
Writes the SLD element following the SLD v1.1 specs.
QScopedPointer< QgsVectorColorRampV2 > mSourceColorRamp
virtual void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:34
QgsCategorizedSymbolRendererV2(const QString &attrName=QString(), const QgsCategoryList &categories=QgsCategoryList())
virtual QSet< QString > legendKeysForFeature(QgsFeature &feature, QgsRenderContext &context) override
Return legend keys matching a specified feature.
bool labelGreaterThan(const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2)
void setAttribute(const QString &name, const QString &value)
#define DEFAULT_SCALE_METHOD
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer...
QString expressionString() const
Returns the expression string of this QgsDataDefined.
int toInt(bool *ok, int base) const
bool updateCategoryLabel(int catIndex, const QString &label)
bool isEmpty() const
static QgsFeatureRendererV2 * create(QDomElement &element)
create renderer from XML element
virtual QgsLegendSymbolList legendSymbolItems(double scaleDenominator=-1, const QString &rule=QString()) override
return a list of item text / symbol
const T & value() const
QString type() const
Definition: qgsrendererv2.h:83
bool isEmpty() const
void setAngle(double angle)
Sets the angle for the whole symbol.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
Needs to be called when a new render cycle is started.
void setSize(double size)
Sets the size for the whole symbol.
bool usingSymbolLevels() const
virtual void setLegendSymbolItem(const QString &key, QgsSymbolV2 *symbol) override
Sets the symbol to be used for a legend symbol item.
virtual Q_DECL_DEPRECATED QgsSymbolV2List symbols()
For symbol levels.
virtual QgsSymbolV2 * originalSymbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
bool labelLessThan(const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2)
static QgsCategorizedSymbolRendererV2 * convertFromRenderer(const QgsFeatureRendererV2 *renderer)
creates a QgsCategorizedSymbolRendererV2 from an existing renderer.
static void convertSymbolSizeScale(QgsSymbolV2 *symbol, QgsSymbolV2::ScaleMethod method, const QString &field)
QDomText createTextNode(const QString &value)
void updateColorRamp(QgsVectorColorRampV2 *ramp, bool inverted=false)
Update the color ramp used and all symbols colors.
T * data() const
void clear()
iterator end()
int fieldNameIndex(const QString &fieldName) const
Look up field&#39;s index from name also looks up case-insensitive if there is no match otherwise...
Definition: qgsfield.cpp:503
QHash< QgsSymbolV2 *, QgsSymbolV2 * > mTempSymbols
temporary symbols, used for data-defined rotation and scaling
QgsExpressionContext & expressionContext()
Gets the expression context.
QString field() const
Get the field which this QgsDataDefined represents.
A renderer that automatically displaces points with the same position.
bool isNull() const
void setUsingSymbolLevels(bool usingSymbolLevels)
QString & replace(int position, int n, QChar after)
QgsFeatureRequest::OrderBy orderBy() const
Get the order in which features shall be processed by this renderer.
const_iterator constBegin() const
bool valueLessThan(const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2)
void copyRendererData(QgsFeatureRendererV2 *destRenderer) const
Clones generic renderer data to another renderer.
Contains information about the context of a rendering operation.
QgsSymbolV2 * sourceSymbol()
Returns the renderer&#39;s source symbol, which is the base symbol used for the each categories&#39; symbol b...
void setOrderBy(const QgsFeatureRequest::OrderBy &orderBy)
Define the order in which features shall be processed by this renderer.
virtual QList< QString > usedAttributes() override
Returns a set of attributes required for this renderer.
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
void stopRender(QgsRenderContext &context)
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
QSet< T > & unite(const QSet< T > &other)
static QgsExpression * fieldOrExpressionToExpression(const QString &fieldOrExpression)
Return a new valid expression instance for given field or expression string.
ScaleMethod
Scale method.
Definition: qgssymbolv2.h:87
QgsDataDefined dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
bool isEmpty() const
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=nullptr)
QgsFeatureRequest::OrderBy mOrderBy
static QString encodeScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
bool updateCategoryValue(int catIndex, const QVariant &value)
QColor color() const
QDomElement firstChildElement(const QString &tagName) const
bool valueGreaterThan(const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2)
static QString fieldOrExpressionFromExpression(QgsExpression *expression)
Return a field name if the whole expression is just a name of the field .
void setScaleMethodToSymbol(QgsSymbolV2 *symbol, int scaleMethod)
bool updateCategorySymbol(int catIndex, QgsSymbolV2 *symbol)
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
int mAttrNum
attribute index (derived from attribute name in startRender)
QList< T > values() const
QList< T > toList() const
void setRenderHints(int hints)
Definition: qgssymbolv2.h:205
static void clearSymbolMap(QgsSymbolV2Map &symbols)
virtual QgsSymbolV2 * symbolForFeature(QgsFeature &feature, QgsRenderContext &context) override
iterator insert(const Key &key, const T &value)
void swap(QgsRendererCategoryV2 &other)
QgsRendererCategoryV2 & operator=(QgsRendererCategoryV2 cat)
QString tagName() const
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
QSet< QString > usedAttributes() const
Return a list of attributes required to render this feature.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required...
Type type() const
bool hasDefaultValues() const
Returns whether the data defined container is set to all the default values, ie, disabled, with empty expression and no assigned field.
A vector of attributes.
Definition: qgsfeature.h:115
void addCategory(const QgsRendererCategoryV2 &category)
QVariant::Type type() const
Gets variant type of the field as it will be retrieved from data source.
Definition: qgsfield.cpp:89
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
QString arg(qlonglong a, int fieldWidth, int base, const QChar &fillChar) const
QString toString() const
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
void setSizeScaleField(const QString &fieldOrExpression)
bool isActive() const
iterator begin()
T take(const Key &key)
virtual QgsLegendSymbologyList legendSymbologyItems(QSize iconSize) override
return a list of symbology items for the legend
void setScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
const QgsFeatureRendererV2 * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
QScopedPointer< QgsExpression > mExpression
const T value(const Key &key) const
static void convertSymbolRotation(QgsSymbolV2 *symbol, const QString &field)
virtual QString dump() const override
for debugging
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.