QGIS API Documentation  2.17.0-Master (00653d2)
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  if ( !mCategories.isEmpty() )
743  {
744  int i = 0;
746  QDomElement catsElem = doc.createElement( "categories" );
748  for ( ; it != mCategories.constEnd(); ++it )
749  {
750  const QgsRendererCategoryV2& cat = *it;
751  QString symbolName = QString::number( i );
752  symbols.insert( symbolName, cat.symbol() );
753 
754  QDomElement catElem = doc.createElement( "category" );
755  catElem.setAttribute( "value", cat.value().toString() );
756  catElem.setAttribute( "symbol", symbolName );
757  catElem.setAttribute( "label", cat.label() );
758  catElem.setAttribute( "render", cat.renderState() ? "true" : "false" );
759  catsElem.appendChild( catElem );
760  i++;
761  }
762  rendererElem.appendChild( catsElem );
763 
764  // save symbols
765  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
766  rendererElem.appendChild( symbolsElem );
767 
768  }
769 
770  // save source symbol
771  if ( mSourceSymbol.data() )
772  {
773  QgsSymbolV2Map sourceSymbols;
774  sourceSymbols.insert( "0", mSourceSymbol.data() );
775  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
776  rendererElem.appendChild( sourceSymbolElem );
777  }
778 
779  // save source color ramp
780  if ( mSourceColorRamp.data() )
781  {
782  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
783  rendererElem.appendChild( colorRampElem );
784  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
785  invertedElem.setAttribute( "value", mInvertedColorRamp );
786  rendererElem.appendChild( invertedElem );
787  }
788 
789  QDomElement rotationElem = doc.createElement( "rotation" );
790  if ( mRotation.data() )
792  rendererElem.appendChild( rotationElem );
793 
794  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
795  if ( mSizeScale.data() )
797  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
798  rendererElem.appendChild( sizeScaleElem );
799 
801  mPaintEffect->saveProperties( doc, rendererElem );
802 
803  if ( !mOrderBy.isEmpty() )
804  {
805  QDomElement orderBy = doc.createElement( "orderby" );
806  mOrderBy.save( orderBy );
807  rendererElem.appendChild( orderBy );
808  }
809  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
810 
811  return rendererElem;
812 }
813 
815 {
817  int count = categories().count();
818  lst.reserve( count );
819  for ( int i = 0; i < count; i++ )
820  {
821  const QgsRendererCategoryV2& cat = categories()[i];
823  lst << qMakePair( cat.label(), pix );
824  }
825  return lst;
826 }
827 
829 {
830  Q_UNUSED( scaleDenominator );
832 
833  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
834  {
835  if ( rule.isEmpty() || cat.label() == rule )
836  {
837  lst << qMakePair( cat.label(), cat.symbol() );
838  }
839  }
840  return lst;
841 }
842 
844 {
846  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
847  {
848  // check that all symbols that have the same size expression
849  QgsDataDefined ddSize;
850  Q_FOREACH ( const QgsRendererCategoryV2& category, mCategories )
851  {
852  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( category.symbol() );
853  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
854  {
855  // no common size expression
857  }
858  else
859  {
860  ddSize = symbol->dataDefinedSize();
861  }
862  }
863 
864  if ( !ddSize.isActive() || !ddSize.useExpression() )
865  {
867  }
868 
869  QgsScaleExpression exp( ddSize.expressionString() );
870  if ( exp.type() != QgsScaleExpression::Unknown )
871  {
872  QgsLegendSymbolItemV2 title( nullptr, exp.baseExpression(), "" );
873  lst << title;
874  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
875  {
877  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
879  s->setSize( exp.size( v ) );
880  lst << si;
881  }
882  // now list the categorized symbols
884  Q_FOREACH ( const QgsLegendSymbolItemV2& item, list2 )
885  lst << item;
886  return lst;
887  }
888  }
889 
891 }
892 
894 {
895  QString value = valueForFeature( feature, context ).toString();
896  int i = 0;
897 
898  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
899  {
900  if ( value == cat.value() )
901  {
902  if ( cat.renderState() )
903  return QSet< QString >() << QString::number( i );
904  else
905  return QSet< QString >();
906  }
907  i++;
908  }
909 
910  return QSet< QString >();
911 }
912 
914 {
915  return mSourceSymbol.data();
916 }
918 {
919  mSourceSymbol.reset( sym );
920 }
921 
923 {
924  return mSourceColorRamp.data();
925 }
926 
928 {
929  mSourceColorRamp.reset( ramp );
930 }
931 
933 {
934  setSourceColorRamp( ramp );
935  setInvertedColorRamp( inverted );
936  double num = mCategories.count() - 1;
937  double count = 0;
938 
939  QgsRandomColorsV2* randomRamp = dynamic_cast<QgsRandomColorsV2*>( ramp );
940  if ( randomRamp )
941  {
942  //ramp is a random colors ramp, so inform it of the total number of required colors
943  //this allows the ramp to pregenerate a set of visually distinctive colors
944  randomRamp->setTotalColorCount( mCategories.count() );
945  }
946 
947  Q_FOREACH ( const QgsRendererCategoryV2 &cat, mCategories )
948  {
949  double value = count / num;
950  if ( mInvertedColorRamp ) value = 1.0 - value;
951  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
952  count += 1;
953  }
954 }
955 
957 {
958  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
959  {
960  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
961  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
962  }
963 }
964 
966 {
967  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
968  {
969  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
970  QgsDataDefined ddAngle = s->dataDefinedAngle();
971  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
972  }
973 
974  return QString();
975 }
976 
978 {
980 }
981 
983 {
985 }
986 
988 {
989  int i = 0;
990  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
991  {
992  QgsSymbolV2* symbol = sym->clone();
993  symbol->setColor( cat.symbol()->color() );
994  updateCategorySymbol( i, symbol );
995  ++i;
996  }
997  setSourceSymbol( sym->clone() );
998 }
999 
1001 {
1004  for ( ; catIt != mCategories.constEnd(); ++catIt )
1005  {
1006  setScaleMethodToSymbol( catIt->symbol(), scaleMethod );
1007  }
1008 }
1009 
1011 {
1012  return true;
1013 }
1014 
1016 {
1017  bool ok;
1018  int index = key.toInt( &ok );
1019  if ( ok && index >= 0 && index < mCategories.size() )
1020  return mCategories.at( index ).renderState();
1021  else
1022  return true;
1023 }
1024 
1026 {
1027  bool ok;
1028  int index = key.toInt( &ok );
1029  if ( ok )
1030  updateCategorySymbol( index, symbol );
1031  else
1032  delete symbol;
1033 }
1034 
1036 {
1037  bool ok;
1038  int index = key.toInt( &ok );
1039  if ( ok )
1040  updateCategoryRenderState( index, state );
1041 }
1042 
1044 {
1045  QgsCategorizedSymbolRendererV2* r = nullptr;
1046  if ( renderer->type() == "categorizedSymbol" )
1047  {
1048  r = dynamic_cast<QgsCategorizedSymbolRendererV2*>( renderer->clone() );
1049  }
1050  else if ( renderer->type() == "pointDisplacement" )
1051  {
1052  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1053  if ( pointDisplacementRenderer )
1054  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1055  }
1056  else if ( renderer->type() == "invertedPolygonRenderer" )
1057  {
1058  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1059  if ( invertedPolygonRenderer )
1060  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1061  }
1062 
1063  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1064  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1065 
1066  if ( !r )
1067  {
1069  QgsRenderContext context;
1070  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
1071  if ( !symbols.isEmpty() )
1072  {
1073  r->setSourceSymbol( symbols.at( 0 )->clone() );
1074  }
1075  }
1076 
1077  r->setOrderBy( renderer->orderBy() );
1078  r->setOrderByEnabled( renderer->orderByEnabled() );
1079 
1080  return r;
1081 }
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:252
SymbolType type() const
Definition: qgssymbolv2.h:107
Line symbol.
Definition: qgssymbolv2.h:82
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:337
QScopedPointer< QgsSymbolV2 > mSymbol
QMap< QString, QString > QgsStringMap
Definition: qgis.h:492
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:269
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:422
Marker symbol.
Definition: qgssymbolv2.h:81
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:92
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:571
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:90
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:208
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:97
Abstract base class for color ramps.
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.