QGIS API Documentation  2.17.0-Master (0497e4a)
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( "invalid symbol in a category! ignoring..." );
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  return;
449 }
450 
452 {
453  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
454  {
455  cat.symbol()->stopRender( context );
456  }
457 
458  // cleanup mTempSymbols
460  for ( ; it2 != mTempSymbols.constEnd(); ++it2 )
461  {
462  it2.value()->stopRender( context );
463  delete it2.value();
464  }
466  mExpression.reset();
467 }
468 
470 {
471  QSet<QString> attributes;
472 
473  // mAttrName can contain either attribute name or an expression.
474  // Sometimes it is not possible to distinguish between those two,
475  // e.g. "a - b" can be both a valid attribute name or expression.
476  // Since we do not have access to fields here, try both options.
477  attributes << mAttrName;
478 
479  QgsExpression testExpr( mAttrName );
480  if ( !testExpr.hasParserError() )
481  attributes.unite( testExpr.referencedColumns().toSet() );
482 
483  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
484  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
485 
487  for ( ; catIt != mCategories.constEnd(); ++catIt )
488  {
489  QgsSymbolV2* catSymbol = catIt->symbol();
490  if ( catSymbol )
491  {
492  attributes.unite( catSymbol->usedAttributes() );
493  }
494  }
495  return attributes.toList();
496 }
497 
499 {
500  QString s = QString( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
501  for ( int i = 0; i < mCategories.count(); i++ )
502  s += mCategories[i].dump();
503  return s;
504 }
505 
507 {
509  if ( mSourceSymbol.data() )
510  r->setSourceSymbol( mSourceSymbol->clone() );
511  if ( mSourceColorRamp.data() )
512  {
513  r->setSourceColorRamp( mSourceColorRamp->clone() );
515  }
518 
519  copyRendererData( r );
520  return r;
521 }
522 
524 {
525  toSld( doc, element, QgsStringMap() );
526 }
527 
529 {
530  QgsStringMap locProps( props );
531  locProps[ "attribute" ] = mAttrName;
532  if ( mRotation.data() )
533  locProps[ "angle" ] = mRotation->expression();
534  if ( mSizeScale.data() )
535  locProps[ "scale" ] = mSizeScale->expression();
536 
537  // create a Rule for each range
539  {
540  QgsStringMap catProps( locProps );
541  it->toSld( doc, element, catProps );
542  }
543 }
544 
546 {
547  int attrNum = fields.fieldNameIndex( mAttrName );
548  bool isExpression = ( attrNum == -1 );
549 
550  bool hasDefault = false;
551  bool defaultActive = false;
552  bool allActive = true;
553  bool noneActive = true;
554 
555  //we need to build lists of both inactive and active values, as either list may be required
556  //depending on whether the default category is active or not
557  QString activeValues;
558  QString inactiveValues;
559 
560  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
561  {
562  if ( cat.value() == "" )
563  {
564  hasDefault = true;
565  defaultActive = cat.renderState();
566  }
567 
568  noneActive = noneActive && !cat.renderState();
569  allActive = allActive && cat.renderState();
570 
571  QVariant::Type valType = isExpression ? cat.value().type() : fields.at( attrNum ).type();
572  QString value = QgsExpression::quotedValue( cat.value(), valType );
573 
574  if ( !cat.renderState() )
575  {
576  if ( cat.value() != "" )
577  {
578  if ( !inactiveValues.isEmpty() )
579  inactiveValues.append( ',' );
580 
581  inactiveValues.append( value );
582  }
583  }
584  else
585  {
586  if ( cat.value() != "" )
587  {
588  if ( !activeValues.isEmpty() )
589  activeValues.append( ',' );
590 
591  activeValues.append( value );
592  }
593  }
594  }
595 
596  QString attr = isExpression ? mAttrName : QString( "\"%1\"" ).arg( mAttrName );
597 
598  if ( allActive && hasDefault )
599  {
600  return QString();
601  }
602  else if ( noneActive )
603  {
604  return "FALSE";
605  }
606  else if ( defaultActive )
607  {
608  return QString( "(%1) NOT IN (%2) OR (%1) IS NULL" ).arg( attr, inactiveValues );
609  }
610  else
611  {
612  return QString( "(%1) IN (%2)" ).arg( attr, activeValues );
613  }
614 }
615 
617 {
618  Q_UNUSED( context );
619  QgsSymbolV2List lst;
620  lst.reserve( mCategories.count() );
621  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
622  {
623  lst.append( cat.symbol() );
624  }
625  return lst;
626 }
627 
629 {
630  QDomElement symbolsElem = element.firstChildElement( "symbols" );
631  if ( symbolsElem.isNull() )
632  return nullptr;
633 
634  QDomElement catsElem = element.firstChildElement( "categories" );
635  if ( catsElem.isNull() )
636  return nullptr;
637 
638  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
639  QgsCategoryList cats;
640 
641  QDomElement catElem = catsElem.firstChildElement();
642  while ( !catElem.isNull() )
643  {
644  if ( catElem.tagName() == "category" )
645  {
646  QVariant value = QVariant( catElem.attribute( "value" ) );
647  QString symbolName = catElem.attribute( "symbol" );
648  QString label = catElem.attribute( "label" );
649  bool render = catElem.attribute( "render" ) != "false";
650  if ( symbolMap.contains( symbolName ) )
651  {
652  QgsSymbolV2* symbol = symbolMap.take( symbolName );
653  cats.append( QgsRendererCategoryV2( value, symbol, label, render ) );
654  }
655  }
656  catElem = catElem.nextSiblingElement();
657  }
658 
659  QString attrName = element.attribute( "attr" );
660 
662 
663  // delete symbols if there are any more
665 
666  // try to load source symbol (optional)
667  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
668  if ( !sourceSymbolElem.isNull() )
669  {
670  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
671  if ( sourceSymbolMap.contains( "0" ) )
672  {
673  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
674  }
675  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
676  }
677 
678  // try to load color ramp (optional)
679  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
680  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
681  {
682  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
683  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
684  if ( !invertedColorRampElem.isNull() )
685  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
686  }
687 
688  QDomElement rotationElem = element.firstChildElement( "rotation" );
689  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
690  {
691  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->mCategories )
692  {
693  convertSymbolRotation( cat.symbol(), rotationElem.attribute( "field" ) );
694  }
695  if ( r->mSourceSymbol.data() )
696  {
697  convertSymbolRotation( r->mSourceSymbol.data(), rotationElem.attribute( "field" ) );
698  }
699  }
700 
701  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
702  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
703  {
704  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->mCategories )
705  {
707  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
708  sizeScaleElem.attribute( "field" ) );
709  }
710  if ( r->mSourceSymbol.data() && r->mSourceSymbol->type() == QgsSymbolV2::Marker )
711  {
713  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
714  sizeScaleElem.attribute( "field" ) );
715  }
716  }
717 
718  // TODO: symbol levels
719  return r;
720 }
721 
723 {
724  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
725  rendererElem.setAttribute( "type", "categorizedSymbol" );
726  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
727  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
728  rendererElem.setAttribute( "attr", mAttrName );
729 
730  // categories
731  if ( !mCategories.isEmpty() )
732  {
733  int i = 0;
735  QDomElement catsElem = doc.createElement( "categories" );
737  for ( ; it != mCategories.constEnd(); ++it )
738  {
739  const QgsRendererCategoryV2& cat = *it;
740  QString symbolName = QString::number( i );
741  symbols.insert( symbolName, cat.symbol() );
742 
743  QDomElement catElem = doc.createElement( "category" );
744  catElem.setAttribute( "value", cat.value().toString() );
745  catElem.setAttribute( "symbol", symbolName );
746  catElem.setAttribute( "label", cat.label() );
747  catElem.setAttribute( "render", cat.renderState() ? "true" : "false" );
748  catsElem.appendChild( catElem );
749  i++;
750  }
751  rendererElem.appendChild( catsElem );
752 
753  // save symbols
754  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
755  rendererElem.appendChild( symbolsElem );
756 
757  }
758 
759  // save source symbol
760  if ( mSourceSymbol.data() )
761  {
762  QgsSymbolV2Map sourceSymbols;
763  sourceSymbols.insert( "0", mSourceSymbol.data() );
764  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
765  rendererElem.appendChild( sourceSymbolElem );
766  }
767 
768  // save source color ramp
769  if ( mSourceColorRamp.data() )
770  {
771  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
772  rendererElem.appendChild( colorRampElem );
773  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
774  invertedElem.setAttribute( "value", mInvertedColorRamp );
775  rendererElem.appendChild( invertedElem );
776  }
777 
778  QDomElement rotationElem = doc.createElement( "rotation" );
779  if ( mRotation.data() )
781  rendererElem.appendChild( rotationElem );
782 
783  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
784  if ( mSizeScale.data() )
786  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
787  rendererElem.appendChild( sizeScaleElem );
788 
790  mPaintEffect->saveProperties( doc, rendererElem );
791 
792  if ( !mOrderBy.isEmpty() )
793  {
794  QDomElement orderBy = doc.createElement( "orderby" );
795  mOrderBy.save( orderBy );
796  rendererElem.appendChild( orderBy );
797  }
798  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
799 
800  return rendererElem;
801 }
802 
804 {
806  int count = categories().count();
807  lst.reserve( count );
808  for ( int i = 0; i < count; i++ )
809  {
810  const QgsRendererCategoryV2& cat = categories()[i];
812  lst << qMakePair( cat.label(), pix );
813  }
814  return lst;
815 }
816 
818 {
819  Q_UNUSED( scaleDenominator );
821 
822  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
823  {
824  if ( rule.isEmpty() || cat.label() == rule )
825  {
826  lst << qMakePair( cat.label(), cat.symbol() );
827  }
828  }
829  return lst;
830 }
831 
833 {
835  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
836  {
837  // check that all symbols that have the same size expression
838  QgsDataDefined ddSize;
839  Q_FOREACH ( const QgsRendererCategoryV2& category, mCategories )
840  {
841  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( category.symbol() );
842  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
843  {
844  // no common size expression
846  }
847  else
848  {
849  ddSize = symbol->dataDefinedSize();
850  }
851  }
852 
853  if ( !ddSize.isActive() || !ddSize.useExpression() )
854  {
856  }
857 
858  QgsScaleExpression exp( ddSize.expressionString() );
859  if ( exp.type() != QgsScaleExpression::Unknown )
860  {
861  QgsLegendSymbolItemV2 title( nullptr, exp.baseExpression(), "" );
862  lst << title;
863  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
864  {
866  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
868  s->setSize( exp.size( v ) );
869  lst << si;
870  }
871  // now list the categorized symbols
873  Q_FOREACH ( const QgsLegendSymbolItemV2& item, list2 )
874  lst << item;
875  return lst;
876  }
877  }
878 
880 }
881 
883 {
884  QString value = valueForFeature( feature, context ).toString();
885  int i = 0;
886 
887  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
888  {
889  if ( value == cat.value() )
890  {
891  if ( cat.renderState() )
892  return QSet< QString >() << QString::number( i );
893  else
894  return QSet< QString >();
895  }
896  i++;
897  }
898 
899  return QSet< QString >();
900 }
901 
903 {
904  return mSourceSymbol.data();
905 }
907 {
908  mSourceSymbol.reset( sym );
909 }
910 
912 {
913  return mSourceColorRamp.data();
914 }
915 
917 {
918  mSourceColorRamp.reset( ramp );
919 }
920 
922 {
923  setSourceColorRamp( ramp );
924  setInvertedColorRamp( inverted );
925  double num = mCategories.count() - 1;
926  double count = 0;
927 
928  QgsRandomColorsV2* randomRamp = dynamic_cast<QgsRandomColorsV2*>( ramp );
929  if ( randomRamp )
930  {
931  //ramp is a random colors ramp, so inform it of the total number of required colors
932  //this allows the ramp to pregenerate a set of visually distinctive colors
933  randomRamp->setTotalColorCount( mCategories.count() );
934  }
935 
936  Q_FOREACH ( const QgsRendererCategoryV2 &cat, mCategories )
937  {
938  double value = count / num;
939  if ( mInvertedColorRamp ) value = 1.0 - value;
940  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
941  count += 1;
942  }
943 }
944 
946 {
947  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
948  {
949  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
950  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
951  }
952 }
953 
955 {
956  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
957  {
958  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
959  QgsDataDefined ddAngle = s->dataDefinedAngle();
960  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
961  }
962 
963  return QString();
964 }
965 
967 {
969 }
970 
972 {
974 }
975 
977 {
978  int i = 0;
979  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
980  {
981  QgsSymbolV2* symbol = sym->clone();
982  symbol->setColor( cat.symbol()->color() );
983  updateCategorySymbol( i, symbol );
984  ++i;
985  }
986  setSourceSymbol( sym->clone() );
987 }
988 
990 {
993  for ( ; catIt != mCategories.constEnd(); ++catIt )
994  {
995  setScaleMethodToSymbol( catIt->symbol(), scaleMethod );
996  }
997 }
998 
1000 {
1001  return true;
1002 }
1003 
1005 {
1006  bool ok;
1007  int index = key.toInt( &ok );
1008  if ( ok && index >= 0 && index < mCategories.size() )
1009  return mCategories.at( index ).renderState();
1010  else
1011  return true;
1012 }
1013 
1015 {
1016  bool ok;
1017  int index = key.toInt( &ok );
1018  if ( ok )
1019  updateCategorySymbol( index, symbol );
1020  else
1021  delete symbol;
1022 }
1023 
1025 {
1026  bool ok;
1027  int index = key.toInt( &ok );
1028  if ( ok )
1029  updateCategoryRenderState( index, state );
1030 }
1031 
1033 {
1034  QgsCategorizedSymbolRendererV2* r = nullptr;
1035  if ( renderer->type() == "categorizedSymbol" )
1036  {
1037  r = dynamic_cast<QgsCategorizedSymbolRendererV2*>( renderer->clone() );
1038  }
1039  else if ( renderer->type() == "pointDisplacement" )
1040  {
1041  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1042  if ( pointDisplacementRenderer )
1043  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1044  }
1045  else if ( renderer->type() == "invertedPolygonRenderer" )
1046  {
1047  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1048  if ( invertedPolygonRenderer )
1049  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1050  }
1051 
1052  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1053  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1054 
1055  if ( !r )
1056  {
1058  QgsRenderContext context;
1059  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
1060  if ( !symbols.isEmpty() )
1061  {
1062  r->setSourceSymbol( symbols.at( 0 )->clone() );
1063  }
1064  }
1065 
1066  r->setOrderBy( renderer->orderBy() );
1067  r->setOrderByEnabled( renderer->orderByEnabled() );
1068 
1069  return r;
1070 }
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 > 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.