QGIS API Documentation  2.14.0-Essen
qgscategorizedsymbolrendererv2.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscategorizedsymbolrendererv2.cpp
3  ---------------------
4  begin : November 2009
5  copyright : (C) 2009 by Martin Dobias
6  email : wonder dot sk at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 #include <algorithm>
16 
18 
19 #include "qgssymbolv2.h"
20 #include "qgssymbollayerv2utils.h"
21 #include "qgsvectorcolorrampv2.h"
24 #include "qgspainteffect.h"
25 #include "qgspainteffectregistry.h"
26 #include "qgsscaleexpression.h"
27 #include "qgsdatadefined.h"
28 
29 #include "qgsfeature.h"
30 #include "qgsvectorlayer.h"
31 #include "qgslogger.h"
32 
33 #include <QDomDocument>
34 #include <QDomElement>
35 #include <QSettings> // for legend
36 
38  : mRender( true )
39 {
40 }
41 
43  : mValue( value )
44  , mSymbol( symbol )
45  , mLabel( label )
46  , mRender( render )
47 {
48 }
49 
51  : mValue( cat.mValue )
52  , mSymbol( cat.mSymbol.data() ? cat.mSymbol->clone() : nullptr )
53  , mLabel( cat.mLabel )
54  , mRender( cat.mRender )
55 {
56 }
57 
58 // copy+swap idion, the copy is done through the 'pass by value'
60 {
61  swap( cat );
62  return *this;
63 }
64 
66 {
67  qSwap( mValue, cat.mValue );
68  qSwap( mSymbol, cat.mSymbol );
69  qSwap( mLabel, cat.mLabel );
70 }
71 
73 {
74  return mValue;
75 }
76 
78 {
79  return mSymbol.data();
80 }
81 
83 {
84  return mLabel;
85 }
86 
88 {
89  return mRender;
90 }
91 
93 {
94  mValue = value;
95 }
96 
98 {
99  if ( mSymbol.data() != s ) mSymbol.reset( s );
100 }
101 
103 {
104  mLabel = label;
105 }
106 
108 {
109  mRender = render;
110 }
111 
113 {
114  return QString( "%1::%2::%3:%4\n" ).arg( mValue.toString(), mLabel, mSymbol->dump() ).arg( mRender );
115 }
116 
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  QString attr = isExpression ? mAttrName : QString( "\"%1\"" ).arg( mAttrName );
589 
590  if ( allActive && hasDefault )
591  {
592  return QString();
593  }
594  else if ( noneActive )
595  {
596  return "FALSE";
597  }
598  else if ( defaultActive )
599  {
600  return QString( "(%1) NOT IN (%2) OR (%1) IS NULL" ).arg( attr, inactiveValues );
601  }
602  else
603  {
604  return QString( "(%1) IN (%2)" ).arg( attr, activeValues );
605  }
606 }
607 
609 {
610  Q_UNUSED( context );
611  QgsSymbolV2List lst;
612  lst.reserve( mCategories.count() );
613  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
614  {
615  lst.append( cat.symbol() );
616  }
617  return lst;
618 }
619 
621 {
622  QDomElement symbolsElem = element.firstChildElement( "symbols" );
623  if ( symbolsElem.isNull() )
624  return nullptr;
625 
626  QDomElement catsElem = element.firstChildElement( "categories" );
627  if ( catsElem.isNull() )
628  return nullptr;
629 
630  QgsSymbolV2Map symbolMap = QgsSymbolLayerV2Utils::loadSymbols( symbolsElem );
631  QgsCategoryList cats;
632 
633  QDomElement catElem = catsElem.firstChildElement();
634  while ( !catElem.isNull() )
635  {
636  if ( catElem.tagName() == "category" )
637  {
638  QVariant value = QVariant( catElem.attribute( "value" ) );
639  QString symbolName = catElem.attribute( "symbol" );
640  QString label = catElem.attribute( "label" );
641  bool render = catElem.attribute( "render" ) != "false";
642  if ( symbolMap.contains( symbolName ) )
643  {
644  QgsSymbolV2* symbol = symbolMap.take( symbolName );
645  cats.append( QgsRendererCategoryV2( value, symbol, label, render ) );
646  }
647  }
648  catElem = catElem.nextSiblingElement();
649  }
650 
651  QString attrName = element.attribute( "attr" );
652 
654 
655  // delete symbols if there are any more
657 
658  // try to load source symbol (optional)
659  QDomElement sourceSymbolElem = element.firstChildElement( "source-symbol" );
660  if ( !sourceSymbolElem.isNull() )
661  {
662  QgsSymbolV2Map sourceSymbolMap = QgsSymbolLayerV2Utils::loadSymbols( sourceSymbolElem );
663  if ( sourceSymbolMap.contains( "0" ) )
664  {
665  r->setSourceSymbol( sourceSymbolMap.take( "0" ) );
666  }
667  QgsSymbolLayerV2Utils::clearSymbolMap( sourceSymbolMap );
668  }
669 
670  // try to load color ramp (optional)
671  QDomElement sourceColorRampElem = element.firstChildElement( "colorramp" );
672  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( "name" ) == "[source]" )
673  {
674  r->setSourceColorRamp( QgsSymbolLayerV2Utils::loadColorRamp( sourceColorRampElem ) );
675  QDomElement invertedColorRampElem = element.firstChildElement( "invertedcolorramp" );
676  if ( !invertedColorRampElem.isNull() )
677  r->setInvertedColorRamp( invertedColorRampElem.attribute( "value" ) == "1" );
678  }
679 
680  QDomElement rotationElem = element.firstChildElement( "rotation" );
681  if ( !rotationElem.isNull() && !rotationElem.attribute( "field" ).isEmpty() )
682  {
683  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->mCategories )
684  {
685  convertSymbolRotation( cat.symbol(), rotationElem.attribute( "field" ) );
686  }
687  if ( r->mSourceSymbol.data() )
688  {
689  convertSymbolRotation( r->mSourceSymbol.data(), rotationElem.attribute( "field" ) );
690  }
691  }
692 
693  QDomElement sizeScaleElem = element.firstChildElement( "sizescale" );
694  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( "field" ).isEmpty() )
695  {
696  Q_FOREACH ( const QgsRendererCategoryV2& cat, r->mCategories )
697  {
699  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
700  sizeScaleElem.attribute( "field" ) );
701  }
702  if ( r->mSourceSymbol.data() && r->mSourceSymbol->type() == QgsSymbolV2::Marker )
703  {
705  QgsSymbolLayerV2Utils::decodeScaleMethod( sizeScaleElem.attribute( "scalemethod" ) ),
706  sizeScaleElem.attribute( "field" ) );
707  }
708  }
709 
710  // TODO: symbol levels
711  return r;
712 }
713 
715 {
716  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
717  rendererElem.setAttribute( "type", "categorizedSymbol" );
718  rendererElem.setAttribute( "symbollevels", ( mUsingSymbolLevels ? "1" : "0" ) );
719  rendererElem.setAttribute( "forceraster", ( mForceRaster ? "1" : "0" ) );
720  rendererElem.setAttribute( "attr", mAttrName );
721 
722  // categories
723  int i = 0;
725  QDomElement catsElem = doc.createElement( "categories" );
727  for ( ; it != mCategories.end(); ++it )
728  {
729  const QgsRendererCategoryV2& cat = *it;
730  QString symbolName = QString::number( i );
731  symbols.insert( symbolName, cat.symbol() );
732 
733  QDomElement catElem = doc.createElement( "category" );
734  catElem.setAttribute( "value", cat.value().toString() );
735  catElem.setAttribute( "symbol", symbolName );
736  catElem.setAttribute( "label", cat.label() );
737  catElem.setAttribute( "render", cat.renderState() ? "true" : "false" );
738  catsElem.appendChild( catElem );
739  i++;
740  }
741 
742  rendererElem.appendChild( catsElem );
743 
744  // save symbols
745  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
746  rendererElem.appendChild( symbolsElem );
747 
748  // save source symbol
749  if ( mSourceSymbol.data() )
750  {
751  QgsSymbolV2Map sourceSymbols;
752  sourceSymbols.insert( "0", mSourceSymbol.data() );
753  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
754  rendererElem.appendChild( sourceSymbolElem );
755  }
756 
757  // save source color ramp
758  if ( mSourceColorRamp.data() )
759  {
760  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
761  rendererElem.appendChild( colorRampElem );
762  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
763  invertedElem.setAttribute( "value", mInvertedColorRamp );
764  rendererElem.appendChild( invertedElem );
765  }
766 
767  QDomElement rotationElem = doc.createElement( "rotation" );
768  if ( mRotation.data() )
770  rendererElem.appendChild( rotationElem );
771 
772  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
773  if ( mSizeScale.data() )
775  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
776  rendererElem.appendChild( sizeScaleElem );
777 
779  mPaintEffect->saveProperties( doc, rendererElem );
780 
781  if ( !mOrderBy.isEmpty() )
782  {
783  QDomElement orderBy = doc.createElement( "orderby" );
784  mOrderBy.save( orderBy );
785  rendererElem.appendChild( orderBy );
786  }
787  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
788 
789  return rendererElem;
790 }
791 
793 {
795  int count = categories().count();
796  lst.reserve( count );
797  for ( int i = 0; i < count; i++ )
798  {
799  const QgsRendererCategoryV2& cat = categories()[i];
801  lst << qMakePair( cat.label(), pix );
802  }
803  return lst;
804 }
805 
807 {
808  Q_UNUSED( scaleDenominator );
810 
811  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
812  {
813  if ( rule.isEmpty() || cat.label() == rule )
814  {
815  lst << qMakePair( cat.label(), cat.symbol() );
816  }
817  }
818  return lst;
819 }
820 
822 {
824  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
825  {
826  // check that all symbols that have the same size expression
827  QgsDataDefined ddSize;
828  Q_FOREACH ( const QgsRendererCategoryV2& category, mCategories )
829  {
830  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( category.symbol() );
831  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
832  {
833  // no common size expression
835  }
836  else
837  {
838  ddSize = symbol->dataDefinedSize();
839  }
840  }
841 
842  if ( !ddSize.isActive() || !ddSize.useExpression() )
843  {
845  }
846 
847  QgsScaleExpression exp( ddSize.expressionString() );
848  if ( exp.type() != QgsScaleExpression::Unknown )
849  {
850  QgsLegendSymbolItemV2 title( nullptr, exp.baseExpression(), "" );
851  lst << title;
852  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
853  {
855  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
857  s->setSize( exp.size( v ) );
858  lst << si;
859  }
860  // now list the categorized symbols
862  Q_FOREACH ( const QgsLegendSymbolItemV2& item, list2 )
863  lst << item;
864  return lst;
865  }
866  }
867 
869 }
870 
872 {
873  QString value = valueForFeature( feature, context ).toString();
874  int i = 0;
875 
876  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
877  {
878  if ( value == cat.value() )
879  {
880  if ( cat.renderState() )
881  return QSet< QString >() << QString::number( i );
882  else
883  return QSet< QString >();
884  }
885  i++;
886  }
887 
888  return QSet< QString >();
889 }
890 
892 {
893  return mSourceSymbol.data();
894 }
896 {
897  mSourceSymbol.reset( sym );
898 }
899 
901 {
902  return mSourceColorRamp.data();
903 }
904 
906 {
907  mSourceColorRamp.reset( ramp );
908 }
909 
911 {
912  setSourceColorRamp( ramp );
913  setInvertedColorRamp( inverted );
914  double num = mCategories.count() - 1;
915  double count = 0;
916 
917  QgsRandomColorsV2* randomRamp = dynamic_cast<QgsRandomColorsV2*>( ramp );
918  if ( randomRamp )
919  {
920  //ramp is a random colors ramp, so inform it of the total number of required colors
921  //this allows the ramp to pregenerate a set of visually distinctive colors
922  randomRamp->setTotalColorCount( mCategories.count() );
923  }
924 
925  Q_FOREACH ( const QgsRendererCategoryV2 &cat, mCategories )
926  {
927  double value = count / num;
928  if ( mInvertedColorRamp ) value = 1.0 - value;
929  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
930  count += 1;
931  }
932 }
933 
935 {
936  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
937  {
938  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
939  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
940  }
941 }
942 
944 {
945  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
946  {
947  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
948  QgsDataDefined ddAngle = s->dataDefinedAngle();
949  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
950  }
951 
952  return QString();
953 }
954 
956 {
958 }
959 
961 {
963 }
964 
966 {
967  int i = 0;
968  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
969  {
970  QgsSymbolV2* symbol = sym->clone();
971  symbol->setColor( cat.symbol()->color() );
972  updateCategorySymbol( i, symbol );
973  ++i;
974  }
975  setSourceSymbol( sym->clone() );
976 }
977 
979 {
982  for ( ; catIt != mCategories.constEnd(); ++catIt )
983  {
984  setScaleMethodToSymbol( catIt->symbol(), scaleMethod );
985  }
986 }
987 
989 {
990  return true;
991 }
992 
994 {
995  bool ok;
996  int index = key.toInt( &ok );
997  if ( ok && index >= 0 && index < mCategories.size() )
998  return mCategories.at( index ).renderState();
999  else
1000  return true;
1001 }
1002 
1004 {
1005  bool ok;
1006  int index = key.toInt( &ok );
1007  if ( ok )
1008  updateCategorySymbol( index, symbol );
1009  else
1010  delete symbol;
1011 }
1012 
1014 {
1015  bool ok;
1016  int index = key.toInt( &ok );
1017  if ( ok )
1018  updateCategoryRenderState( index, state );
1019 }
1020 
1022 {
1023  QgsCategorizedSymbolRendererV2* r = nullptr;
1024  if ( renderer->type() == "categorizedSymbol" )
1025  {
1026  r = dynamic_cast<QgsCategorizedSymbolRendererV2*>( renderer->clone() );
1027  }
1028  else if ( renderer->type() == "pointDisplacement" )
1029  {
1030  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1031  if ( pointDisplacementRenderer )
1032  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1033  }
1034  else if ( renderer->type() == "invertedPolygonRenderer" )
1035  {
1036  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1037  if ( invertedPolygonRenderer )
1038  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1039  }
1040 
1041  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1042  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1043 
1044  if ( !r )
1045  {
1047  QgsRenderContext context;
1048  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
1049  if ( !symbols.isEmpty() )
1050  {
1051  r->setSourceSymbol( symbols.at( 0 )->clone() );
1052  }
1053  }
1054 
1055  r->setOrderBy( renderer->orderBy() );
1056  r->setOrderByEnabled( renderer->orderByEnabled() );
1057 
1058  return r;
1059 }
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:104
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:503
double rendererScale() const
virtual QgsSymbolV2 * clone() const =0
Class storing parameters of a scale expression, which is a subclass of QgsExpression for expressions ...
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
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:187
Line symbol.
Definition: qgssymbolv2.h:79
const_iterator constFind(const Key &key) const
virtual void setTotalColorCount(const int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
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:78
int size() const
Q_DECL_DEPRECATED void setRotationField(const QString &fieldOrExpression) override
sets rotation field of renderer (if supported by the renderer)
QHash< QString, QgsSymbolV2 * > mSymbolHash
hashtable for faster access to symbols
virtual QDomElement save(QDomDocument &doc) override
store renderer info to XML element
void reset(T *other)
T value(int i) const
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
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer...
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:385
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:87
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:204
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:89
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