QGIS API Documentation  3.24.2-Tisler (13c1a02865)
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"
23 #include "qgscolorrampimpl.h"
27 #include "qgspainteffect.h"
28 #include "qgspainteffectregistry.h"
29 #include "qgssymbollayer.h"
30 #include "qgsfeature.h"
31 #include "qgsvectorlayer.h"
32 #include "qgslogger.h"
33 #include "qgsproperty.h"
34 #include "qgsstyle.h"
35 #include "qgsfieldformatter.h"
37 #include "qgsapplication.h"
39 #include "qgsstyleentityvisitor.h"
41 #include "qgsmarkersymbol.h"
42 
43 #include <QDomDocument>
44 #include <QDomElement>
45 #include <QSettings> // for legend
46 #include <QRegularExpression>
47 
48 QgsRendererCategory::QgsRendererCategory( const QVariant &value, QgsSymbol *symbol, const QString &label, bool render )
49  : mValue( value )
50  , mSymbol( symbol )
51  , mLabel( label )
52  , mRender( render )
53 {
54 }
55 
57  : mValue( cat.mValue )
58  , mSymbol( cat.mSymbol ? cat.mSymbol->clone() : nullptr )
59  , mLabel( cat.mLabel )
60  , mRender( cat.mRender )
61 {
62 }
63 
64 // copy+swap idion, the copy is done through the 'pass by value'
66 {
67  swap( cat );
68  return *this;
69 }
70 
72 
74 {
75  std::swap( mValue, cat.mValue );
76  std::swap( mSymbol, cat.mSymbol );
77  std::swap( mLabel, cat.mLabel );
78 }
79 
81 {
82  return mValue;
83 }
84 
86 {
87  return mSymbol.get();
88 }
89 
91 {
92  return mLabel;
93 }
94 
96 {
97  return mRender;
98 }
99 
100 void QgsRendererCategory::setValue( const QVariant &value )
101 {
102  mValue = value;
103 }
104 
106 {
107  if ( mSymbol.get() != s ) mSymbol.reset( s );
108 }
109 
110 void QgsRendererCategory::setLabel( const QString &label )
111 {
112  mLabel = label;
113 }
114 
116 {
117  mRender = render;
118 }
119 
121 {
122  return QStringLiteral( "%1::%2::%3:%4\n" ).arg( mValue.toString(), mLabel, mSymbol->dump() ).arg( mRender );
123 }
124 
125 void QgsRendererCategory::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
126 {
127  if ( !mSymbol.get() || props.value( QStringLiteral( "attribute" ), QString() ).toString().isEmpty() )
128  return;
129 
130  QString attrName = props[ QStringLiteral( "attribute" )].toString();
131 
132  QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
133  element.appendChild( ruleElem );
134 
135  QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
136  nameElem.appendChild( doc.createTextNode( mLabel ) );
137  ruleElem.appendChild( nameElem );
138 
139  QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
140  QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
141  QString descrStr = QStringLiteral( "%1 is '%2'" ).arg( attrName, mValue.toString() );
142  titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
143  descrElem.appendChild( titleElem );
144  ruleElem.appendChild( descrElem );
145 
146  // create the ogc:Filter for the range
147  QString filterFunc;
148  if ( mValue.isNull() || mValue.toString().isEmpty() )
149  {
150  filterFunc = QStringLiteral( "%1 = '%2' or %1 is null" )
151  .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
152  mValue.toString().replace( '\'', QLatin1String( "''" ) ) );
153  }
154  else
155  {
156  filterFunc = QStringLiteral( "%1 = '%2'" )
157  .arg( attrName.replace( '\"', QLatin1String( "\"\"" ) ),
158  mValue.toString().replace( '\'', QLatin1String( "''" ) ) );
159  }
160 
161  QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc );
162 
163  // add the mix/max scale denoms if we got any from the callers
164  QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
165 
166  mSymbol->toSld( doc, ruleElem, props );
167 }
168 
170 
172  : QgsFeatureRenderer( QStringLiteral( "categorizedSymbol" ) )
173  , mAttrName( attrName )
174 {
175  //important - we need a deep copy of the categories list, not a shared copy. This is required because
176  //QgsRendererCategory::symbol() is marked const, and so retrieving the symbol via this method does not
177  //trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
178  for ( const QgsRendererCategory &cat : categories )
179  {
180  if ( !cat.symbol() )
181  {
182  QgsDebugMsg( QStringLiteral( "invalid symbol in a category! ignoring..." ) );
183  }
184  mCategories << cat;
185  }
186 }
187 
189 
191 {
192  mSymbolHash.clear();
193 
194  for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
195  {
196  const QVariant val = cat.value();
197  if ( val.type() == QVariant::List )
198  {
199  const QVariantList list = val.toList();
200  for ( const QVariant &v : list )
201  {
202  mSymbolHash.insert( v.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
203  }
204  }
205  else
206  {
207  mSymbolHash.insert( val.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
208  }
209  }
210 }
211 
213 {
214  return nullptr;
215 }
216 
218 {
219  bool found = false;
220  return symbolForValue( value, found );
221 }
222 
223 QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value, bool &foundMatchingSymbol ) const
224 {
225  foundMatchingSymbol = false;
226 
227  // TODO: special case for int, double
228  QHash<QString, QgsSymbol *>::const_iterator it = mSymbolHash.constFind( value.isNull() ? QString() : value.toString() );
229  if ( it == mSymbolHash.constEnd() )
230  {
231  if ( mSymbolHash.isEmpty() )
232  {
233  QgsDebugMsg( QStringLiteral( "there are no hashed symbols!!!" ) );
234  }
235  else
236  {
237  QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
238  }
239  return nullptr;
240  }
241 
242  foundMatchingSymbol = true;
243 
244  return *it;
245 }
246 
248 {
249  return originalSymbolForFeature( feature, context );
250 }
251 
252 QVariant QgsCategorizedSymbolRenderer::valueForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
253 {
254  QgsAttributes attrs = feature.attributes();
255  QVariant value;
256  if ( mAttrNum == -1 )
257  {
258  Q_ASSERT( mExpression );
259 
260  value = mExpression->evaluate( &context.expressionContext() );
261  }
262  else
263  {
264  value = attrs.value( mAttrNum );
265  }
266 
267  return value;
268 }
269 
271 {
272  QVariant value = valueForFeature( feature, context );
273 
274  bool foundCategory = false;
275  // find the right symbol for the category
276  QgsSymbol *symbol = symbolForValue( value, foundCategory );
277 
278  if ( !foundCategory )
279  {
280  // if no symbol found, use default symbol
281  return symbolForValue( QVariant( "" ), foundCategory );
282  }
283 
284  return symbol;
285 }
286 
287 
289 {
290  for ( int i = 0; i < mCategories.count(); i++ )
291  {
292  if ( mCategories[i].value() == val )
293  return i;
294  }
295  return -1;
296 }
297 
299 {
300  int idx = -1;
301  for ( int i = 0; i < mCategories.count(); i++ )
302  {
303  if ( mCategories[i].label() == val )
304  {
305  if ( idx != -1 )
306  return -1;
307  else
308  idx = i;
309  }
310  }
311  return idx;
312 }
313 
314 bool QgsCategorizedSymbolRenderer::updateCategoryValue( int catIndex, const QVariant &value )
315 {
316  if ( catIndex < 0 || catIndex >= mCategories.size() )
317  return false;
318  mCategories[catIndex].setValue( value );
319  return true;
320 }
321 
323 {
324  if ( catIndex < 0 || catIndex >= mCategories.size() )
325  return false;
326  mCategories[catIndex].setSymbol( symbol );
327  return true;
328 }
329 
330 bool QgsCategorizedSymbolRenderer::updateCategoryLabel( int catIndex, const QString &label )
331 {
332  if ( catIndex < 0 || catIndex >= mCategories.size() )
333  return false;
334  mCategories[catIndex].setLabel( label );
335  return true;
336 }
337 
339 {
340  if ( catIndex < 0 || catIndex >= mCategories.size() )
341  return false;
342  mCategories[catIndex].setRenderState( render );
343  return true;
344 }
345 
347 {
348  if ( !cat.symbol() )
349  {
350  QgsDebugMsg( QStringLiteral( "invalid symbol in a category! ignoring..." ) );
351  return;
352  }
353 
354  mCategories.append( cat );
355 }
356 
358 {
359  if ( catIndex < 0 || catIndex >= mCategories.size() )
360  return false;
361 
362  mCategories.removeAt( catIndex );
363  return true;
364 }
365 
367 {
368  mCategories.clear();
369 }
370 
372 {
373  if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
374  mCategories.move( from, to );
375 }
376 
378 {
379  return qgsVariantLessThan( c1.value(), c2.value() );
380 }
382 {
383  return qgsVariantGreaterThan( c1.value(), c2.value() );
384 }
385 
386 void QgsCategorizedSymbolRenderer::sortByValue( Qt::SortOrder order )
387 {
388  if ( order == Qt::AscendingOrder )
389  {
390  std::sort( mCategories.begin(), mCategories.end(), valueLessThan );
391  }
392  else
393  {
394  std::sort( mCategories.begin(), mCategories.end(), valueGreaterThan );
395  }
396 }
397 
399 {
400  return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
401 }
402 
404 {
405  return !labelLessThan( c1, c2 );
406 }
407 
408 void QgsCategorizedSymbolRenderer::sortByLabel( Qt::SortOrder order )
409 {
410  if ( order == Qt::AscendingOrder )
411  {
412  std::sort( mCategories.begin(), mCategories.end(), labelLessThan );
413  }
414  else
415  {
416  std::sort( mCategories.begin(), mCategories.end(), labelGreaterThan );
417  }
418 }
419 
421 {
422  QgsFeatureRenderer::startRender( context, fields );
423 
424  mCounting = context.rendererScale() == 0.0;
425 
426  // make sure that the hash table is up to date
427  rebuildHash();
428 
429  // find out classification attribute index from name
430  mAttrNum = fields.lookupField( mAttrName );
431  if ( mAttrNum == -1 )
432  {
433  mExpression.reset( new QgsExpression( mAttrName ) );
434  mExpression->prepare( &context.expressionContext() );
435  }
436 
437  for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
438  {
439  cat.symbol()->startRender( context, fields );
440  }
441 }
442 
444 {
446 
447  for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
448  {
449  cat.symbol()->stopRender( context );
450  }
451  mExpression.reset();
452 }
453 
455 {
456  QSet<QString> attributes;
457 
458  // mAttrName can contain either attribute name or an expression.
459  // Sometimes it is not possible to distinguish between those two,
460  // e.g. "a - b" can be both a valid attribute name or expression.
461  // Since we do not have access to fields here, try both options.
462  attributes << mAttrName;
463 
464  QgsExpression testExpr( mAttrName );
465  if ( !testExpr.hasParserError() )
466  attributes.unite( testExpr.referencedColumns() );
467 
468  QgsCategoryList::const_iterator catIt = mCategories.constBegin();
469  for ( ; catIt != mCategories.constEnd(); ++catIt )
470  {
471  QgsSymbol *catSymbol = catIt->symbol();
472  if ( catSymbol )
473  {
474  attributes.unite( catSymbol->usedAttributes( context ) );
475  }
476  }
477  return attributes;
478 }
479 
481 {
482  QgsExpression testExpr( mAttrName );
483  if ( !testExpr.hasParserError() )
484  {
485  QgsExpressionContext context;
486  context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) ); // unfortunately no layer access available!
487  testExpr.prepare( &context );
488  return testExpr.needsGeometry();
489  }
490  return false;
491 }
492 
494 {
495  QString s = QStringLiteral( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
496  for ( int i = 0; i < mCategories.count(); i++ )
497  s += mCategories[i].dump();
498  return s;
499 }
500 
502 {
504  if ( mSourceSymbol )
505  r->setSourceSymbol( mSourceSymbol->clone() );
506  if ( mSourceColorRamp )
507  {
508  r->setSourceColorRamp( mSourceColorRamp->clone() );
509  }
511 
512  copyRendererData( r );
513  return r;
514 }
515 
516 void QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
517 {
518  QVariantMap newProps = props;
519  newProps[ QStringLiteral( "attribute" )] = mAttrName;
520 
521  // create a Rule for each range
522  for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); ++it )
523  {
524  it->toSld( doc, element, newProps );
525  }
526 }
527 
529 {
530  int attrNum = fields.lookupField( mAttrName );
531  bool isExpression = ( attrNum == -1 );
532 
533  bool hasDefault = false;
534  bool defaultActive = false;
535  bool allActive = true;
536  bool noneActive = true;
537 
538  //we need to build lists of both inactive and active values, as either list may be required
539  //depending on whether the default category is active or not
540  QString activeValues;
541  QString inactiveValues;
542 
543  for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
544  {
545  if ( cat.value() == "" || cat.value().isNull() )
546  {
547  hasDefault = true;
548  defaultActive = cat.renderState();
549  }
550 
551  noneActive = noneActive && !cat.renderState();
552  allActive = allActive && cat.renderState();
553 
554  QVariant::Type valType = isExpression ? cat.value().type() : fields.at( attrNum ).type();
555  const bool isList = cat.value().type() == QVariant::List;
556  QString value = QgsExpression::quotedValue( cat.value(), valType );
557 
558  if ( !cat.renderState() )
559  {
560  if ( cat.value() != "" )
561  {
562  if ( isList )
563  {
564  const QVariantList list = cat.value().toList();
565  for ( const QVariant &v : list )
566  {
567  if ( !inactiveValues.isEmpty() )
568  inactiveValues.append( ',' );
569 
570  inactiveValues.append( QgsExpression::quotedValue( v, isExpression ? v.type() : fields.at( attrNum ).type() ) );
571  }
572  }
573  else
574  {
575  if ( !inactiveValues.isEmpty() )
576  inactiveValues.append( ',' );
577 
578  inactiveValues.append( value );
579  }
580  }
581  }
582  else
583  {
584  if ( cat.value() != "" )
585  {
586  if ( isList )
587  {
588  const QVariantList list = cat.value().toList();
589  for ( const QVariant &v : list )
590  {
591  if ( !activeValues.isEmpty() )
592  activeValues.append( ',' );
593 
594  activeValues.append( QgsExpression::quotedValue( v, isExpression ? v.type() : fields.at( attrNum ).type() ) );
595  }
596  }
597  else
598  {
599  if ( !activeValues.isEmpty() )
600  activeValues.append( ',' );
601 
602  activeValues.append( value );
603  }
604  }
605  }
606  }
607 
608  QString attr = isExpression ? mAttrName : QStringLiteral( "\"%1\"" ).arg( mAttrName );
609 
610  if ( allActive && hasDefault )
611  {
612  return QString();
613  }
614  else if ( noneActive )
615  {
616  return QStringLiteral( "FALSE" );
617  }
618  else if ( defaultActive )
619  {
620  return QStringLiteral( "(%1) NOT IN (%2) OR (%1) IS NULL" ).arg( attr, inactiveValues );
621  }
622  else
623  {
624  return QStringLiteral( "(%1) IN (%2)" ).arg( attr, activeValues );
625  }
626 }
627 
629 {
630  Q_UNUSED( context )
631  QgsSymbolList lst;
632  lst.reserve( mCategories.count() );
633  for ( const QgsRendererCategory &cat : mCategories )
634  {
635  lst.append( cat.symbol() );
636  }
637  return lst;
638 }
639 
641 {
642  for ( const QgsRendererCategory &cat : mCategories )
643  {
644  QgsStyleSymbolEntity entity( cat.symbol() );
645  if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, cat.value().toString(), cat.label() ) ) )
646  return false;
647  }
648 
649  if ( mSourceColorRamp )
650  {
652  if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
653  return false;
654  }
655 
656  return true;
657 }
658 
660 {
661  QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
662  if ( symbolsElem.isNull() )
663  return nullptr;
664 
665  QDomElement catsElem = element.firstChildElement( QStringLiteral( "categories" ) );
666  if ( catsElem.isNull() )
667  return nullptr;
668 
669  QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
670  QgsCategoryList cats;
671 
672  // Value from string (long, ulong, double and string)
673  const auto valueFromString = []( const QString & value, const QString & valueType ) -> QVariant
674  {
675  if ( valueType == QLatin1String( "double" ) )
676  {
677  bool ok;
678  const auto val { value.toDouble( &ok ) };
679  if ( ok )
680  {
681  return val;
682  }
683  }
684  else if ( valueType == QLatin1String( "ulong" ) )
685  {
686  bool ok;
687  const auto val { value.toULongLong( &ok ) };
688  if ( ok )
689  {
690  return val;
691  }
692  }
693  else if ( valueType == QLatin1String( "long" ) )
694  {
695  bool ok;
696  const auto val { value.toLongLong( &ok ) };
697  if ( ok )
698  {
699  return val;
700  }
701  }
702  return value;
703  };
704 
705  QDomElement catElem = catsElem.firstChildElement();
706  while ( !catElem.isNull() )
707  {
708  if ( catElem.tagName() == QLatin1String( "category" ) )
709  {
710  QVariant value;
711  if ( catElem.hasAttribute( QStringLiteral( "value" ) ) )
712  {
713  value = valueFromString( catElem.attribute( QStringLiteral( "value" ) ), catElem.attribute( QStringLiteral( "type" ), QString() ) ) ;
714 
715  }
716  else
717  {
718  QVariantList values;
719  QDomElement valElem = catElem.firstChildElement();
720  while ( !valElem.isNull() )
721  {
722  if ( valElem.tagName() == QLatin1String( "val" ) )
723  {
724  values << valueFromString( valElem.attribute( QStringLiteral( "value" ) ), valElem.attribute( QStringLiteral( "type" ), QString() ) );
725  }
726  valElem = valElem.nextSiblingElement();
727  }
728  if ( !values.isEmpty() )
729  value = values;
730  }
731  QString symbolName = catElem.attribute( QStringLiteral( "symbol" ) );
732  QString label = catElem.attribute( QStringLiteral( "label" ) );
733  bool render = catElem.attribute( QStringLiteral( "render" ) ) != QLatin1String( "false" );
734  if ( symbolMap.contains( symbolName ) )
735  {
736  QgsSymbol *symbol = symbolMap.take( symbolName );
737  cats.append( QgsRendererCategory( value, symbol, label, render ) );
738  }
739  }
740  catElem = catElem.nextSiblingElement();
741  }
742 
743  QString attrName = element.attribute( QStringLiteral( "attr" ) );
744 
746 
747  // delete symbols if there are any more
749 
750  // try to load source symbol (optional)
751  QDomElement sourceSymbolElem = element.firstChildElement( QStringLiteral( "source-symbol" ) );
752  if ( !sourceSymbolElem.isNull() )
753  {
754  QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
755  if ( sourceSymbolMap.contains( QStringLiteral( "0" ) ) )
756  {
757  r->setSourceSymbol( sourceSymbolMap.take( QStringLiteral( "0" ) ) );
758  }
759  QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
760  }
761 
762  // try to load color ramp (optional)
763  QDomElement sourceColorRampElem = element.firstChildElement( QStringLiteral( "colorramp" ) );
764  if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
765  {
766  r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
767  }
768 
769  QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
770  if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
771  {
772  for ( const QgsRendererCategory &cat : r->mCategories )
773  {
774  convertSymbolRotation( cat.symbol(), rotationElem.attribute( QStringLiteral( "field" ) ) );
775  }
776  if ( r->mSourceSymbol )
777  {
778  convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
779  }
780  }
781 
782  QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
783  if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
784  {
785  for ( const QgsRendererCategory &cat : r->mCategories )
786  {
788  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
789  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
790  }
791  if ( r->mSourceSymbol && r->mSourceSymbol->type() == Qgis::SymbolType::Marker )
792  {
794  QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
795  sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
796  }
797  }
798 
799  QDomElement ddsLegendSizeElem = element.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
800  if ( !ddsLegendSizeElem.isNull() )
801  {
802  r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
803  }
804 
805  // TODO: symbol levels
806  return r;
807 }
808 
809 QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
810 {
811  // clazy:skip
812  QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
813  rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "categorizedSymbol" ) );
814  rendererElem.setAttribute( QStringLiteral( "attr" ), mAttrName );
815 
816  // String for type
817  // We just need string and three numeric types: double, ulong and long for unsigned, signed and float/double
818  const auto stringForType = []( const QVariant::Type type ) -> QString
819  {
820  if ( type == QVariant::Char || type == QVariant::Int || type == QVariant::LongLong )
821  {
822  return QStringLiteral( "long" );
823  }
824  else if ( type == QVariant::UInt || type == QVariant::ULongLong )
825  {
826  return QStringLiteral( "ulong" );
827  }
828  else if ( type == QVariant::Double )
829  {
830  return QStringLiteral( "double" ) ;
831  }
832  else // Default: string
833  {
834  return QStringLiteral( "string" );
835  }
836  };
837 
838  // categories
839  if ( !mCategories.isEmpty() )
840  {
841  int i = 0;
843  QDomElement catsElem = doc.createElement( QStringLiteral( "categories" ) );
844  QgsCategoryList::const_iterator it = mCategories.constBegin();
845  for ( ; it != mCategories.constEnd(); ++it )
846  {
847  const QgsRendererCategory &cat = *it;
848  QString symbolName = QString::number( i );
849  symbols.insert( symbolName, cat.symbol() );
850 
851  QDomElement catElem = doc.createElement( QStringLiteral( "category" ) );
852  if ( cat.value().type() == QVariant::List )
853  {
854  const QVariantList list = cat.value().toList();
855  for ( const QVariant &v : list )
856  {
857  QDomElement valueElem = doc.createElement( QStringLiteral( "val" ) );
858  valueElem.setAttribute( QStringLiteral( "value" ), v.toString() );
859  valueElem.setAttribute( QStringLiteral( "type" ), stringForType( v.type() ) );
860  catElem.appendChild( valueElem );
861  }
862  }
863  else
864  {
865  catElem.setAttribute( QStringLiteral( "value" ), cat.value().toString() );
866  catElem.setAttribute( QStringLiteral( "type" ), stringForType( cat.value().type() ) );
867  }
868  catElem.setAttribute( QStringLiteral( "symbol" ), symbolName );
869  catElem.setAttribute( QStringLiteral( "label" ), cat.label() );
870  catElem.setAttribute( QStringLiteral( "render" ), cat.renderState() ? "true" : "false" );
871  catsElem.appendChild( catElem );
872  i++;
873  }
874  rendererElem.appendChild( catsElem );
875 
876  // save symbols
877  QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
878  rendererElem.appendChild( symbolsElem );
879  }
880 
881  // save source symbol
882  if ( mSourceSymbol )
883  {
884  QgsSymbolMap sourceSymbols;
885  sourceSymbols.insert( QStringLiteral( "0" ), mSourceSymbol.get() );
886  QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, QStringLiteral( "source-symbol" ), doc, context );
887  rendererElem.appendChild( sourceSymbolElem );
888  }
889 
890  // save source color ramp
891  if ( mSourceColorRamp )
892  {
893  QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), mSourceColorRamp.get(), doc );
894  rendererElem.appendChild( colorRampElem );
895  }
896 
897  QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
898  rendererElem.appendChild( rotationElem );
899 
900  QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
901  rendererElem.appendChild( sizeScaleElem );
902 
904  {
905  QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
906  mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
907  rendererElem.appendChild( ddsLegendElem );
908  }
909 
910  saveRendererData( doc, rendererElem, context );
911 
912  return rendererElem;
913 }
914 
915 
916 QgsLegendSymbolList QgsCategorizedSymbolRenderer::baseLegendSymbolItems() const
917 {
919  int i = 0;
920  for ( const QgsRendererCategory &cat : mCategories )
921  {
922  lst << QgsLegendSymbolItem( cat.symbol(), cat.label(), QString::number( i++ ), true );
923  }
924  return lst;
925 }
926 
928 {
929 
930  auto _displayString = [ ]( const QVariant & v, int precision ) -> QString
931  {
932 
933  if ( v.isNull() )
934  {
936  }
937 
938  const bool isNumeric {v.type() == QVariant::Double || v.type() == QVariant::Int || v.type() == QVariant::UInt || v.type() == QVariant::LongLong || v.type() == QVariant::ULongLong};
939 
940  // Special treatment for numeric types if group separator is set or decimalPoint is not a dot
941  if ( v.type() == QVariant::Double )
942  {
943  // if value doesn't contain a double (a default value expression for instance),
944  // apply no transformation
945  bool ok;
946  v.toDouble( &ok );
947  if ( !ok )
948  return v.toString();
949 
950  // Locales with decimal point != '.' or that require group separator: use QLocale
951  if ( QLocale().decimalPoint() != '.' ||
952  !( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
953  {
954  if ( precision > 0 )
955  {
956  if ( -1 < v.toDouble() && v.toDouble() < 1 )
957  {
958  return QLocale().toString( v.toDouble(), 'g', precision );
959  }
960  else
961  {
962  return QLocale().toString( v.toDouble(), 'f', precision );
963  }
964  }
965  else
966  {
967  // Precision is not set, let's guess it from the
968  // standard conversion to string
969  const QString s( v.toString() );
970  const int dotPosition( s.indexOf( '.' ) );
971  int precision;
972  if ( dotPosition < 0 && s.indexOf( 'e' ) < 0 )
973  {
974  precision = 0;
975  return QLocale().toString( v.toDouble(), 'f', precision );
976  }
977  else
978  {
979  if ( dotPosition < 0 ) precision = 0;
980  else precision = s.length() - dotPosition - 1;
981 
982  if ( -1 < v.toDouble() && v.toDouble() < 1 )
983  {
984  return QLocale().toString( v.toDouble(), 'g', precision );
985  }
986  else
987  {
988  return QLocale().toString( v.toDouble(), 'f', precision );
989  }
990  }
991  }
992  }
993  // Default for doubles with precision
994  else if ( precision > 0 )
995  {
996  if ( -1 < v.toDouble() && v.toDouble() < 1 )
997  {
998  return QString::number( v.toDouble(), 'g', precision );
999  }
1000  else
1001  {
1002  return QString::number( v.toDouble(), 'f', precision );
1003  }
1004  }
1005  }
1006  // Other numeric types than doubles
1007  else if ( isNumeric &&
1008  !( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
1009  {
1010  bool ok;
1011  const qlonglong converted( v.toLongLong( &ok ) );
1012  if ( ok )
1013  return QLocale().toString( converted );
1014  }
1015  else if ( v.type() == QVariant::ByteArray )
1016  {
1017  return QObject::tr( "BLOB" );
1018  }
1019 
1020  // Fallback if special rules do not apply
1021  return v.toString();
1022  };
1023 
1024  if ( v.type() == QVariant::StringList || v.type() == QVariant::List )
1025  {
1026  // Note that this code is never hit because the joining of lists (merged categories) happens
1027  // in data(); I'm leaving this here anyway because it is tested and it may be useful for
1028  // other purposes in the future.
1029  QString result;
1030  const QVariantList list = v.toList();
1031  for ( const QVariant &var : list )
1032  {
1033  if ( !result.isEmpty() )
1034  {
1035  result.append( ';' );
1036  }
1037  result.append( _displayString( var, precision ) );
1038  }
1039  return result;
1040  }
1041  else
1042  {
1043  return _displayString( v, precision );
1044  }
1045 }
1046 
1048 {
1050  {
1051  // check that all symbols that have the same size expression
1052  QgsProperty ddSize;
1053  for ( const QgsRendererCategory &category : mCategories )
1054  {
1055  const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( category.symbol() );
1056  if ( ddSize )
1057  {
1058  QgsProperty sSize( symbol->dataDefinedSize() );
1059  if ( sSize != ddSize )
1060  {
1061  // no common size expression
1062  return baseLegendSymbolItems();
1063  }
1064  }
1065  else
1066  {
1067  ddSize = symbol->dataDefinedSize();
1068  }
1069  }
1070 
1071  if ( ddSize && ddSize.isActive() )
1072  {
1073  QgsLegendSymbolList lst;
1074 
1076  ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
1077  lst += ddSizeLegend.legendSymbolList();
1078 
1079  lst += baseLegendSymbolItems();
1080  return lst;
1081  }
1082  }
1083 
1084  return baseLegendSymbolItems();
1085 }
1086 
1088 {
1089  const QVariant value = valueForFeature( feature, context );
1090  int i = 0;
1091 
1092  for ( const QgsRendererCategory &cat : mCategories )
1093  {
1094  bool match = false;
1095  if ( cat.value().type() == QVariant::List )
1096  {
1097  const QVariantList list = cat.value().toList();
1098  for ( const QVariant &v : list )
1099  {
1100  if ( value == v )
1101  {
1102  match = true;
1103  break;
1104  }
1105  }
1106  }
1107  else
1108  {
1109  // Numeric NULL cat value is stored as an empty string
1110  if ( value.isNull() && ( value.type() == QVariant::Double || value.type() == QVariant::Int ||
1111  value.type() == QVariant::UInt || value.type() == QVariant::LongLong ||
1112  value.type() == QVariant::ULongLong ) )
1113  {
1114  match = cat.value().toString().isEmpty();
1115  }
1116  else
1117  {
1118  match = value == cat.value();
1119  }
1120  }
1121 
1122  if ( match )
1123  {
1124  if ( cat.renderState() || mCounting )
1125  return QSet< QString >() << QString::number( i );
1126  else
1127  return QSet< QString >();
1128  }
1129  i++;
1130  }
1131 
1132  return QSet< QString >();
1133 }
1134 
1136 {
1137  return mSourceSymbol.get();
1138 }
1139 
1141 {
1142  return mSourceSymbol.get();
1143 }
1144 
1146 {
1147  mSourceSymbol.reset( sym );
1148 }
1149 
1151 {
1152  return mSourceColorRamp.get();
1153 }
1154 
1156 {
1157  return mSourceColorRamp.get();
1158 }
1159 
1161 {
1162  mSourceColorRamp.reset( ramp );
1163 }
1164 
1166 {
1167  setSourceColorRamp( ramp );
1168  double num = mCategories.count() - 1;
1169  double count = 0;
1170 
1171  QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp );
1172  if ( randomRamp )
1173  {
1174  //ramp is a random colors ramp, so inform it of the total number of required colors
1175  //this allows the ramp to pregenerate a set of visually distinctive colors
1176  randomRamp->setTotalColorCount( mCategories.count() );
1177  }
1178 
1179  for ( const QgsRendererCategory &cat : mCategories )
1180  {
1181  double value = count / num;
1182  cat.symbol()->setColor( mSourceColorRamp->color( value ) );
1183  count += 1;
1184  }
1185 }
1186 
1188 {
1189  int i = 0;
1190  for ( const QgsRendererCategory &cat : mCategories )
1191  {
1192  QgsSymbol *symbol = sym->clone();
1193  symbol->setColor( cat.symbol()->color() );
1194  updateCategorySymbol( i, symbol );
1195  ++i;
1196  }
1197  setSourceSymbol( sym->clone() );
1198 }
1199 
1201 {
1202  return true;
1203 }
1204 
1206 {
1207  bool ok;
1208  int index = key.toInt( &ok );
1209  if ( ok && index >= 0 && index < mCategories.size() )
1210  return mCategories.at( index ).renderState();
1211  else
1212  return true;
1213 }
1214 
1216 {
1217  bool ok;
1218  int index = key.toInt( &ok );
1219  if ( ok )
1220  updateCategorySymbol( index, symbol );
1221  else
1222  delete symbol;
1223 }
1224 
1225 void QgsCategorizedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
1226 {
1227  bool ok;
1228  int index = key.toInt( &ok );
1229  if ( ok )
1230  updateCategoryRenderState( index, state );
1231 }
1232 
1234 {
1235  std::unique_ptr< QgsCategorizedSymbolRenderer > r;
1236  if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
1237  {
1238  r.reset( static_cast<QgsCategorizedSymbolRenderer *>( renderer->clone() ) );
1239  }
1240  else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1241  {
1242  const QgsGraduatedSymbolRenderer *graduatedSymbolRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1243  if ( graduatedSymbolRenderer )
1244  {
1245  r.reset( new QgsCategorizedSymbolRenderer( QString(), QgsCategoryList() ) );
1246  if ( graduatedSymbolRenderer->sourceSymbol() )
1247  r->setSourceSymbol( graduatedSymbolRenderer->sourceSymbol()->clone() );
1248  if ( graduatedSymbolRenderer->sourceColorRamp() )
1249  {
1250  r->setSourceColorRamp( graduatedSymbolRenderer->sourceColorRamp()->clone() );
1251  }
1252  r->setClassAttribute( graduatedSymbolRenderer->classAttribute() );
1253  }
1254  }
1255  else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1256  {
1257  const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
1258  if ( pointDistanceRenderer )
1259  r.reset( convertFromRenderer( pointDistanceRenderer->embeddedRenderer() ) );
1260  }
1261  else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1262  {
1263  const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
1264  if ( invertedPolygonRenderer )
1265  r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1266  }
1267  else if ( renderer->type() == QLatin1String( "embeddedSymbol" ) && layer )
1268  {
1269  const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
1271  QgsFeatureRequest req;
1273  req.setNoAttributes();
1274  QgsFeatureIterator it = layer->getFeatures( req );
1275  QgsFeature feature;
1276  while ( it.nextFeature( feature ) && categories.size() < 2000 )
1277  {
1278  if ( feature.embeddedSymbol() )
1279  categories.append( QgsRendererCategory( feature.id(), feature.embeddedSymbol()->clone(), QString::number( feature.id() ) ) );
1280  }
1281  categories.append( QgsRendererCategory( QVariant(), embeddedRenderer->defaultSymbol()->clone(), QString() ) );
1282  r.reset( new QgsCategorizedSymbolRenderer( QStringLiteral( "$id" ), categories ) );
1283  }
1284 
1285  // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1286  // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
1287 
1288  if ( !r )
1289  {
1290  r = std::make_unique< QgsCategorizedSymbolRenderer >( QString(), QgsCategoryList() );
1291  QgsRenderContext context;
1292  QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
1293  if ( !symbols.isEmpty() )
1294  {
1295  r->setSourceSymbol( symbols.at( 0 )->clone() );
1296  }
1297  }
1298 
1299  renderer->copyRendererData( r.get() );
1300 
1301  return r.release();
1302 }
1303 
1305 {
1306  mDataDefinedSizeLegend.reset( settings );
1307 }
1308 
1310 {
1311  return mDataDefinedSizeLegend.get();
1312 }
1313 
1314 int QgsCategorizedSymbolRenderer::matchToSymbols( QgsStyle *style, Qgis::SymbolType type, QVariantList &unmatchedCategories, QStringList &unmatchedSymbols, const bool caseSensitive, const bool useTolerantMatch )
1315 {
1316  if ( !style )
1317  return 0;
1318 
1319  int matched = 0;
1320  unmatchedSymbols = style->symbolNames();
1321  const QSet< QString > allSymbolNames = qgis::listToSet( unmatchedSymbols );
1322 
1323  const QRegularExpression tolerantMatchRe( QStringLiteral( "[^\\w\\d ]" ), QRegularExpression::UseUnicodePropertiesOption );
1324 
1325  for ( int catIdx = 0; catIdx < mCategories.count(); ++catIdx )
1326  {
1327  const QVariant value = mCategories.at( catIdx ).value();
1328  const QString val = value.toString().trimmed();
1329  std::unique_ptr< QgsSymbol > symbol( style->symbol( val ) );
1330  // case-sensitive match
1331  if ( symbol && symbol->type() == type )
1332  {
1333  matched++;
1334  unmatchedSymbols.removeAll( val );
1335  updateCategorySymbol( catIdx, symbol.release() );
1336  continue;
1337  }
1338 
1339  if ( !caseSensitive || useTolerantMatch )
1340  {
1341  QString testVal = val;
1342  if ( useTolerantMatch )
1343  testVal.replace( tolerantMatchRe, QString() );
1344 
1345  bool foundMatch = false;
1346  for ( const QString &name : allSymbolNames )
1347  {
1348  QString testName = name.trimmed();
1349  if ( useTolerantMatch )
1350  testName.replace( tolerantMatchRe, QString() );
1351 
1352  if ( testName == testVal || ( !caseSensitive && testName.trimmed().compare( testVal, Qt::CaseInsensitive ) == 0 ) )
1353  {
1354  // found a case-insensitive match
1355  std::unique_ptr< QgsSymbol > symbol( style->symbol( name ) );
1356  if ( symbol && symbol->type() == type )
1357  {
1358  matched++;
1359  unmatchedSymbols.removeAll( name );
1360  updateCategorySymbol( catIdx, symbol.release() );
1361  foundMatch = true;
1362  break;
1363  }
1364  }
1365  }
1366  if ( foundMatch )
1367  continue;
1368  }
1369 
1370  unmatchedCategories << value;
1371  }
1372 
1373  return matched;
1374 }
1375 
1376 QgsCategoryList QgsCategorizedSymbolRenderer::createCategories( const QList<QVariant> &values, const QgsSymbol *symbol, QgsVectorLayer *layer, const QString &attributeName )
1377 {
1378  QgsCategoryList cats;
1379  QVariantList vals = values;
1380  // sort the categories first
1381  QgsSymbolLayerUtils::sortVariantList( vals, Qt::AscendingOrder );
1382 
1383  if ( layer && !attributeName.isNull() )
1384  {
1385  const QgsFields fields = layer->fields();
1386  for ( const QVariant &value : vals )
1387  {
1388  QgsSymbol *newSymbol = symbol->clone();
1389  if ( !value.isNull() )
1390  {
1391  const int fieldIdx = fields.lookupField( attributeName );
1392  QString categoryName = displayString( value );
1393  if ( fieldIdx != -1 )
1394  {
1395  const QgsField field = fields.at( fieldIdx );
1398  categoryName = formatter->representValue( layer, fieldIdx, setup.config(), QVariant(), value );
1399  }
1400  cats.append( QgsRendererCategory( value, newSymbol, categoryName, true ) );
1401  }
1402  }
1403  }
1404 
1405  // add null (default) value
1406  QgsSymbol *newSymbol = symbol->clone();
1407  cats.append( QgsRendererCategory( QVariant(), newSymbol, QString(), true ) );
1408 
1409  return cats;
1410 }
1411 
SymbolType
Symbol types.
Definition: qgis.h:183
@ Marker
Marker symbol.
static QString nullRepresentation()
This string is used to represent the value NULL throughout QGIS.
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
A vector of attributes.
Definition: qgsattributes.h:58
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
Sorts the existing categories by their value.
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
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...
QgsSymbol * symbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
To be overridden.
void updateSymbols(QgsSymbol *sym)
Update all the symbols but leave categories and colors.
bool updateCategoryRenderState(int catIndex, bool render)
Changes the render state for the category with the specified index.
void setSourceColorRamp(QgsColorRamp *ramp)
Sets the source color ramp.
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
QgsSymbol * sourceSymbol()
Returns the renderer's source symbol, which is the base symbol used for the each categories' symbol b...
int matchToSymbols(QgsStyle *style, Qgis::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.
std::unique_ptr< QgsColorRamp > mSourceColorRamp
Q_DECL_DEPRECATED QgsSymbol * symbolForValue(const QVariant &value) const
Returns the matching symbol corresponding to an attribute value.
std::unique_ptr< QgsSymbol > mSourceSymbol
static QgsCategorizedSymbolRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer, QgsVectorLayer *layer=nullptr)
Creates a new QgsCategorizedSymbolRenderer from an existing renderer.
void updateColorRamp(QgsColorRamp *ramp)
Update the color ramp used and all symbols colors.
QgsDataDefinedSizeLegend * dataDefinedSizeLegend() const
Returns configuration of appearance of legend when using data-defined size for marker symbols.
static QgsCategoryList createCategories(const QVariantList &values, const QgsSymbol *symbol, QgsVectorLayer *layer=nullptr, const QString &fieldName=QString())
Create categories for a list of values.
QHash< QString, QgsSymbol * > mSymbolHash
hashtable for faster access to symbols
bool filterNeedsGeometry() const override
Returns true if this renderer requires the geometry to apply the filter.
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
void setSourceSymbol(QgsSymbol *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each categories' symbo...
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
QgsSymbolList symbols(QgsRenderContext &context) const override
Returns list of symbols used by the renderer.
int categoryIndexForValue(const QVariant &val)
Returns the index for the category with the specified value (or -1 if not found).
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
Creates a categorized renderer from an XML element.
bool updateCategorySymbol(int catIndex, QgsSymbol *symbol)
Changes the symbol for the category with the specified index.
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
std::unique_ptr< QgsExpression > mExpression
std::unique_ptr< QgsDataDefinedSizeLegend > mDataDefinedSizeLegend
bool legendSymbolItemChecked(const QString &key) override
items of symbology items in legend is checked
bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
void addCategory(const QgsRendererCategory &category)
Adds a new category to the renderer.
QgsCategorizedSymbolRenderer(const QString &attrName=QString(), const QgsCategoryList &categories=QgsCategoryList())
Constructor for QgsCategorizedSymbolRenderer.
void sortByLabel(Qt::SortOrder order=Qt::AscendingOrder)
Sorts the existing categories by their label.
QgsSymbol * originalSymbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns symbol for feature.
int mAttrNum
attribute index (derived from attribute name in startRender)
QgsLegendSymbolList legendSymbolItems() const override
Returns a list of symbology items for the legend.
void moveCategory(int from, int to)
Moves an existing category at index position from to index position to.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
Stores renderer properties to an XML element.
bool deleteCategory(int catIndex)
Deletes the category with the specified index from the renderer.
Q_DECL_DEPRECATED QgsSymbol * skipRender()
void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
QString dump() const override
Returns debug information about this renderer.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns legend keys matching a specified feature.
QgsColorRamp * sourceColorRamp()
Returns the source color ramp, from which each categories' color is derived.
bool updateCategoryValue(int catIndex, const QVariant &value)
Changes the value for the category with the specified index.
void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props=QVariantMap()) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
QgsCategorizedSymbolRenderer * clone() const override
Create a deep copy of this renderer.
void deleteAllCategories()
Deletes all existing categories from the renderer.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
bool updateCategoryLabel(int catIndex, const QString &label)
Changes the label for the category with the specified index.
static QString displayString(const QVariant &value, int precision=-1)
Returns a localized representation of value with the given precision, if precision is -1 then precisi...
~QgsCategorizedSymbolRenderer() override
int categoryIndexForLabel(const QString &val)
Returns the index of the category with the specified label (or -1 if the label was not found,...
Abstract base class for color ramps.
Definition: qgscolorramp.h:30
virtual QgsColorRamp * clone() const =0
Creates a clone of the color ramp.
Object that keeps configuration of appearance of marker symbol's data-defined size in legend.
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...
void updateFromSymbolAndProperty(const QgsMarkerSymbol *symbol, const QgsProperty &ddSize)
Updates the list of classes, source symbol and title label from given symbol and property.
QgsLegendSymbolList legendSymbolList() const
Generates legend symbol items according to the configuration.
Holder for the widget type and its configuration for a field.
QVariantMap config() const
A vector feature renderer which uses embedded feature symbology to render per-feature symbols.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
bool needsGeometry() const
Returns true if the expression uses feature geometry for some computation.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
QString type() const
Definition: qgsrenderer.h:142
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:52
static void convertSymbolRotation(QgsSymbol *symbol, const QString &field)
void saveRendererData(QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context)
Saves generic renderer data into the specified element.
virtual const QgsFeatureRenderer * embeddedRenderer() const
Returns the current embedded renderer (subrenderer) for this feature renderer.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:96
static void convertSymbolSizeScale(QgsSymbol *symbol, Qgis::ScaleMethod method, const QString &field)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
@ EmbeddedSymbols
Retrieve any embedded feature symbology (since QGIS 3.20)
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QgsAttributes attributes
Definition: qgsfeature.h:65
const QgsSymbol * embeddedSymbol() const
Returns the feature's embedded symbology, or nullptr if the feature has no embedded symbol.
Definition: qgsfeature.cpp:306
Q_GADGET QgsFeatureId id
Definition: qgsfeature.h:64
QgsFieldFormatter * fieldFormatter(const QString &id) const
Gets a field formatter by its id.
A field formatter helps to handle and display values for a field.
Encapsulate a field in an attribute table or data source.
Definition: qgsfield.h:51
QVariant::Type type
Definition: qgsfield.h:58
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition: qgsfield.cpp:602
Container of fields for a vector layer.
Definition: qgsfields.h:45
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Definition: qgsfields.cpp:163
int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
Definition: qgsfields.cpp:349
QgsSymbol * sourceSymbol()
Returns the renderer's source symbol, which is the base symbol used for the each classes' symbol befo...
QgsColorRamp * sourceColorRamp()
Returns the source color ramp, from which each classes' color is derived.
void setSourceSymbol(QgsSymbol *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each classes' symbol b...
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted,...
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
A marker symbol type, for rendering Point and MultiPoint geometries.
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
A store for object properties.
Definition: qgsproperty.h:231
bool isActive() const
Returns whether the property is currently active.
Totally random color ramp.
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
double rendererScale() const
Returns the renderer map scale.
QgsExpressionContext & expressionContext()
Gets the expression context.
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
void setRenderState(bool render)
Sets whether the category is currently enabled and should be rendered.
void swap(QgsRendererCategory &other)
std::unique_ptr< QgsSymbol > mSymbol
QgsSymbol * symbol() const
Returns the symbol which will be used to render this category.
void setSymbol(QgsSymbol *s)
Sets the symbol which will be used to render this category.
QgsRendererCategory()=default
Constructor for QgsRendererCategory.
bool renderState() const
Returns true if the category is currently enabled and should be rendered.
QString dump() const
Returns a string representing the categories settings, used for debugging purposes only.
void setLabel(const QString &label)
Sets the label for this category, which is used to represent the category within legends and the laye...
void toSld(QDomDocument &doc, QDomElement &element, QVariantMap props) const
Converts the category to a matching SLD rule, within the specified DOM document and element.
void setValue(const QVariant &value)
Sets the value corresponding to this category.
QVariant value() const
Returns the value corresponding to this category.
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
QgsRendererCategory & operator=(QgsRendererCategory cat)
A color ramp entity for QgsStyle databases.
Definition: qgsstyle.h:1254
An interface for classes which can visit style entity (e.g.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A symbol entity for QgsStyle databases.
Definition: qgsstyle.h:1222
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition: qgsstyle.cpp:278
QStringList symbolNames() const
Returns a list of names of symbols.
Definition: qgsstyle.cpp:294
static void sortVariantList(QList< QVariant > &list, Qt::SortOrder order)
Sorts the passed list in requested order.
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, QVariantMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
static QgsColorRamp * loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static Qgis::ScaleMethod decodeScaleMethod(const QString &str)
Decodes a symbol scale method from a string.
static QDomElement saveColorRamp(const QString &name, QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp's settings to an XML element.
static void clearSymbolMap(QgsSymbolMap &symbols)
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 ...
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.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:38
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
Definition: qgssymbol.cpp:825
void setColor(const QColor &color)
Sets the color for the symbol.
Definition: qgssymbol.cpp:541
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
Represents a vector layer which manages a vector based data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
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:119
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:187
bool labelGreaterThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
bool valueLessThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
bool valueGreaterThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
bool labelLessThan(const QgsRendererCategory &c1, const QgsRendererCategory &c2)
QList< QgsRendererCategory > QgsCategoryList
const QgsField & field
Definition: qgsfield.h:463
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsgLevel(str, level)
Definition: qgslogger.h:39
#define QgsDebugMsg(str)
Definition: qgslogger.h:38
#define RENDERER_TAG_NAME
Definition: qgsrenderer.h:50
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition: qgsrenderer.h:45
QList< QgsSymbol * > QgsSymbolList
Definition: qgsrenderer.h:44
int precision
Contains information relating to the style entity currently being visited.