QGIS API Documentation  2.15.0-Master (13f053b)
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  if ( !mCategories.isEmpty() )
724  {
725  int i = 0;
727  QDomElement catsElem = doc.createElement( "categories" );
729  for ( ; it != mCategories.end(); ++it )
730  {
731  const QgsRendererCategoryV2& cat = *it;
732  QString symbolName = QString::number( i );
733  symbols.insert( symbolName, cat.symbol() );
734 
735  QDomElement catElem = doc.createElement( "category" );
736  catElem.setAttribute( "value", cat.value().toString() );
737  catElem.setAttribute( "symbol", symbolName );
738  catElem.setAttribute( "label", cat.label() );
739  catElem.setAttribute( "render", cat.renderState() ? "true" : "false" );
740  catsElem.appendChild( catElem );
741  i++;
742  }
743  rendererElem.appendChild( catsElem );
744 
745  // save symbols
746  QDomElement symbolsElem = QgsSymbolLayerV2Utils::saveSymbols( symbols, "symbols", doc );
747  rendererElem.appendChild( symbolsElem );
748 
749  }
750 
751  // save source symbol
752  if ( mSourceSymbol.data() )
753  {
754  QgsSymbolV2Map sourceSymbols;
755  sourceSymbols.insert( "0", mSourceSymbol.data() );
756  QDomElement sourceSymbolElem = QgsSymbolLayerV2Utils::saveSymbols( sourceSymbols, "source-symbol", doc );
757  rendererElem.appendChild( sourceSymbolElem );
758  }
759 
760  // save source color ramp
761  if ( mSourceColorRamp.data() )
762  {
763  QDomElement colorRampElem = QgsSymbolLayerV2Utils::saveColorRamp( "[source]", mSourceColorRamp.data(), doc );
764  rendererElem.appendChild( colorRampElem );
765  QDomElement invertedElem = doc.createElement( "invertedcolorramp" );
766  invertedElem.setAttribute( "value", mInvertedColorRamp );
767  rendererElem.appendChild( invertedElem );
768  }
769 
770  QDomElement rotationElem = doc.createElement( "rotation" );
771  if ( mRotation.data() )
773  rendererElem.appendChild( rotationElem );
774 
775  QDomElement sizeScaleElem = doc.createElement( "sizescale" );
776  if ( mSizeScale.data() )
778  sizeScaleElem.setAttribute( "scalemethod", QgsSymbolLayerV2Utils::encodeScaleMethod( mScaleMethod ) );
779  rendererElem.appendChild( sizeScaleElem );
780 
782  mPaintEffect->saveProperties( doc, rendererElem );
783 
784  if ( !mOrderBy.isEmpty() )
785  {
786  QDomElement orderBy = doc.createElement( "orderby" );
787  mOrderBy.save( orderBy );
788  rendererElem.appendChild( orderBy );
789  }
790  rendererElem.setAttribute( "enableorderby", ( mOrderByEnabled ? "1" : "0" ) );
791 
792  return rendererElem;
793 }
794 
796 {
798  int count = categories().count();
799  lst.reserve( count );
800  for ( int i = 0; i < count; i++ )
801  {
802  const QgsRendererCategoryV2& cat = categories()[i];
804  lst << qMakePair( cat.label(), pix );
805  }
806  return lst;
807 }
808 
810 {
811  Q_UNUSED( scaleDenominator );
813 
814  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
815  {
816  if ( rule.isEmpty() || cat.label() == rule )
817  {
818  lst << qMakePair( cat.label(), cat.symbol() );
819  }
820  }
821  return lst;
822 }
823 
825 {
827  if ( mSourceSymbol.data() && mSourceSymbol->type() == QgsSymbolV2::Marker )
828  {
829  // check that all symbols that have the same size expression
830  QgsDataDefined ddSize;
831  Q_FOREACH ( const QgsRendererCategoryV2& category, mCategories )
832  {
833  const QgsMarkerSymbolV2 * symbol = static_cast<const QgsMarkerSymbolV2 *>( category.symbol() );
834  if ( !ddSize.hasDefaultValues() && symbol->dataDefinedSize() != ddSize )
835  {
836  // no common size expression
838  }
839  else
840  {
841  ddSize = symbol->dataDefinedSize();
842  }
843  }
844 
845  if ( !ddSize.isActive() || !ddSize.useExpression() )
846  {
848  }
849 
850  QgsScaleExpression exp( ddSize.expressionString() );
851  if ( exp.type() != QgsScaleExpression::Unknown )
852  {
853  QgsLegendSymbolItemV2 title( nullptr, exp.baseExpression(), "" );
854  lst << title;
855  Q_FOREACH ( double v, QgsSymbolLayerV2Utils::prettyBreaks( exp.minValue(), exp.maxValue(), 4 ) )
856  {
858  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( si.symbol() );
860  s->setSize( exp.size( v ) );
861  lst << si;
862  }
863  // now list the categorized symbols
865  Q_FOREACH ( const QgsLegendSymbolItemV2& item, list2 )
866  lst << item;
867  return lst;
868  }
869  }
870 
872 }
873 
875 {
876  QString value = valueForFeature( feature, context ).toString();
877  int i = 0;
878 
879  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
880  {
881  if ( value == cat.value() )
882  {
883  if ( cat.renderState() )
884  return QSet< QString >() << QString::number( i );
885  else
886  return QSet< QString >();
887  }
888  i++;
889  }
890 
891  return QSet< QString >();
892 }
893 
895 {
896  return mSourceSymbol.data();
897 }
899 {
900  mSourceSymbol.reset( sym );
901 }
902 
904 {
905  return mSourceColorRamp.data();
906 }
907 
909 {
910  mSourceColorRamp.reset( ramp );
911 }
912 
914 {
915  setSourceColorRamp( ramp );
916  setInvertedColorRamp( inverted );
917  double num = mCategories.count() - 1;
918  double count = 0;
919 
920  QgsRandomColorsV2* randomRamp = dynamic_cast<QgsRandomColorsV2*>( ramp );
921  if ( randomRamp )
922  {
923  //ramp is a random colors ramp, so inform it of the total number of required colors
924  //this allows the ramp to pregenerate a set of visually distinctive colors
925  randomRamp->setTotalColorCount( mCategories.count() );
926  }
927 
928  Q_FOREACH ( const QgsRendererCategoryV2 &cat, mCategories )
929  {
930  double value = count / num;
931  if ( mInvertedColorRamp ) value = 1.0 - value;
932  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
933  count += 1;
934  }
935 }
936 
938 {
939  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
940  {
941  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
942  s->setDataDefinedAngle( QgsDataDefined( fieldOrExpression ) );
943  }
944 }
945 
947 {
948  if ( mSourceSymbol && mSourceSymbol->type() == QgsSymbolV2::Marker )
949  {
950  QgsMarkerSymbolV2 * s = static_cast<QgsMarkerSymbolV2 *>( mSourceSymbol.data() );
951  QgsDataDefined ddAngle = s->dataDefinedAngle();
952  return ddAngle.useExpression() ? ddAngle.expressionString() : ddAngle.field();
953  }
954 
955  return QString();
956 }
957 
959 {
961 }
962 
964 {
966 }
967 
969 {
970  int i = 0;
971  Q_FOREACH ( const QgsRendererCategoryV2& cat, mCategories )
972  {
973  QgsSymbolV2* symbol = sym->clone();
974  symbol->setColor( cat.symbol()->color() );
975  updateCategorySymbol( i, symbol );
976  ++i;
977  }
978  setSourceSymbol( sym->clone() );
979 }
980 
982 {
985  for ( ; catIt != mCategories.constEnd(); ++catIt )
986  {
987  setScaleMethodToSymbol( catIt->symbol(), scaleMethod );
988  }
989 }
990 
992 {
993  return true;
994 }
995 
997 {
998  bool ok;
999  int index = key.toInt( &ok );
1000  if ( ok && index >= 0 && index < mCategories.size() )
1001  return mCategories.at( index ).renderState();
1002  else
1003  return true;
1004 }
1005 
1007 {
1008  bool ok;
1009  int index = key.toInt( &ok );
1010  if ( ok )
1011  updateCategorySymbol( index, symbol );
1012  else
1013  delete symbol;
1014 }
1015 
1017 {
1018  bool ok;
1019  int index = key.toInt( &ok );
1020  if ( ok )
1021  updateCategoryRenderState( index, state );
1022 }
1023 
1025 {
1026  QgsCategorizedSymbolRendererV2* r = nullptr;
1027  if ( renderer->type() == "categorizedSymbol" )
1028  {
1029  r = dynamic_cast<QgsCategorizedSymbolRendererV2*>( renderer->clone() );
1030  }
1031  else if ( renderer->type() == "pointDisplacement" )
1032  {
1033  const QgsPointDisplacementRenderer* pointDisplacementRenderer = dynamic_cast<const QgsPointDisplacementRenderer*>( renderer );
1034  if ( pointDisplacementRenderer )
1035  r = convertFromRenderer( pointDisplacementRenderer->embeddedRenderer() );
1036  }
1037  else if ( renderer->type() == "invertedPolygonRenderer" )
1038  {
1039  const QgsInvertedPolygonRenderer* invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer*>( renderer );
1040  if ( invertedPolygonRenderer )
1041  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1042  }
1043 
1044  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1045  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbo)
1046 
1047  if ( !r )
1048  {
1050  QgsRenderContext context;
1051  QgsSymbolV2List symbols = const_cast<QgsFeatureRendererV2 *>( renderer )->symbols( context );
1052  if ( !symbols.isEmpty() )
1053  {
1054  r->setSourceSymbol( symbols.at( 0 )->clone() );
1055  }
1056  }
1057 
1058  r->setOrderBy( renderer->orderBy() );
1059  r->setOrderByEnabled( renderer->orderByEnabled() );
1060 
1061  return r;
1062 }
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:534
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:193
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)
Compares two QVariant values and returns whether the first is greater than the second.
Definition: qgis.cpp:331
QScopedPointer< QgsSymbolV2 > mSymbol
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:263
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:86
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)
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.
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)
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)
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