QGIS API Documentation  3.8.0-Zanzibar (11aff65)
qgscategorizedsymbolrenderer.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgscategorizedsymbolrenderer.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 
20 #include "qgssymbol.h"
21 #include "qgssymbollayerutils.h"
22 #include "qgscolorramp.h"
25 #include "qgspainteffect.h"
26 #include "qgspainteffectregistry.h"
27 #include "qgssymbollayer.h"
28 #include "qgsfeature.h"
29 #include "qgsvectorlayer.h"
30 #include "qgslogger.h"
31 #include "qgsproperty.h"
32 #include "qgsstyle.h"
33 #include "qgsfieldformatter.h"
35 #include "qgsapplication.h"
37 
38 #include <QDomDocument>
39 #include <QDomElement>
40 #include <QSettings> // for legend
41 
42 QgsRendererCategory::QgsRendererCategory( const QVariant &value, QgsSymbol *symbol, const QString &label, bool render )
43  : mValue( value )
44  , mSymbol( symbol )
45  , mLabel( label )
46  , mRender( render )
47 {
48 }
49 
51  : mValue( cat.mValue )
52  , mSymbol( cat.mSymbol ? 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  std::swap( mValue, cat.mValue );
68  std::swap( mSymbol, cat.mSymbol );
69  std::swap( mLabel, cat.mLabel );
70 }
71 
73 {
74  return mValue;
75 }
76 
78 {
79  return mSymbol.get();
80 }
81 
83 {
84  return mLabel;
85 }
86 
88 {
89  return mRender;
90 }
91 
92 void QgsRendererCategory::setValue( const QVariant &value )
93 {
94  mValue = value;
95 }
96 
98 {
99  if ( mSymbol.get() != s ) mSymbol.reset( s );
100 }
101 
102 void QgsRendererCategory::setLabel( const QString &label )
103 {
104  mLabel = label;
105 }
106 
108 {
109  mRender = render;
110 }
111 
113 {
114  return QStringLiteral( "%1::%2::%3:%4\n" ).arg( mValue.toString(), mLabel, mSymbol->dump() ).arg( mRender );
115 }
116 
117 void QgsRendererCategory::toSld( QDomDocument &doc, QDomElement &element, QgsStringMap props ) const
118 {
119  if ( !mSymbol.get() || props.value( QStringLiteral( "attribute" ), QString() ).isEmpty() )
120  return;
121 
122  QString attrName = props[ QStringLiteral( "attribute" )];
123 
124  QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
125  element.appendChild( ruleElem );
126 
127  QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
128  nameElem.appendChild( doc.createTextNode( mLabel ) );
129  ruleElem.appendChild( nameElem );
130 
131  QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
132  QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
133  QString descrStr = QStringLiteral( "%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;
140  if ( mValue.isNull() || mValue.toString().isEmpty() )
141  {
142  filterFunc = QStringLiteral( "%1 = '%2' or %1 is null" )
143  .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
144  mValue.toString().replace( '\'', QLatin1String( "''" ) ) );
145  }
146  else
147  {
148  filterFunc = QStringLiteral( "%1 = '%2'" )
149  .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
150  mValue.toString().replace( '\'', QLatin1String( "''" ) ) );
151  }
152 
153  QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc );
154 
155  // add the mix/max scale denoms if we got any from the callers
156  QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
157 
158  mSymbol->toSld( doc, ruleElem, props );
159 }
160 
162 
164  : QgsFeatureRenderer( QStringLiteral( "categorizedSymbol" ) )
165  , mAttrName( attrName )
166 {
167  //important - we need a deep copy of the categories list, not a shared copy. This is required because
168  //QgsRendererCategory::symbol() is marked const, and so retrieving the symbol via this method does not
169  //trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
170  for ( const QgsRendererCategory &cat : categories )
171  {
172  if ( !cat.symbol() )
173  {
174  QgsDebugMsg( QStringLiteral( "invalid symbol in a category! ignoring..." ) );
175  }
176  mCategories << cat;
177  }
178 }
179 
181 {
182  mSymbolHash.clear();
183 
184  for ( const QgsRendererCategory &cat : qgis::as_const( mCategories ) )
185  {
186  const QVariant val = cat.value();
187  QString valAsString;
188  if ( val.type() == QVariant::List )
189  {
190  const QVariantList list = val.toList();
191  for ( const QVariant &v : list )
192  {
193  mSymbolHash.insert( v.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
194  }
195  }
196  else
197  {
198  mSymbolHash.insert( val.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
199  }
200  }
201 }
202 
204 {
205  return nullptr;
206 }
207 
209 {
210  bool found = false;
211  return symbolForValue( value, found );
212 }
213 
214 QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value, bool &foundMatchingSymbol ) const
215 {
216  foundMatchingSymbol = false;
217 
218  // TODO: special case for int, double
219  QHash<QString, QgsSymbol *>::const_iterator it = mSymbolHash.constFind( value.isNull() ? QString() : value.toString() );
220  if ( it == mSymbolHash.constEnd() )
221  {
222  if ( mSymbolHash.isEmpty() )
223  {
224  QgsDebugMsg( QStringLiteral( "there are no hashed symbols!!!" ) );
225  }
226  else
227  {
228  QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
229  }
230  return nullptr;
231  }
232 
233  foundMatchingSymbol = true;
234 
235  return *it;
236 }
237 
239 {
240  return originalSymbolForFeature( feature, context );
241 }
242 
243 QVariant QgsCategorizedSymbolRenderer::valueForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
244 {
245  QgsAttributes attrs = feature.attributes();
246  QVariant value;
247  if ( mAttrNum == -1 )
248  {
249  Q_ASSERT( mExpression );
250 
251  value = mExpression->evaluate( &context.expressionContext() );
252  }
253  else
254  {
255  value = attrs.value( mAttrNum );
256  }
257 
258  return value;
259 }
260 
262 {
263  QVariant value = valueForFeature( feature, context );
264 
265  bool foundCategory = false;
266  // find the right symbol for the category
267  QgsSymbol *symbol = symbolForValue( value, foundCategory );
268 
269  if ( !foundCategory )
270  {
271  // if no symbol found, use default symbol
272  return symbolForValue( QVariant( "" ), foundCategory );
273  }
274 
275  return symbol;
276 }
277 
278 
280 {
281  for ( int i = 0; i < mCategories.count(); i++ )
282  {
283  if ( mCategories[i].value() == val )
284  return i;
285  }
286  return -1;
287 }
288 
290 {
291  int idx = -1;
292  for ( int i = 0; i < mCategories.count(); i++ )
293  {
294  if ( mCategories[i].label() == val )
295  {
296  if ( idx != -1 )
297  return -1;
298  else
299  idx = i;
300  }
301  }
302  return idx;
303 }
304 
305 bool QgsCategorizedSymbolRenderer::updateCategoryValue( int catIndex, const QVariant &value )
306 {
307  if ( catIndex < 0 || catIndex >= mCategories.size() )
308  return false;
309  mCategories[catIndex].setValue( value );
310  return true;
311 }
312 
314 {
315  if ( catIndex < 0 || catIndex >= mCategories.size() )
316  return false;
317  mCategories[catIndex].setSymbol( symbol );
318  return true;
319 }
320 
321 bool QgsCategorizedSymbolRenderer::updateCategoryLabel( int catIndex, const QString &label )
322 {
323  if ( catIndex < 0 || catIndex >= mCategories.size() )
324  return false;
325  mCategories[catIndex].setLabel( label );
326  return true;
327 }
328 
330 {
331  if ( catIndex < 0 || catIndex >= mCategories.size() )
332  return false;
333  mCategories[catIndex].setRenderState( render );
334  return true;
335 }
336 
338 {
339  if ( !cat.symbol() )
340  {
341  QgsDebugMsg( QStringLiteral( "invalid symbol in a category! ignoring..." ) );
342  return;
343  }
344 
345  mCategories.append( cat );
346 }
347 
349 {
350  if ( catIndex < 0 || catIndex >= mCategories.size() )
351  return false;
352 
353  mCategories.removeAt( catIndex );
354  return true;
355 }
356 
358 {
359  mCategories.clear();
360 }
361 
363 {
364  if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
365  mCategories.move( from, to );
366 }
367 
369 {
370  return qgsVariantLessThan( c1.value(), c2.value() );
371 }
373 {
374  return qgsVariantGreaterThan( c1.value(), c2.value() );
375 }
376 
377 void QgsCategorizedSymbolRenderer::sortByValue( Qt::SortOrder order )
378 {
379  if ( order == Qt::AscendingOrder )
380  {
381  std::sort( mCategories.begin(), mCategories.end(), valueLessThan );
382  }
383  else
384  {
385  std::sort( mCategories.begin(), mCategories.end(), valueGreaterThan );
386  }
387 }
388 
390 {
391  return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
392 }
393 
395 {
396  return !labelLessThan( c1, c2 );
397 }
398 
399 void QgsCategorizedSymbolRenderer::sortByLabel( Qt::SortOrder order )
400 {
401  if ( order == Qt::AscendingOrder )
402  {
403  std::sort( mCategories.begin(), mCategories.end(), labelLessThan );
404  }
405  else
406  {
407  std::sort( mCategories.begin(), mCategories.end(), labelGreaterThan );
408  }
409 }
410 
412 {
413  QgsFeatureRenderer::startRender( context, fields );
414 
415  mCounting = context.rendererScale() == 0.0;
416 
417  // make sure that the hash table is up to date
418  rebuildHash();
419 
420  // find out classification attribute index from name
421  mAttrNum = fields.lookupField( mAttrName );
422  if ( mAttrNum == -1 )
423  {
424  mExpression.reset( new QgsExpression( mAttrName ) );
425  mExpression->prepare( &context.expressionContext() );
426  }
427 
428  for ( const QgsRendererCategory &cat : qgis::as_const( mCategories ) )
429  {
430  cat.symbol()->startRender( context, fields );
431  }
432 }
433 
435 {
437 
438  for ( const QgsRendererCategory &cat : qgis::as_const( mCategories ) )
439  {
440  cat.symbol()->stopRender( context );
441  }
442  mExpression.reset();
443 }
444 
446 {
447  QSet<QString> attributes;
448 
449  // mAttrName can contain either attribute name or an expression.
450  // Sometimes it is not possible to distinguish between those two,
451  // e.g. "a - b" can be both a valid attribute name or expression.
452  // Since we do not have access to fields here, try both options.
453  attributes << mAttrName;
454 
455  QgsExpression testExpr( mAttrName );
456  if ( !testExpr.hasParserError() )
457  attributes.unite( testExpr.referencedColumns() );
458 
459  QgsCategoryList::const_iterator catIt = mCategories.constBegin();
460  for ( ; catIt != mCategories.constEnd(); ++catIt )
461  {
462  QgsSymbol *catSymbol = catIt->symbol();
463  if ( catSymbol )
464  {
465  attributes.unite( catSymbol->usedAttributes( context ) );
466  }
467  }
468  return attributes;
469 }
470 
472 {
473  QgsExpression testExpr( mAttrName );
474  if ( !testExpr.hasParserError() )
475  {
476  QgsExpressionContext context;
477  context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) ); // unfortunately no layer access available!
478  testExpr.prepare( &context );
479  return testExpr.needsGeometry();
480  }
481  return false;
482 }
483 
485 {
486  QString s = QStringLiteral( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
487  for ( int i = 0; i < mCategories.count(); i++ )
488  s += mCategories[i].dump();
489  return s;
490 }
491 
493 {
495  if ( mSourceSymbol )
496  r->setSourceSymbol( mSourceSymbol->clone() );
497  if ( mSourceColorRamp )
498  {
499  r->setSourceColorRamp( mSourceColorRamp->clone() );
500  }
503 
504  copyRendererData( r );
505  return r;
506 }
507 
508 void QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QgsStringMap &props ) const
509 {
510  QgsStringMap newProps = props;
511  newProps[ QStringLiteral( "attribute" )] = mAttrName;
512 
513  // create a Rule for each range
514  for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); ++it )
515  {
516  it->toSld( doc, element, newProps );
517  }
518 }
519 
521 {
522  int attrNum = fields.lookupField( mAttrName );
523  bool isExpression = ( attrNum == -1 );
524 
525  bool hasDefault = false;
526  bool defaultActive = false;
527  bool allActive = true;
528  bool noneActive = true;
529 
530  //we need to build lists of both inactive and active values, as either list may be required
531  //depending on whether the default category is active or not
532  QString activeValues;
533  QString inactiveValues;
534 
535  for ( const QgsRendererCategory &cat : qgis::as_const( mCategories ) )
536  {
537  if ( cat.value() == "" || cat.value().isNull() )
538  {
539  hasDefault = true;
540  defaultActive = cat.renderState();
541  }
542 
543  noneActive = noneActive && !cat.renderState();
544  allActive = allActive && cat.renderState();
545 
546  QVariant::Type valType = isExpression ? cat.value().type() : fields.at( attrNum ).type();
547  const bool isList = cat.value().type() == QVariant::List;
548  QString value = QgsExpression::quotedValue( cat.value(), valType );
549 
550  if ( !cat.renderState() )
551  {
552  if ( cat.value() != "" )
553  {
554  if ( isList )
555  {
556  const QVariantList list = cat.value().toList();
557  for ( const QVariant &v : list )
558  {
559  if ( !inactiveValues.isEmpty() )
560  inactiveValues.append( ',' );
561 
562  inactiveValues.append( QgsExpression::quotedValue( v, isExpression ? v.type() : fields.at( attrNum ).type() ) );
563  }
564  }
565  else
566  {
567  if ( !inactiveValues.isEmpty() )
568  inactiveValues.append( ',' );
569 
570  inactiveValues.append( value );
571  }
572  }
573  }
574  else
575  {
576  if ( cat.value() != "" )
577  {
578  if ( isList )
579  {
580  const QVariantList list = cat.value().toList();
581  for ( const QVariant &v : list )
582  {
583  if ( !activeValues.isEmpty() )
584  activeValues.append( ',' );
585 
586  activeValues.append( QgsExpression::quotedValue( v, isExpression ? v.type() : fields.at( attrNum ).type() ) );
587  }
588  }
589  else
590  {
591  if ( !activeValues.isEmpty() )
592  activeValues.append( ',' );
593 
594  activeValues.append( value );
595  }
596  }
597  }
598  }
599 
600  QString attr = isExpression ? mAttrName : QStringLiteral( "\"%1\"" ).arg( mAttrName );
601 
602  if ( allActive && hasDefault )
603  {
604  return QString();
605  }
606  else if ( noneActive )
607  {
608  return QStringLiteral( "FALSE" );
609  }
610  else if ( defaultActive )
611  {
612  return QStringLiteral( "(%1) NOT IN (%2) OR (%1) IS NULL" ).arg( attr, inactiveValues );
613  }
614  else
615  {
616  return QStringLiteral( "(%1) IN (%2)" ).arg( attr, activeValues );
617  }
618 }
619 
621 {
622  Q_UNUSED( context )
623  QgsSymbolList lst;
624  lst.reserve( mCategories.count() );
625  for ( const QgsRendererCategory &cat : mCategories )
626  {
627  lst.append( cat.symbol() );
628  }
629  return lst;
630 }
631 
633 {
634  QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
635  if ( symbolsElem.isNull() )
636  return nullptr;
637 
638  QDomElement catsElem = element.firstChildElement( QStringLiteral( "categories" ) );
639  if ( catsElem.isNull() )
640  return nullptr;
641 
642  QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
643  QgsCategoryList cats;
644 
645  QDomElement catElem = catsElem.firstChildElement();
646  while ( !catElem.isNull() )
647  {
648  if ( catElem.tagName() == QLatin1String( "category" ) )
649  {
650  QVariant value;
651  if ( catElem.hasAttribute( QStringLiteral( "value" ) ) )
652  {
653  value = QVariant( catElem.attribute( QStringLiteral( "value" ) ) );
654  }
655  else
656  {
657  QVariantList values;
658  QDomElement valElem = catElem.firstChildElement();
659  while ( !valElem.isNull() )
660  {
661  if ( valElem.tagName() == QLatin1String( "val" ) )
662  {
663  values << QVariant( valElem.attribute( QStringLiteral( "value" ) ) );
664  }
665  valElem = valElem.nextSiblingElement();
666  }
667  if ( !values.isEmpty() )
668  value = values;
669  }
670  QString symbolName = catElem.attribute( QStringLiteral( "symbol" ) );
671  QString label = catElem.attribute( QStringLiteral( "label" ) );
672  bool render = catElem.attribute( QStringLiteral( "render" ) ) != QLatin1String( "false" );
673  if ( symbolMap.contains( symbolName ) )
674  {
675  QgsSymbol *symbol = symbolMap.take( symbolName );
676  cats.append( QgsRendererCategory( value, symbol, label, render ) );
677  }
678  }
679  catElem = catElem.nextSiblingElement();
680  }
681 
682  QString attrName = element.attribute( QStringLiteral( "attr" ) );
683 
685 
686  // delete symbols if there are any more
688 
689  // try to load source symbol (optional)
690  QDomElement sourceSymbolElem = element.firstChildElement( QStringLiteral( "source-symbol" ) );
691  if ( !sourceSymbolElem.isNull() )
692  {
693  QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
694  if ( sourceSymbolMap.contains( QStringLiteral( "0" ) ) )
695  {
696  r->setSourceSymbol( sourceSymbolMap.take( QStringLiteral( "0" ) ) );
697  }
698  QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
699  }
700 
701  // try to load color ramp (optional)
702  QDomElement sourceColorRampElem = element.firstChildElement( QStringLiteral( "colorramp" ) );
703  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
704  {
705  r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
706  }
707 
708  QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
709  if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
710  {
711  for ( const QgsRendererCategory &cat : r->mCategories )
712  {
713  convertSymbolRotation( cat.symbol(), rotationElem.attribute( QStringLiteral( "field" ) ) );
714  }
715  if ( r->mSourceSymbol )
716  {
717  convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
718  }
719  }
720 
721  QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
722  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
723  {
724  for ( const QgsRendererCategory &cat : r->mCategories )
725  {
727  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
728  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
729  }
730  if ( r->mSourceSymbol && r->mSourceSymbol->type() == QgsSymbol::Marker )
731  {
733  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
734  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
735  }
736  }
737 
738  QDomElement ddsLegendSizeElem = element.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
739  if ( !ddsLegendSizeElem.isNull() )
740  {
741  r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
742  }
743 
744  // TODO: symbol levels
745  return r;
746 }
747 
748 QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
749 {
750  // clazy:skip
751  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
752  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "categorizedSymbol" ) );
753  rendererElem.setAttribute( QStringLiteral( "symbollevels" ), ( mUsingSymbolLevels ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
754  rendererElem.setAttribute( QStringLiteral( "forceraster" ), ( mForceRaster ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
755  rendererElem.setAttribute( QStringLiteral( "attr" ), mAttrName );
756 
757  // categories
758  if ( !mCategories.isEmpty() )
759  {
760  int i = 0;
762  QDomElement catsElem = doc.createElement( QStringLiteral( "categories" ) );
763  QgsCategoryList::const_iterator it = mCategories.constBegin();
764  for ( ; it != mCategories.constEnd(); ++it )
765  {
766  const QgsRendererCategory &cat = *it;
767  QString symbolName = QString::number( i );
768  symbols.insert( symbolName, cat.symbol() );
769 
770  QDomElement catElem = doc.createElement( QStringLiteral( "category" ) );
771  if ( cat.value().type() == QVariant::List )
772  {
773  const QVariantList list = cat.value().toList();
774  for ( const QVariant &v : list )
775  {
776  QDomElement valueElem = doc.createElement( QStringLiteral( "val" ) );
777  valueElem.setAttribute( "value", v.toString() );
778  catElem.appendChild( valueElem );
779  }
780  }
781  else
782  {
783  catElem.setAttribute( QStringLiteral( "value" ), cat.value().toString() );
784  }
785  catElem.setAttribute( QStringLiteral( "symbol" ), symbolName );
786  catElem.setAttribute( QStringLiteral( "label" ), cat.label() );
787  catElem.setAttribute( QStringLiteral( "render" ), cat.renderState() ? "true" : "false" );
788  catsElem.appendChild( catElem );
789  i++;
790  }
791  rendererElem.appendChild( catsElem );
792 
793  // save symbols
794  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
795  rendererElem.appendChild( symbolsElem );
796 
797  }
798 
799  // save source symbol
800  if ( mSourceSymbol )
801  {
802  QgsSymbolMap sourceSymbols;
803  sourceSymbols.insert( QStringLiteral( "0" ), mSourceSymbol.get() );
804  QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, QStringLiteral( "source-symbol" ), doc, context );
805  rendererElem.appendChild( sourceSymbolElem );
806  }
807 
808  // save source color ramp
809  if ( mSourceColorRamp )
810  {
811  QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), mSourceColorRamp.get(), doc );
812  rendererElem.appendChild( colorRampElem );
813  }
814 
815  QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
816  rendererElem.appendChild( rotationElem );
817 
818  QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
819  rendererElem.appendChild( sizeScaleElem );
820 
822  mPaintEffect->saveProperties( doc, rendererElem );
823 
824  if ( !mOrderBy.isEmpty() )
825  {
826  QDomElement orderBy = doc.createElement( QStringLiteral( "orderby" ) );
827  mOrderBy.save( orderBy );
828  rendererElem.appendChild( orderBy );
829  }
830  rendererElem.setAttribute( QStringLiteral( "enableorderby" ), ( mOrderByEnabled ? QStringLiteral( "1" ) : QStringLiteral( "0" ) ) );
831 
833  {
834  QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
835  mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
836  rendererElem.appendChild( ddsLegendElem );
837  }
838 
839  return rendererElem;
840 }
841 
842 
843 QgsLegendSymbolList QgsCategorizedSymbolRenderer::baseLegendSymbolItems() const
844 {
846  int i = 0;
847  for ( const QgsRendererCategory &cat : mCategories )
848  {
849  lst << QgsLegendSymbolItem( cat.symbol(), cat.label(), QString::number( i++ ), true );
850  }
851  return lst;
852 }
853 
855 {
857  {
858  // check that all symbols that have the same size expression
859  QgsProperty ddSize;
860  for ( const QgsRendererCategory &category : mCategories )
861  {
862  const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( category.symbol() );
863  if ( ddSize )
864  {
865  QgsProperty sSize( symbol->dataDefinedSize() );
866  if ( sSize != ddSize )
867  {
868  // no common size expression
869  return baseLegendSymbolItems();
870  }
871  }
872  else
873  {
874  ddSize = symbol->dataDefinedSize();
875  }
876  }
877 
878  if ( ddSize && ddSize.isActive() )
879  {
881 
883  ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
884  lst += ddSizeLegend.legendSymbolList();
885 
886  lst += baseLegendSymbolItems();
887  return lst;
888  }
889  }
890 
891  return baseLegendSymbolItems();
892 }
893 
895 {
896  QString value = valueForFeature( feature, context ).toString();
897  int i = 0;
898 
899  for ( const QgsRendererCategory &cat : mCategories )
900  {
901  bool match = false;
902  if ( cat.value().type() == QVariant::List )
903  {
904  const QVariantList list = cat.value().toList();
905  for ( const QVariant &v : list )
906  {
907  if ( value == v )
908  {
909  match = true;
910  break;
911  }
912  }
913  }
914  else
915  {
916  match = value == cat.value();
917  }
918 
919  if ( match )
920  {
921  if ( cat.renderState() || mCounting )
922  return QSet< QString >() << QString::number( i );
923  else
924  return QSet< QString >();
925  }
926  i++;
927  }
928 
929  return QSet< QString >();
930 }
931 
933 {
934  return mSourceSymbol.get();
935 }
937 {
938  mSourceSymbol.reset( sym );
939 }
940 
942 {
943  return mSourceColorRamp.get();
944 }
945 
947 {
948  mSourceColorRamp.reset( ramp );
949 }
950 
952 {
953  setSourceColorRamp( ramp );
954  double num = mCategories.count() - 1;
955  double count = 0;
956 
957  QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp );
958  if ( randomRamp )
959  {
960  //ramp is a random colors ramp, so inform it of the total number of required colors
961  //this allows the ramp to pregenerate a set of visually distinctive colors
962  randomRamp->setTotalColorCount( mCategories.count() );
963  }
964 
965  for ( const QgsRendererCategory &cat : mCategories )
966  {
967  double value = count / num;
968  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
969  count += 1;
970  }
971 }
972 
974 {
975  int i = 0;
976  for ( const QgsRendererCategory &cat : mCategories )
977  {
978  QgsSymbol *symbol = sym->clone();
979  symbol->setColor( cat.symbol()->color() );
980  updateCategorySymbol( i, symbol );
981  ++i;
982  }
983  setSourceSymbol( sym->clone() );
984 }
985 
987 {
988  return true;
989 }
990 
992 {
993  bool ok;
994  int index = key.toInt( &ok );
995  if ( ok && index >= 0 && index < mCategories.size() )
996  return mCategories.at( index ).renderState();
997  else
998  return true;
999 }
1000 
1002 {
1003  bool ok;
1004  int index = key.toInt( &ok );
1005  if ( ok )
1006  updateCategorySymbol( index, symbol );
1007  else
1008  delete symbol;
1009 }
1010 
1011 void QgsCategorizedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
1012 {
1013  bool ok;
1014  int index = key.toInt( &ok );
1015  if ( ok )
1016  updateCategoryRenderState( index, state );
1017 }
1018 
1020 {
1021  QgsCategorizedSymbolRenderer *r = nullptr;
1022  if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
1023  {
1024  r = dynamic_cast<QgsCategorizedSymbolRenderer *>( renderer->clone() );
1025  }
1026  else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1027  {
1028  const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
1029  if ( pointDistanceRenderer )
1030  r = convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1031  }
1032  else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1033  {
1034  const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
1035  if ( invertedPolygonRenderer )
1036  r = convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() );
1037  }
1038 
1039  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1040  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
1041 
1042  if ( !r )
1043  {
1044  r = new QgsCategorizedSymbolRenderer( QString(), QgsCategoryList() );
1045  QgsRenderContext context;
1046  QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
1047  if ( !symbols.isEmpty() )
1048  {
1049  r->setSourceSymbol( symbols.at( 0 )->clone() );
1050  }
1051  }
1052 
1053  r->setOrderBy( renderer->orderBy() );
1054  r->setOrderByEnabled( renderer->orderByEnabled() );
1055 
1056  return r;
1057 }
1058 
1060 {
1061  mDataDefinedSizeLegend.reset( settings );
1062 }
1063 
1065 {
1066  return mDataDefinedSizeLegend.get();
1067 }
1068 
1069 int QgsCategorizedSymbolRenderer::matchToSymbols( QgsStyle *style, const QgsSymbol::SymbolType type, QVariantList &unmatchedCategories, QStringList &unmatchedSymbols, const bool caseSensitive, const bool useTolerantMatch )
1070 {
1071  if ( !style )
1072  return 0;
1073 
1074  int matched = 0;
1075  unmatchedSymbols = style->symbolNames();
1076  const QSet< QString > allSymbolNames = unmatchedSymbols.toSet();
1077 
1078  const QRegularExpression tolerantMatchRe( QStringLiteral( "[^\\w\\d ]" ), QRegularExpression::UseUnicodePropertiesOption );
1079 
1080  for ( int catIdx = 0; catIdx < mCategories.count(); ++catIdx )
1081  {
1082  const QVariant value = mCategories.at( catIdx ).value();
1083  const QString val = value.toString().trimmed();
1084  std::unique_ptr< QgsSymbol > symbol( style->symbol( val ) );
1085  // case-sensitive match
1086  if ( symbol && symbol->type() == type )
1087  {
1088  matched++;
1089  unmatchedSymbols.removeAll( val );
1090  updateCategorySymbol( catIdx, symbol.release() );
1091  continue;
1092  }
1093 
1094  if ( !caseSensitive || useTolerantMatch )
1095  {
1096  QString testVal = val;
1097  if ( useTolerantMatch )
1098  testVal.replace( tolerantMatchRe, QString() );
1099 
1100  bool foundMatch = false;
1101  for ( const QString &name : allSymbolNames )
1102  {
1103  QString testName = name.trimmed();
1104  if ( useTolerantMatch )
1105  testName.replace( tolerantMatchRe, QString() );
1106 
1107  if ( testName == testVal || ( !caseSensitive && testName.trimmed().compare( testVal, Qt::CaseInsensitive ) == 0 ) )
1108  {
1109  // found a case-insensitive match
1110  std::unique_ptr< QgsSymbol > symbol( style->symbol( name ) );
1111  if ( symbol && symbol->type() == type )
1112  {
1113  matched++;
1114  unmatchedSymbols.removeAll( name );
1115  updateCategorySymbol( catIdx, symbol.release() );
1116  foundMatch = true;
1117  break;
1118  }
1119  }
1120  }
1121  if ( foundMatch )
1122  continue;
1123  }
1124 
1125  unmatchedCategories << value;
1126  }
1127 
1128  return matched;
1129 }
1130 
1131 QgsCategoryList QgsCategorizedSymbolRenderer::createCategories( const QList<QVariant> &values, const QgsSymbol *symbol, QgsVectorLayer *layer, const QString &attributeName )
1132 {
1133  QgsCategoryList cats;
1134  QVariantList vals = values;
1135  // sort the categories first
1136  QgsSymbolLayerUtils::sortVariantList( vals, Qt::AscendingOrder );
1137 
1138  if ( layer && !attributeName.isNull() )
1139  {
1140  const QgsFields fields = layer->fields();
1141  for ( const QVariant &value : vals )
1142  {
1143  QgsSymbol *newSymbol = symbol->clone();
1144  if ( !value.isNull() )
1145  {
1146  int fieldIdx = fields.lookupField( attributeName );
1147  QString categoryName = value.toString();
1148  if ( fieldIdx != -1 )
1149  {
1150  const QgsField field = fields.at( fieldIdx );
1151  const QgsEditorWidgetSetup setup = field.editorWidgetSetup();
1153  categoryName = formatter->representValue( layer, fieldIdx, setup.config(), QVariant(), value );
1154  }
1155  cats.append( QgsRendererCategory( value, newSymbol, categoryName, true ) );
1156  }
1157  }
1158  }
1159 
1160  // add null (default) value
1161  QgsSymbol *newSymbol = symbol->clone();
1162  cats.append( QgsRendererCategory( QVariant(), newSymbol, QString(), true ) );
1163 
1164  return cats;
1165 }
1166 
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
int lookupField(const QString &fieldName) const
Looks up field&#39;s index from the field name.
Definition: qgsfields.cpp:324
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
Q_DECL_DEPRECATED QgsSymbol * skipRender()
Class for parsing and evaluation of expressions (formerly called "search strings").
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
The class is used as a container of context for various read/write operations on other objects...
double rendererScale() const
Returns the renderer map scale.
QgsSymbolList symbols(QgsRenderContext &context) const override
Returns list of symbols used by the renderer.
static QgsSymbol::ScaleMethod decodeScaleMethod(const QString &str)
QVariantMap config() const
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
QList< QgsLegendSymbolItem > QgsLegendSymbolList
QgsFeatureRequest::OrderBy mOrderBy
Definition: qgsrenderer.h:521
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
void updateColorRamp(QgsColorRamp *ramp)
Update the color ramp used and all symbols colors.
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:61
bool updateCategoryRenderState(int catIndex, bool render)
Changes the render state for the category with the specified index.
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, QgsStringMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
Creates a categorized renderer from an XML element.
QgsSymbol * symbol() const
Returns the symbol which will be used to render this category.
bool filterNeedsGeometry() const override
Returns true if this renderer requires the geometry to apply the filter.
QgsFeatureRequest::OrderBy orderBy() const
Gets the order in which features shall be processed by this renderer.
QgsCategorizedSymbolRenderer(const QString &attrName=QString(), const QgsCategoryList &categories=QgsCategoryList())
Constructor for QgsCategorizedSymbolRenderer.
Abstract base class for color ramps.
Definition: qgscolorramp.h:31
void moveCategory(int from, int to)
Moves an existing category at index position from to index position to.
static bool isDefaultStack(QgsPaintEffect *effect)
Tests whether a paint effect matches the default effects stack.
static QDomElement saveColorRamp(const QString &name, QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp&#39;s settings to an XML element.
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...
Container of fields for a vector layer.
Definition: qgsfields.h:42
#define RENDERER_TAG_NAME
Definition: qgsrenderer.h:49
void setUsingSymbolLevels(bool usingSymbolLevels)
Definition: qgsrenderer.h:272
void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:410
bool updateCategoryLabel(int catIndex, const QString &label)
Changes the label for the category with the specified index.
static void clearSymbolMap(QgsSymbolMap &symbols)
QgsLegendSymbolList legendSymbolItems() const override
Returns a list of symbology items for the legend.
QgsPaintEffect * mPaintEffect
Definition: qgsrenderer.h:505
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
The feature class encapsulates a single feature including its id, geometry and a list of field/values...
Definition: qgsfeature.h:55
std::unique_ptr< QgsSymbol > mSourceSymbol
int categoryIndexForLabel(const QString &val)
Returns the index of the category with the specified label (or -1 if the label was not found...
QgsLegendSymbolList legendSymbolList() const
Generates legend symbol items according to the configuration.
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:221
void swap(QgsRendererCategory &other)
QMap< QString, QString > QgsStringMap
Definition: qgis.h:587
A marker symbol type, for rendering Point and MultiPoint geometries.
Definition: qgssymbol.h:766
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:153
bool valueGreaterThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
QgsField at(int i) const
Gets field at particular index (must be in range 0..N-1)
Definition: qgsfields.cpp:163
QList< QgsRendererCategory > QgsCategoryList
SymbolType
Type of the symbol.
Definition: qgssymbol.h:83
QgsCategorizedSymbolRenderer * clone() const override
Create a deep copy of this renderer.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
store renderer info to XML element
static QgsCategoryList createCategories(const QVariantList &values, const QgsSymbol *symbol, QgsVectorLayer *layer=nullptr, const QString &fieldName=QString())
Create categories for a list of values.
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend when using data-defined size for marker symbols...
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:43
QString type() const
Definition: qgsrenderer.h:129
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void toSld(QDomDocument &doc, QDomElement &element, QgsStringMap props) const
Converts the category to a matching SLD rule, within the specified DOM document and element...
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted...
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
QStringList symbolNames()
Returns a list of names of symbols.
Definition: qgsstyle.cpp:191
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
bool labelGreaterThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
std::unique_ptr< QgsSymbol > mSymbol
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
QgsSymbol * originalSymbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns symbol for feature.
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
Sorts the existing categories by their label.
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
void updateFromSymbolAndProperty(const QgsMarkerSymbol *symbol, const QgsProperty &ddSize)
Updates the list of classes, source symbol and title label from given symbol and property.
static QgsColorRamp * loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static void convertSymbolSizeScale(QgsSymbol *symbol, QgsSymbol::ScaleMethod method, const QString &field)
bool legendSymbolItemChecked(const QString &key) override
items of symbology items in legend is checked
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer&#39;s project and layer.
void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:48
QgsSymbol * sourceSymbol()
Returns the renderer&#39;s source symbol, which is the base symbol used for the each categories&#39; symbol b...
A store for object properties.
Definition: qgsproperty.h:229
void deleteAllCategories()
Deletes all existing categories from the renderer.
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
bool orderByEnabled() const
Returns whether custom ordering will be applied before features are processed by this renderer...
bool renderState() const
Returns true if the category is currently enabled and should be rendered.
void setSymbol(QgsSymbol *s)
Sets the symbol which will be used to render this category.
bool labelLessThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
void setRenderState(bool render)
Sets whether the category is currently enabled and should be rendered.
void setOrderBy(const QgsFeatureRequest::OrderBy &orderBy)
Define the order in which features shall be processed by this renderer.
A field formatter helps to handle and display values for a field.
Totally random color ramp.
Definition: qgscolorramp.h:427
QVariant value() const
Returns the value corresponding to this category.
int matchToSymbols(QgsStyle *style, QgsSymbol::SymbolType type, QVariantList &unmatchedCategories, QStringList &unmatchedSymbols, bool caseSensitive=true, bool useTolerantMatch=false)
Replaces category symbols with the symbols from a style that have a matching name and symbol type...
Q_DECL_DEPRECATED QgsSymbol * symbolForValue(const QVariant &value) const
Returns the matching symbol corresponding to an attribute value.
QString dump() const override
Returns debug information about this renderer.
QgsExpressionContext & expressionContext()
Gets the expression context.
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
Definition: qgssymbol.cpp:1476
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
static void sortVariantList(QList< QVariant > &list, Qt::SortOrder order)
Sorts the passed list in requested order.
static void convertSymbolRotation(QgsSymbol *symbol, const QString &field)
Marker symbol.
Definition: qgssymbol.h:85
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Definition: qgssymbol.cpp:684
void addCategory(const QgsRendererCategory &category)
Adds a new category to the renderer.
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
Contains information about the context of a rendering operation.
void setSourceColorRamp(QgsColorRamp *ramp)
Sets the source color ramp.
bool usingSymbolLevels() const
Definition: qgsrenderer.h:271
virtual QString representValue(QgsVectorLayer *layer, int fieldIndex, const QVariantMap &config, const QVariant &cache, const QVariant &value) const
Create a pretty String representation of the value.
QgsColorRamp * sourceColorRamp()
Returns the source color ramp, from which each categories&#39; color is derived.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns legend keys matching a specified feature.
QgsRendererCategory & operator=(QgsRendererCategory cat)
Holder for the widget type and its configuration for a field.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:93
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
std::unique_ptr< QgsColorRamp > mSourceColorRamp
void CORE_EXPORT save(QDomElement &elem) const
Serialize to XML.
int categoryIndexForValue(const QVariant &val)
Returns the index for the category with the specified value (or -1 if not found). ...
bool updateCategoryValue(int catIndex, const QVariant &value)
Changes the value for the category with the specified index.
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
QMap< QString, QgsSymbol *> QgsSymbolMap
Definition: qgsrenderer.h:44
bool updateCategorySymbol(int catIndex, QgsSymbol *symbol)
Changes the symbol for the category with the specified index.
bool valueLessThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
QgsSymbol * symbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
To be overridden.
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:49
static QgsCategorizedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer)
creates a QgsCategorizedSymbolRenderer from an existing renderer.
QHash< QString, QgsSymbol * > mSymbolHash
hashtable for faster access to symbols
void appendScopes(const QList< QgsExpressionContextScope *> &scopes)
Appends a list of scopes to the end of the context.
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
Sorts the existing categories by their value.
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition: qgsstyle.cpp:175
void updateSymbols(QgsSymbol *sym)
Update all the symbols but leave categories and colors.
QString dump() const
Returns a string representing the categories settings, used for debugging purposes only...
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required...
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
QgsRendererCategory()=default
Constructor for QgsRendererCategory.
A vector of attributes.
Definition: qgsattributes.h:57
Represents a vector layer which manages a vector based data sets.
Object that keeps configuration of appearance of marker symbol&#39;s data-defined size in legend...
std::unique_ptr< QgsExpression > mExpression
int mAttrNum
attribute index (derived from attribute name in startRender)
void toSld(QDomDocument &doc, QDomElement &element, const QgsStringMap &props=QgsStringMap()) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
void setSourceSymbol(QgsSymbol *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each categories&#39; symbo...
void setLabel(const QString &label)
Sets the label for this category, which is used to represent the category within legends and the laye...
static QgsDataDefinedSizeLegend * readXml(const QDomElement &elem, const QgsReadWriteContext &context) SIP_FACTORY
Creates instance from given element and returns it (caller takes ownership). Returns nullptr on error...
bool deleteCategory(int catIndex)
Deletes the category with the specified index from the renderer.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
void setOrderByEnabled(bool enabled)
Sets whether custom ordering should be applied before features are processed by this renderer...
QVariant::Type type
Definition: qgsfield.h:56
void setValue(const QVariant &value)
Sets the value corresponding to this category.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
QgsAttributes attributes
Definition: qgsfeature.h:65
bool isActive() const
Returns whether the property is currently active.
std::unique_ptr< QgsDataDefinedSizeLegend > mDataDefinedSizeLegend
void setColor(const QColor &color)
Sets the color for the symbol.
Definition: qgssymbol.cpp:470
virtual bool saveProperties(QDomDocument &doc, QDomElement &element) const
Saves the current state of the effect to a DOM element.