QGIS API Documentation  2.13.0-Master
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 
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  mSymbol->toSld( doc, ruleElem, props );
145 }
146 
148 
150  : QgsFeatureRendererV2( "categorizedSymbol" )
151  , mAttrName( attrName )
152  , mInvertedColorRamp( false )
153  , mScaleMethod( DEFAULT_SCALE_METHOD )
154  , mAttrNum( -1 )
155  , mCounting( false )
156 {
157  //important - we need a deep copy of the categories list, not a shared copy. This is required because
158  //QgsRendererCategoryV2::symbol() is marked const, and so retrieving the symbol via this method does not
159  //trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
160  Q_FOREACH ( const QgsRendererCategoryV2& cat, categories )
161  {
162  if ( cat.symbol() )
163  {
164  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
165  }
166  mCategories << cat;
167  }
168 }
169 
171 {
172 }
173 
175 {
176  mSymbolHash.clear();
177 
178  for ( int i = 0; i < mCategories.size(); ++i )
179  {
180  const QgsRendererCategoryV2& cat = mCategories.at( i );
181  mSymbolHash.insert( cat.value().toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : skipRender() );
182  }
183 }
184 
186 {
187  static QgsMarkerSymbolV2* skipRender = nullptr;
188  if ( !skipRender )
189  skipRender = new QgsMarkerSymbolV2();
190 
191  return skipRender;
192 }
193 
195 {
196  // TODO: special case for int, double
198  if ( it == mSymbolHash.constEnd() )
199  {
200  if ( mSymbolHash.isEmpty() )
201  {
202  QgsDebugMsg( "there are no hashed symbols!!!" );
203  }
204  else
205  {
206  QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
207  }
208  return nullptr;
209  }
210 
211  return *it;
212 }
213 
215 {
216  QgsSymbolV2* symbol = originalSymbolForFeature( feature, context );
217  if ( !symbol )
218  return nullptr;
219 
220  if ( !mRotation.data() && !mSizeScale.data() )
221  return symbol; // no data-defined rotation/scaling - just return the symbol
222 
223  // find out rotation, size scale
224  const double rotation = mRotation.data() ? mRotation->evaluate( &context.expressionContext() ).toDouble() : 0;
225  const double sizeScale = mSizeScale.data() ? mSizeScale->evaluate( &context.expressionContext() ).toDouble() : 1.;
226 
227  // take a temporary symbol (or create it if doesn't exist)
228  QgsSymbolV2* tempSymbol = mTempSymbols[symbol];
229 
230  // modify the temporary symbol and return it
231  if ( tempSymbol->type() == QgsSymbolV2::Marker )
232  {
233  QgsMarkerSymbolV2* markerSymbol = static_cast<QgsMarkerSymbolV2*>( tempSymbol );
234  if ( mRotation.data() ) markerSymbol->setAngle( rotation );
235  markerSymbol->setSize( sizeScale * static_cast<QgsMarkerSymbolV2*>( symbol )->size() );
236  markerSymbol->setScaleMethod( mScaleMethod );
237  }
238  else if ( tempSymbol->type() == QgsSymbolV2::Line )
239  {
240  QgsLineSymbolV2* lineSymbol = static_cast<QgsLineSymbolV2*>( tempSymbol );
241  lineSymbol->setWidth( sizeScale * static_cast<QgsLineSymbolV2*>( symbol )->width() );
242  }
243 
244  return tempSymbol;
245 }
246 
247 
248 QVariant QgsCategorizedSymbolRendererV2::valueForFeature( QgsFeature& feature, QgsRenderContext &context ) const
249 {
250  QgsAttributes attrs = feature.attributes();
251  QVariant value;
252  if ( mAttrNum == -1 )
253  {
254  Q_ASSERT( mExpression.data() );
255 
256  value = mExpression->evaluate( &context.expressionContext() );
257  }
258  else
259  {
260  value = attrs.value( mAttrNum );
261  }
262 
263  return value;
264 }
265 
267 {
268  QVariant value = valueForFeature( feature, context );
269 
270  // find the right symbol for the category
271  QgsSymbolV2 *symbol = symbolForValue( value );
272  if ( symbol == skipRender() )
273  return nullptr;
274 
275  if ( !symbol )
276  {
277  // if no symbol found use default one
278  return symbolForValue( QVariant( "" ) );
279  }
280 
281  return symbol;
282 }
283 
284 
286 {
287  for ( int i = 0; i < mCategories.count(); i++ )
288  {
289  if ( mCategories[i].value() == val )
290  return i;
291  }
292  return -1;
293 }
294 
296 {
297  int idx = -1;
298  for ( int i = 0; i < mCategories.count(); i++ )
299  {
300  if ( mCategories[i].label() == val )
301  {
302  if ( idx != -1 )
303  return -1;
304  else
305  idx = i;
306  }
307  }
308  return idx;
309 }
310 
312 {
313  if ( catIndex < 0 || catIndex >= mCategories.size() )
314  return false;
315  mCategories[catIndex].setValue( value );
316  return true;
317 }
318 
320 {
321  if ( catIndex < 0 || catIndex >= mCategories.size() )
322  return false;
323  mCategories[catIndex].setSymbol( symbol );
324  return true;
325 }
326 
328 {
329  if ( catIndex < 0 || catIndex >= mCategories.size() )
330  return false;
331  mCategories[catIndex].setLabel( label );
332  return true;
333 }
334 
336 {
337  if ( catIndex < 0 || catIndex >= mCategories.size() )
338  return false;
339  mCategories[catIndex].setRenderState( render );
340  return true;
341 }
342 
344 {
345  if ( !cat.symbol() )
346  {
347  QgsDebugMsg( "invalid symbol in a category! ignoring..." );
348  return;
349  }
350 
351  mCategories.append( cat );
352 }
353 
355 {
356  if ( catIndex < 0 || catIndex >= mCategories.size() )
357  return false;
358 
359  mCategories.removeAt( catIndex );
360  return true;
361 }
362 
364 {
365  mCategories.clear();
366 }
367 
369 {
370  if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
371  mCategories.move( from, to );
372 }
373 
375 {
376  return qgsVariantLessThan( c1.value(), c2.value() );
377 }
379 {
380  return qgsVariantGreaterThan( c1.value(), c2.value() );
381 }
382 
384 {
385  if ( order == Qt::AscendingOrder )
386  {
388  }
389  else
390  {
392  }
393 }
394 
396 {
397  return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
398 }
399 
401 {
402  return !labelLessThan( c1, c2 );
403 }
404 
406 {
407  if ( order == Qt::AscendingOrder )
408  {
410  }
411  else
412  {
414  }
415 }
416 
418 {
419  mCounting = context.rendererScale() == 0.0;
420 
421  // make sure that the hash table is up to date
422  rebuildHash();
423 
424  // find out classification attribute index from name
425  mAttrNum = fields.fieldNameIndex( mAttrName );
426  if ( mAttrNum == -1 )
427  {
429  mExpression->prepare( &context.expressionContext() );
430  }
431 
432  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
433  {
434  cat.symbol()->startRender( context, &fields );
435 
436  if ( mRotation.data() || mSizeScale.data() )
437  {
438  QgsSymbolV2* tempSymbol = cat.symbol()->clone();
441  tempSymbol->startRender( context, &fields );
442  mTempSymbols[ cat.symbol()] = tempSymbol;
443  }
444  }
445  return;
446 }
447 
449 {
450  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
451  {
452  cat.symbol()->stopRender( context );
453  }
454 
455  // cleanup mTempSymbols
457  for ( ; it2 != mTempSymbols.constEnd(); ++it2 )
458  {
459  it2.value()->stopRender( context );
460  delete it2.value();
461  }
463  mExpression.reset();
464 }
465 
467 {
468  QSet<QString> attributes;
469 
470  // mAttrName can contain either attribute name or an expression.
471  // Sometimes it is not possible to distinguish between those two,
472  // e.g. "a - b" can be both a valid attribute name or expression.
473  // Since we do not have access to fields here, try both options.
474  attributes << mAttrName;
475 
476  QgsExpression testExpr( mAttrName );
477  if ( !testExpr.hasParserError() )
478  attributes.unite( testExpr.referencedColumns().toSet() );
479 
480  if ( mRotation.data() ) attributes.unite( mRotation->referencedColumns().toSet() );
481  if ( mSizeScale.data() ) attributes.unite( mSizeScale->referencedColumns().toSet() );
482 
484  for ( ; catIt != mCategories.constEnd(); ++catIt )
485  {
486  QgsSymbolV2* catSymbol = catIt->symbol();
487  if ( catSymbol )
488  {
489  attributes.unite( catSymbol->usedAttributes() );
490  }
491  }
492  return attributes.toList();
493 }
494 
496 {
497  QString s = QString( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
498  for ( int i = 0; i < mCategories.count(); i++ )
499  s += mCategories[i].dump();
500  return s;
501 }
502 
504 {
506  if ( mSourceSymbol.data() )
507  r->setSourceSymbol( mSourceSymbol->clone() );
508  if ( mSourceColorRamp.data() )
509  {
510  r->setSourceColorRamp( mSourceColorRamp->clone() );
512  }
515 
516  copyRendererData( r );
517  return r;
518 }
519 
521 {
522  QgsStringMap props;
523  props[ "attribute" ] = mAttrName;
524  if ( mRotation.data() )
525  props[ "angle" ] = mRotation->expression();
526  if ( mSizeScale.data() )
527  props[ "scale" ] = mSizeScale->expression();
528 
529  // create a Rule for each range
531  {
532  QgsStringMap catProps( props );
533  it->toSld( doc, element, catProps );
534  }
535 }
536 
538 {
539  int attrNum = fields.fieldNameIndex( mAttrName );
540  bool isExpression = ( attrNum == -1 );
541 
542  bool hasDefault = false;
543  bool defaultActive = false;
544  bool allActive = true;
545  bool noneActive = true;
546 
547  //we need to build lists of both inactive and active values, as either list may be required
548  //depending on whether the default category is active or not
549  QString activeValues;
550  QString inactiveValues;
551 
552  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
553  {
554  if ( cat.value() == "" )
555  {
556  hasDefault = true;
557  defaultActive = cat.renderState();
558  }
559 
560  noneActive = noneActive && !cat.renderState();
561  allActive = allActive && cat.renderState();
562 
563  QVariant::Type valType = isExpression ? cat.value().type() : fields.at( attrNum ).type();
564  QString value = QgsExpression::quotedValue( cat.value(), valType );
565 
566  if ( !cat.renderState() )
567  {
568  if ( cat.value() != "" )
569  {
570  if ( !inactiveValues.isEmpty() )
571  inactiveValues.append( ',' );
572 
573  inactiveValues.append( value );
574  }
575  }
576  else
577  {
578  if ( cat.value() != "" )
579  {
580  if ( !activeValues.isEmpty() )
581  activeValues.append( ',' );
582 
583  activeValues.append( value );
584  }
585  }
586  }
587 
588  if ( allActive && hasDefault )
589  {
590  return QString();
591  }
592  else if ( noneActive )
593  {
594  return "FALSE";
595  }
596  else if ( defaultActive )
597  {
598  return QString( "(\"%1\") NOT IN (%2) OR (\"%1\") IS NULL" ).arg( mAttrName, inactiveValues );
599  }
600  else
601  {
602  return QString( "(\"%1\") IN (%2)" ).arg( mAttrName, activeValues );
603  }
604 }
605 
607 {
608  Q_UNUSED( context );
609  QgsSymbolV2List lst;
610  lst.reserve( mCategories.count() );
611  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
612  {
613  lst.append( cat.symbol() );
614  }
615  return lst;
616 }
617 
619 {
620  QDomElement symbolsElem = element.firstChildElement( "symbols" );
621  if ( symbolsElem.isNull() )
622  return nullptr;
623 
624  QDomElement catsElem = element.firstChildElement( "categories" );
625  if ( catsElem.isNull() )
626  return nullptr;
627 
628  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
629  QgsCategoryList cats;
630 
631  QDomElement catElem = catsElem.firstChildElement();
632  while ( !catElem.isNull() )
633  {
634  if ( catElem.tagName() == "category" )
635  {
636  QVariant value = QVariant( catElem.attribute( "value" ) );
637  QString symbolName = catElem.attribute( "symbol" );
638  QString label = catElem.attribute( "label" );
639  bool render = catElem.attribute( "render" ) != "false";
640  if ( symbolMap.contains( symbolName ) )
641  {
642  QgsSymbolV2* symbol = symbolMap.take( symbolName );
643  cats.append( QgsRendererCategoryV2( value, symbol, label, render ) );
644  }
645  }
646  catElem = catElem.nextSiblingElement();
647  }
648 
649  QString attrName = element.attribute( "attr" );
650 
652 
653  // delete symbols if there are any more
655 
656  // try to load source symbol (optional)
657  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
658  if ( !sourceSymbolElem.isNull() )
659  {
660  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
661  if ( sourceSymbolMap.contains( "0" ) )
662  {
663  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
664  }
665  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
666  }
667 
668  // try to load color ramp (optional)
669  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
670  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
671  {
672  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
673  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
674  if ( !invertedColorRampElem.isNull() )
675  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
676  }
677 
678  QDomElement rotationElem = element.firstChildElement( "rotation" );
679  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
680  {
681  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->mCategories )
682  {
683  convertSymbolRotation( cat.symbol(), rotationElem.attribute( "field" ) );
684  }
685  if ( r->mSourceSymbol.data() )
686  {
687  convertSymbolRotation( r->mSourceSymbol.data(), rotationElem.attribute( "field" ) );
688  }
689  }
690 
691  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
692  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
693  {
694  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->mCategories )
695  {
697  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
698  sizeScaleElem.attribute( "field" ) );
699  }
700  if ( r->mSourceSymbol.data() && r->mSourceSymbol->type() == QgsSymbolV2::Marker )
701  {
703  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
704  sizeScaleElem.attribute( "field" ) );
705  }
706  }
707 
708  // TODO: symbol levels
709  return r;
710 }
711 
713 {
714  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
715  rendererElem.setAttribute( "type", "categorizedSymbol" );
716  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
717  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
718  rendererElem.setAttribute( "attr", mAttrName );
719 
720  // categories
721  int i = 0;
723  QDomElement catsElem = doc.createElement( "categories" );
725  for ( ; it != mCategories.end(); ++it )
726  {
727  const QgsRendererCategoryV2& cat = *it;
728  QString symbolName = QString::number( i );
729  symbols.insert( symbolName, cat.symbol() );
730 
731  QDomElement catElem = doc.createElement( "category" );
732  catElem.setAttribute( "value", cat.value().toString() );
733  catElem.setAttribute( "symbol", symbolName );
734  catElem.setAttribute( "label", cat.label() );
735  catElem.setAttribute( "render", cat.renderState() ? "true" : "false" );
736  catsElem.appendChild( catElem );
737  i++;
738  }
739 
740  rendererElem.appendChild( catsElem );
741 
742  // save symbols
743  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
744  rendererElem.appendChild( symbolsElem );
745 
746  // save source symbol
747  if ( mSourceSymbol.data() )
748  {
749  QgsSymbolV2Map sourceSymbols;
750  sourceSymbols.insert( "0", mSourceSymbol.data() );
751  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
752  rendererElem.appendChild( sourceSymbolElem );
753  }
754 
755  // save source color ramp
756  if ( mSourceColorRamp.data() )
757  {
758  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
759  rendererElem.appendChild( colorRampElem );
760  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
761  invertedElem.setAttribute( "value", mInvertedColorRamp );
762  rendererElem.appendChild( invertedElem );
763  }
764 
765  QDomElement rotationElem = doc.createElement( "rotation" );
766  if ( mRotation.data() )
768  rendererElem.appendChild( rotationElem );
769 
770  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
771  if ( mSizeScale.data() )
773  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
774  rendererElem.appendChild( sizeScaleElem );
775 
777  mPaintEffect->saveProperties( doc, rendererElem );
778 
779  if ( !mOrderBy.isEmpty() )
780  {
781  QDomElement orderBy = doc.createElement( "orderby" );
782  mOrderBy.save( orderBy );
783  rendererElem.appendChild( orderBy );
784  }
785 
786  return rendererElem;
787 }
788 
790 {
792  int count = categories().count();
793  lst.reserve( count );
794  for ( int i = 0; i < count; i++ )
795  {
796  const QgsRendererCategoryV2& cat = categories()[i];
798  lst << qMakePair( cat.label(), pix );
799  }
800  return lst;
801 }
802 
804 {
805  Q_UNUSED( scaleDenominator );
807 
808  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
809  {
810  if ( rule.isEmpty() || cat.label() == rule )
811  {
812  lst << qMakePair( cat.label(), cat.symbol() );
813  }
814  }
815  return lst;
816 }
817 
819 {
821  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
822  {
823  // check that all symbols that have the same size expression
824  QgsDataDefined ddSize;
825  Q_FOREACH ( const QgsRendererCategoryV2& category, mCategories )
826  {
827  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( category.symbol() );
828  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
829  {
830  // no common size expression
832  }
833  else
834  {
835  ddSize = symbol->dataDefinedSize();
836  }
837  }
838 
839  if ( !ddSize.isActive() || !ddSize.useExpression() )
840  {
842  }
843 
844  QgsScaleExpression exp( ddSize.expressionString() );
845  if ( exp.type() != QgsScaleExpression::Unknown )
846  {
847  QgsLegendSymbolItemV2 title( nullptr, exp.baseExpression(), "" );
848  lst << title;
849  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
850  {
852  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
854  s->setSize( exp.size( v ) );
855  lst << si;
856  }
857  // now list the categorized symbols
859  Q_FOREACH ( const QgsLegendSymbolItemV2& item, list2 )
860  lst << item;
861  return lst;
862  }
863  }
864 
866 }
867 
869 {
870  QString value = valueForFeature( feature, context ).toString();
871  int i = 0;
872 
873  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
874  {
875  if ( value == cat.value() )
876  {
877  if ( cat.renderState() )
878  return QSet< QString >() << QString::number( i );
879  else
880  return QSet< QString >();
881  }
882  i++;
883  }
884 
885  return QSet< QString >();
886 }
887 
889 {
890  return mSourceSymbol.data();
891 }
893 {
894  mSourceSymbol.reset( sym );
895 }
896 
898 {
899  return mSourceColorRamp.data();
900 }
901 
903 {
904  mSourceColorRamp.reset( ramp );
905 }
906 
908 {
909  setSourceColorRamp( ramp );
910  setInvertedColorRamp( inverted );
911  double num = mCategories.count() - 1;
912  double count = 0;
913 
914  QgsRandomColorsV2* randomRamp = dynamic_cast<QgsRandomColorsV2*>( ramp );
915  if ( randomRamp )
916  {
917  //ramp is a random colors ramp, so inform it of the total number of required colors
918  //this allows the ramp to pregenerate a set of visually distinctive colors
919  randomRamp->setTotalColorCount( mCategories.count() );
920  }
921 
922  Q_FOREACH ( const QgsRendererCategoryV2 &cat, mCategories )
923  {
924  double value = count / num;
925  if ( mInvertedColorRamp ) value = 1.0 - value;
926  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
927  count += 1;
928  }
929 }
930 
932 {
933  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
934  {
935  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
936  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
937  }
938 }
939 
941 {
942  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
943  {
944  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
945  QgsDataDefined ddAngle = s->dataDefinedAngle();
946  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
947  }
948 
949  return QString();
950 }
951 
953 {
955 }
956 
958 {
960 }
961 
963 {
964  int i = 0;
965  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
966  {
967  QgsSymbolV2* symbol = sym->clone();
968  symbol->setColor( cat.symbol()->color() );
969  updateCategorySymbol( i, symbol );
970  ++i;
971  }
972  setSourceSymbol( sym->clone() );
973 }
974 
976 {
979  for ( ; catIt != mCategories.constEnd(); ++catIt )
980  {
981  setScaleMethodToSymbol( catIt->symbol(), scaleMethod );
982  }
983 }
984 
986 {
987  return true;
988 }
989 
991 {
992  bool ok;
993  int index = key.toInt( &ok );
994  if ( ok && index >= 0 && index < mCategories.size() )
995  return mCategories.at( index ).renderState();
996  else
997  return true;
998 }
999 
1001 {
1002  bool ok;
1003  int index = key.toInt( &ok );
1004  if ( ok )
1005  updateCategorySymbol( index, symbol );
1006  else
1007  delete symbol;
1008 }
1009 
1011 {
1012  bool ok;
1013  int index = key.toInt( &ok );
1014  if ( ok )
1015  updateCategoryRenderState( index, state );
1016 }
1017 
1019 {
1020  QgsCategorizedSymbolRendererV2* r = nullptr;
1021  if ( renderer->type() == "categorizedSymbol" )
1022  {
1023  r = dynamic_cast<QgsCategorizedSymbolRendererV2*>( renderer->clone() );
1024  }
1025  else if ( renderer->type() == "pointDisplacement" )
1026  {
1027  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1028  if ( pointDisplacementRenderer )
1029  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1030  }
1031  else if ( renderer->type() == "invertedPolygonRenderer" )
1032  {
1033  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1034  if ( invertedPolygonRenderer )
1035  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1036  }
1037 
1038  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1039  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1040 
1041  if ( !r )
1042  {
1044  QgsRenderContext context;
1045  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
1046  if ( !symbols.isEmpty() )
1047  {
1048  r->setSourceSymbol( symbols.at( 0 )->clone() );
1049  }
1050  }
1051 
1052  r->setOrderBy( renderer->orderBy() );
1053 
1054  return r;
1055 }
static QDomElement saveSymbols(QgsSymbolV2Map &symbols, const QString &tagName, QDomDocument &doc)
Class for parsing and evaluation of expressions (formerly called "search strings").
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
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
static unsigned index
QString & append(QChar ch)
iterator insert(const Key &key, const T &value)
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
virtual void stopRender(QgsRenderContext &context) override
Needs to be called when a render cycle has finished to clean up.
QStringList referencedColumns() const
Get list of columns referenced by the expression.
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
const QgsCategoryList & categories() const
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.
QgsFeatureRequest::OrderBy orderBy() const
Get the order in which features shall be processed by this renderer.
QDomNode appendChild(const QDomNode &newChild)
SymbolType type() const
Definition: qgssymbolv2.h:101
QSet< QString > usedAttributes() const
Return a list of attributes required to render this feature.
virtual bool legendSymbolItemChecked(const QString &key) override
item in symbology was checked
QString field() const
Get the field which this QgsDataDefined represents.
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)
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:446
double rendererScale() const
virtual QgsSymbolV2 * clone() const =0
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
QgsDataDefined dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
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:189
Line symbol.
Definition: qgssymbolv2.h:76
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.
QString expressionString() const
Returns the expression string of this QgsDataDefined.
void move(int from, int to)
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)
Definition: qgis.cpp:288
QScopedPointer< QgsSymbolV2 > mSymbol
QgsPaintEffect * mPaintEffect
void setSourceColorRamp(QgsVectorColorRampV2 *ramp)
Sets the source color ramp.
bool qgsVariantLessThan(const QVariant &lhs, const QVariant &rhs)
Definition: qgis.cpp:261
void setWidth(double width)
Marker symbol.
Definition: qgssymbolv2.h:75
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
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...
QString type() const
Definition: qgsrendererv2.h:83
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
QList< QgsRendererCategoryV2 > QgsCategoryList
QString number(int n, int base)
int count(const T &value) const
void append(const T &value)
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
used from subclasses to create SLD Rule elements following 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.
QgsAttributes attributes() const
Returns the feature&#39;s attributes.
Definition: qgsfeature.cpp:110
bool labelGreaterThan(const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2)
void setAttribute(const QString &name, const QString &value)
#define DEFAULT_SCALE_METHOD
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
bool isEmpty() const
void setAngle(double angle)
virtual void startRender(QgsRenderContext &context, const QgsFields &fields) override
Needs to be called when a new render cycle is started.
void setSize(double size)
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
const QgsField & at(int i) const
Get field at particular index (must be in range 0..N-1)
Definition: qgsfield.cpp:383
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)
QgsFeatureRendererV2 * embeddedRenderer() const
bool useExpression() const
Returns if the field or the expression part is active.
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()
const T value(const Key &key) const
QHash< QgsSymbolV2 *, QgsSymbolV2 * > mTempSymbols
temporary symbols, used for data-defined rotation and scaling
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
QgsExpressionContext & expressionContext()
Gets the expression context.
void copyRendererData(QgsFeatureRendererV2 *destRenderer) const
Clones generic renderer data to another renderer.
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)
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.
const_iterator constBegin() const
bool valueLessThan(const QgsRendererCategoryV2 &c1, const QgsRendererCategoryV2 &c2)
Contains information about the context of a rendering operation.
const QgsFeatureRendererV2 * embeddedRenderer() const
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.
QgsDataDefined dataDefinedAngle() const
Returns data defined angle for whole symbol (including all symbol layers).
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:84
bool isEmpty() const
static QPixmap symbolPreviewPixmap(QgsSymbolV2 *symbol, QSize size, QgsRenderContext *customContext=nullptr)
virtual QgsLegendSymbolListV2 legendSymbolItemsV2() const
Return a list of symbology items for the legend.
QgsFeatureRequest::OrderBy mOrderBy
static QString encodeScaleMethod(QgsSymbolV2::ScaleMethod scaleMethod)
bool updateCategoryValue(int catIndex, const QVariant &value)
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 .
bool usingSymbolLevels() const
void setScaleMethodToSymbol(QgsSymbolV2 *symbol, int scaleMethod)
bool updateCategorySymbol(int catIndex, QgsSymbolV2 *symbol)
int mAttrNum
attribute index (derived from attribute name in startRender)
QList< T > toList() const
void setRenderHints(int hints)
Definition: qgssymbolv2.h:201
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
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.
QgsSymbolV2::ScaleMethod scaleMethod() const
const_iterator constEnd() const
QDomElement createElement(const QString &tagName)
const_iterator constBegin() const
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required...
Type type() const
A vector of attributes.
Definition: qgsfeature.h:115
void addCategory(const QgsRendererCategoryV2 &category)
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)
QScopedPointer< QgsExpression > mExpression
QVariant::Type type() const
Gets variant type of the field as it will be retrieved from data source.
Definition: qgsfield.cpp:87
const T value(const Key &key) const
static void convertSymbolRotation(QgsSymbolV2 *symbol, const QString &field)
QColor color() const
virtual QString dump() const override
for debugging