QGIS API Documentation 3.99.0-Master (d270888f95f)
Loading...
Searching...
No Matches
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 ***************************************************************************/
16
17#include <algorithm>
18#include <memory>
19
20#include "qgsapplication.h"
21#include "qgscolorramp.h"
22#include "qgscolorrampimpl.h"
27#include "qgsfeature.h"
28#include "qgsfieldformatter.h"
32#include "qgslogger.h"
33#include "qgsmarkersymbol.h"
34#include "qgspainteffect.h"
36#include "qgsproperty.h"
38#include "qgssldexportcontext.h"
39#include "qgsstyle.h"
41#include "qgssymbol.h"
42#include "qgssymbollayer.h"
43#include "qgssymbollayerutils.h"
44#include "qgsvariantutils.h"
45#include "qgsvectorlayer.h"
46
47#include <QDomDocument>
48#include <QDomElement>
49#include <QRegularExpression>
50#include <QSettings>
51#include <QString>
52#include <QUuid>
53
54using namespace Qt::StringLiterals;
55
56QgsRendererCategory::QgsRendererCategory( const QVariant &value, QgsSymbol *symbol, const QString &label, bool render, const QString &uuid )
57 : mValue( value )
58 , mSymbol( symbol )
59 , mLabel( label )
60 , mRender( render )
61{
62 mUuid = !uuid.isEmpty() ? uuid : QUuid::createUuid().toString();
63}
64
66 : mValue( cat.mValue )
67 , mSymbol( cat.mSymbol ? cat.mSymbol->clone() : nullptr )
68 , mLabel( cat.mLabel )
69 , mRender( cat.mRender )
70 , mUuid( cat.mUuid )
71{
72}
73
75{
76 if ( &cat == this )
77 return *this;
78
79 mValue = cat.mValue;
80 mSymbol.reset( cat.mSymbol ? cat.mSymbol->clone() : nullptr );
81 mLabel = cat.mLabel;
82 mRender = cat.mRender;
83 mUuid = cat.mUuid;
84 return *this;
85}
86
88
90{
91 return mUuid;
92}
93
95{
96 return mValue;
97}
98
100{
101 return mSymbol.get();
102}
103
105{
106 return mLabel;
107}
108
110{
111 return mRender;
112}
113
115{
116 mValue = value;
117}
118
120{
121 if ( mSymbol.get() != s ) mSymbol.reset( s );
122}
123
125{
126 mLabel = label;
127}
128
130{
131 mRender = render;
132}
133
135{
136 return u"%1::%2::%3:%4\n"_s.arg( mValue.toString(), mLabel, mSymbol->dump() ).arg( mRender );
137}
138
139void QgsRendererCategory::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
140{
141 if ( !mSymbol.get() || props.value( u"attribute"_s, QString() ).toString().isEmpty() )
142 return;
143
144 QString attrName = props[ u"attribute"_s].toString();
145
146 QgsSldExportContext context;
147 context.setExtraProperties( props );
148 toSld( doc, element, attrName, context );
149}
150
151bool QgsRendererCategory::toSld( QDomDocument &doc, QDomElement &element, const QString &classAttribute, QgsSldExportContext &context ) const
152{
153 if ( !mSymbol.get() || classAttribute.isEmpty() )
154 return false;
155
156 QString attrName = classAttribute;
157
158 // try to determine if attribute name is actually a field reference or expression.
159 // If it's a field reference, we need to quote it.
160 // Because we don't have access to the layer or fields here, we treat a parser error
161 // as just an unquoted field name (eg a field name with spaces)
162 const QgsExpression attrExpression = QgsExpression( attrName );
163 if ( attrExpression.hasParserError() )
164 {
165 attrName = QgsExpression::quotedColumnRef( attrName );
166 }
167 else if ( attrExpression.isField() )
168 {
170 qgis::down_cast<const QgsExpressionNodeColumnRef *>( attrExpression.rootNode() )->name()
171 );
172 }
173
174 QDomElement ruleElem = doc.createElement( u"se:Rule"_s );
175
176 QDomElement nameElem = doc.createElement( u"se:Name"_s );
177 nameElem.appendChild( doc.createTextNode( mLabel ) );
178 ruleElem.appendChild( nameElem );
179
180 QDomElement descrElem = doc.createElement( u"se:Description"_s );
181 QDomElement titleElem = doc.createElement( u"se:Title"_s );
182 QString descrStr = u"%1 is '%2'"_s.arg( attrName, mValue.toString() );
183 titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
184 descrElem.appendChild( titleElem );
185 ruleElem.appendChild( descrElem );
186
187 // create the ogc:Filter for the range
188 QString filterFunc;
189 if ( mValue.userType() == QMetaType::Type::QVariantList )
190 {
191 const QVariantList list = mValue.toList();
192 if ( list.size() == 1 )
193 {
194 filterFunc = u"%1 = %2"_s.arg( attrName, QgsExpression::quotedValue( list.at( 0 ) ) );
195 }
196 else
197 {
198 QStringList valuesList;
199 valuesList.reserve( list.size() );
200 for ( const QVariant &v : list )
201 {
202 valuesList << QgsExpression::quotedValue( v );
203 }
204 filterFunc = u"%1 IN (%2)"_s.arg( attrName,
205 valuesList.join( ',' ) );
206 }
207 }
208 else if ( QgsVariantUtils::isNull( mValue ) || mValue.toString().isEmpty() )
209 {
210 filterFunc = u"ELSE"_s;
211 }
212 else
213 {
214 filterFunc = u"%1 = %2"_s.arg( attrName, QgsExpression::quotedValue( mValue ) );
215 }
216
217 QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc, context );
218
219 // add the mix/max scale denoms if we got any from the callers
220 const QVariantMap oldProps = context.extraProperties();
221 QVariantMap props = oldProps;
222 QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
223 context.setExtraProperties( props );
224 mSymbol->toSld( doc, ruleElem, context );
225 context.setExtraProperties( oldProps );
226 if ( !QgsSymbolLayerUtils::hasSldSymbolizer( ruleElem ) )
227 {
228 // symbol could not be converted to SLD, or is an "empty" symbol. In this case we do not generate a rule, as
229 // SLD spec requires a Symbolizer element to be present
230 return false;
231 }
232
233 element.appendChild( ruleElem );
234 return true;
235}
236
238
240 : QgsFeatureRenderer( u"categorizedSymbol"_s )
241 , mAttrName( attrName )
242{
243 //important - we need a deep copy of the categories list, not a shared copy. This is required because
244 //QgsRendererCategory::symbol() is marked const, and so retrieving the symbol via this method does not
245 //trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
246 for ( const QgsRendererCategory &cat : categories )
247 {
248 if ( !cat.symbol() )
249 {
250 QgsDebugError( u"invalid symbol in a category! ignoring..."_s );
251 }
252 mCategories << cat;
253 }
254}
255
257{
259 QgsCategoryList::const_iterator catIt = mCategories.constBegin();
260 for ( ; catIt != mCategories.constEnd(); ++catIt )
261 {
262 if ( QgsSymbol *catSymbol = catIt->symbol() )
263 {
264 if ( catSymbol->flags().testFlag( Qgis::SymbolFlag::AffectsLabeling ) )
266 }
267 }
268
269 return res;
270}
271
273
275{
276 mSymbolHash.clear();
277
278 for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
279 {
280 const QVariant val = cat.value();
281 if ( val.userType() == QMetaType::Type::QVariantList )
282 {
283 const QVariantList list = val.toList();
284 for ( const QVariant &v : list )
285 {
286 mSymbolHash.insert( v.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
287 }
288 }
289 else
290 {
291 mSymbolHash.insert( val.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
292 }
293 }
294}
295
300
302{
303 bool found = false;
304 return symbolForValue( value, found );
305}
306
307QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value, bool &foundMatchingSymbol ) const
308{
309 foundMatchingSymbol = false;
310
311 // TODO: special case for int, double
312 QHash<QString, QgsSymbol *>::const_iterator it = mSymbolHash.constFind( QgsVariantUtils::isNull( value ) ? QString() : value.toString() );
313 if ( it == mSymbolHash.constEnd() )
314 {
315 if ( mSymbolHash.isEmpty() )
316 {
317 QgsDebugError( u"there are no hashed symbols!!!"_s );
318 }
319 else
320 {
321 QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
322 }
323 return nullptr;
324 }
325
326 foundMatchingSymbol = true;
327
328 return *it;
329}
330
332{
333 return originalSymbolForFeature( feature, context );
334}
335
336QVariant QgsCategorizedSymbolRenderer::valueForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
337{
338 QgsAttributes attrs = feature.attributes();
339 QVariant value;
340 if ( mAttrNum == -1 )
341 {
342 Q_ASSERT( mExpression );
343
344 value = mExpression->evaluate( &context.expressionContext() );
345 }
346 else
347 {
348 value = attrs.value( mAttrNum );
349 }
350
351 return value;
352}
353
355{
356 QVariant value = valueForFeature( feature, context );
357
358 bool foundCategory = false;
359 // find the right symbol for the category
360 QgsSymbol *symbol = symbolForValue( value, foundCategory );
361
362 if ( !foundCategory )
363 {
364 // if no symbol found, use default symbol
365 return symbolForValue( QVariant( "" ), foundCategory );
366 }
367
368 return symbol;
369}
370
371
373{
374 for ( int i = 0; i < mCategories.count(); i++ )
375 {
376 if ( mCategories[i].value() == val )
377 return i;
378 }
379 return -1;
380}
381
383{
384 int idx = -1;
385 for ( int i = 0; i < mCategories.count(); i++ )
386 {
387 if ( mCategories[i].label() == val )
388 {
389 if ( idx != -1 )
390 return -1;
391 else
392 idx = i;
393 }
394 }
395 return idx;
396}
397
398bool QgsCategorizedSymbolRenderer::updateCategoryValue( int catIndex, const QVariant &value )
399{
400 if ( catIndex < 0 || catIndex >= mCategories.size() )
401 return false;
402 mCategories[catIndex].setValue( value );
403 return true;
404}
405
407{
408 if ( catIndex < 0 || catIndex >= mCategories.size() )
409 return false;
410 mCategories[catIndex].setSymbol( symbol );
411 return true;
412}
413
414bool QgsCategorizedSymbolRenderer::updateCategoryLabel( int catIndex, const QString &label )
415{
416 if ( catIndex < 0 || catIndex >= mCategories.size() )
417 return false;
418 mCategories[catIndex].setLabel( label );
419 return true;
420}
421
423{
424 if ( catIndex < 0 || catIndex >= mCategories.size() )
425 return false;
426 mCategories[catIndex].setRenderState( render );
427 return true;
428}
429
431{
432 if ( !cat.symbol() )
433 {
434 QgsDebugError( u"invalid symbol in a category! ignoring..."_s );
435 return;
436 }
437
438 mCategories.append( cat );
439}
440
442{
443 if ( catIndex < 0 || catIndex >= mCategories.size() )
444 return false;
445
446 mCategories.removeAt( catIndex );
447 return true;
448}
449
454
456{
457 if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
458 mCategories.move( from, to );
459}
460
462{
463 return qgsVariantLessThan( c1.value(), c2.value() );
464}
466{
467 return qgsVariantGreaterThan( c1.value(), c2.value() );
468}
469
471{
472 if ( order == Qt::AscendingOrder )
473 {
474 std::sort( mCategories.begin(), mCategories.end(), valueLessThan );
475 }
476 else
477 {
478 std::sort( mCategories.begin(), mCategories.end(), valueGreaterThan );
479 }
480}
481
483{
484 return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
485}
486
488{
489 return QString::localeAwareCompare( c1.label(), c2.label() ) > 0;
490}
491
493{
494 if ( order == Qt::AscendingOrder )
495 {
496 std::sort( mCategories.begin(), mCategories.end(), labelLessThan );
497 }
498 else
499 {
500 std::sort( mCategories.begin(), mCategories.end(), labelGreaterThan );
501 }
502}
503
505{
506 QgsFeatureRenderer::startRender( context, fields );
507
508 mCounting = context.rendererScale() == 0.0;
509
510 // make sure that the hash table is up to date
511 rebuildHash();
512
513 // find out classification attribute index from name
514 mAttrNum = fields.lookupField( mAttrName );
515 if ( mAttrNum == -1 )
516 {
517 mExpression = std::make_unique<QgsExpression>( mAttrName );
518 mExpression->prepare( &context.expressionContext() );
519 }
520
521 for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
522 {
523 cat.symbol()->startRender( context, fields );
524 }
525}
526
528{
530
531 for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
532 {
533 cat.symbol()->stopRender( context );
534 }
535 mExpression.reset();
536}
537
539{
540 QSet<QString> attributes;
541
542 // mAttrName can contain either attribute name or an expression.
543 // Sometimes it is not possible to distinguish between those two,
544 // e.g. "a - b" can be both a valid attribute name or expression.
545 // Since we do not have access to fields here, try both options.
546 attributes << mAttrName;
547
548 QgsExpression testExpr( mAttrName );
549 if ( !testExpr.hasParserError() )
550 attributes.unite( testExpr.referencedColumns() );
551
552 QgsCategoryList::const_iterator catIt = mCategories.constBegin();
553 for ( ; catIt != mCategories.constEnd(); ++catIt )
554 {
555 QgsSymbol *catSymbol = catIt->symbol();
556 if ( catSymbol )
557 {
558 attributes.unite( catSymbol->usedAttributes( context ) );
559 }
560 }
561 return attributes;
562}
563
565{
566 QgsExpression testExpr( mAttrName );
567 if ( !testExpr.hasParserError() )
568 {
569 QgsExpressionContext context;
570 context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) ); // unfortunately no layer access available!
571 testExpr.prepare( &context );
572 return testExpr.needsGeometry();
573 }
574 return false;
575}
576
578{
579 QString s = u"CATEGORIZED: idx %1\n"_s.arg( mAttrName );
580 for ( int i = 0; i < mCategories.count(); i++ )
581 s += mCategories[i].dump();
582 return s;
583}
584
599
600void QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
601{
602 QgsSldExportContext context;
603 context.setExtraProperties( props );
604 toSld( doc, element, context );
605}
606
607bool QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
608{
609 const QVariantMap oldProps = context.extraProperties();
610 QVariantMap newProps = oldProps;
611 newProps[ u"attribute"_s] = mAttrName;
612 context.setExtraProperties( newProps );
613
614 // create a Rule for each range
615 bool result = true;
616 for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); ++it )
617 {
618 if ( !it->toSld( doc, element, mAttrName, context ) )
619 result = false;
620 }
621 context.setExtraProperties( oldProps );
622 return result;
623}
624
626{
627 int attrNum = fields.lookupField( mAttrName );
628 bool isExpression = ( attrNum == -1 );
629
630 bool hasDefault = false;
631 bool defaultActive = false;
632 bool allActive = true;
633 bool noneActive = true;
634
635 //we need to build lists of both inactive and active values, as either list may be required
636 //depending on whether the default category is active or not
637 QString activeValues;
638 QString inactiveValues;
639
640 for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
641 {
642 if ( cat.value() == "" || QgsVariantUtils::isNull( cat.value() ) )
643 {
644 hasDefault = true;
645 defaultActive = cat.renderState();
646 }
647
648 noneActive = noneActive && !cat.renderState();
649 allActive = allActive && cat.renderState();
650
651 const bool isList = cat.value().userType() == QMetaType::Type::QVariantList;
652 QString value = QgsExpression::quotedValue( cat.value(), static_cast<QMetaType::Type>( cat.value().userType() ) );
653
654 if ( !cat.renderState() )
655 {
656 if ( value != "" )
657 {
658 if ( isList )
659 {
660 const QVariantList list = cat.value().toList();
661 for ( const QVariant &v : list )
662 {
663 if ( !inactiveValues.isEmpty() )
664 inactiveValues.append( ',' );
665
666 inactiveValues.append( QgsExpression::quotedValue( v, isExpression ? static_cast<QMetaType::Type>( v.userType() ) : fields.at( attrNum ).type() ) );
667 }
668 }
669 else
670 {
671 if ( !inactiveValues.isEmpty() )
672 inactiveValues.append( ',' );
673
674 inactiveValues.append( value );
675 }
676 }
677 }
678 else
679 {
680 if ( value != "" )
681 {
682 if ( isList )
683 {
684 const QVariantList list = cat.value().toList();
685 for ( const QVariant &v : list )
686 {
687 if ( !activeValues.isEmpty() )
688 activeValues.append( ',' );
689
690 activeValues.append( QgsExpression::quotedValue( v, isExpression ? static_cast<QMetaType::Type>( v.userType() ) : fields.at( attrNum ).type() ) );
691 }
692 }
693 else
694 {
695 if ( !activeValues.isEmpty() )
696 activeValues.append( ',' );
697
698 activeValues.append( value );
699 }
700 }
701 }
702 }
703
704 QString attr = isExpression ? mAttrName : u"\"%1\""_s.arg( mAttrName );
705
706 if ( allActive && hasDefault )
707 {
708 return QString();
709 }
710 else if ( noneActive )
711 {
712 return u"FALSE"_s;
713 }
714 else if ( defaultActive )
715 {
716 return u"(%1) NOT IN (%2) OR (%1) IS NULL"_s.arg( attr, inactiveValues );
717 }
718 else
719 {
720 return u"(%1) IN (%2)"_s.arg( attr, activeValues );
721 }
722}
723
725{
726 Q_UNUSED( context )
727 QgsSymbolList lst;
728 lst.reserve( mCategories.count() );
729 for ( const QgsRendererCategory &cat : mCategories )
730 {
731 lst.append( cat.symbol() );
732 }
733 return lst;
734}
735
737{
738 for ( const QgsRendererCategory &cat : mCategories )
739 {
740 QgsStyleSymbolEntity entity( cat.symbol() );
741 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, cat.value().toString(), cat.label() ) ) )
742 return false;
743 }
744
745 if ( mSourceColorRamp )
746 {
748 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
749 return false;
750 }
751
752 return true;
753}
754
756{
757 QDomElement symbolsElem = element.firstChildElement( u"symbols"_s );
758 if ( symbolsElem.isNull() )
759 return nullptr;
760
761 QDomElement catsElem = element.firstChildElement( u"categories"_s );
762 if ( catsElem.isNull() )
763 return nullptr;
764
765 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
766 QgsCategoryList cats;
767
768 // Value from string (long, ulong, double and string)
769 const auto valueFromString = []( const QString & value, const QString & valueType ) -> QVariant
770 {
771 if ( valueType == "double"_L1 )
772 {
773 bool ok;
774 const auto val { value.toDouble( &ok ) };
775 if ( ok )
776 {
777 return val;
778 }
779 }
780 else if ( valueType == "ulong"_L1 )
781 {
782 bool ok;
783 const auto val { value.toULongLong( &ok ) };
784 if ( ok )
785 {
786 return val;
787 }
788 }
789 else if ( valueType == "long"_L1 )
790 {
791 bool ok;
792 const auto val { value.toLongLong( &ok ) };
793 if ( ok )
794 {
795 return val;
796 }
797 }
798 else if ( valueType == "bool"_L1 )
799 {
800 if ( value.toLower() == "false"_L1 )
801 return false;
802 if ( value.toLower() == "true"_L1 )
803 return true;
804 }
805 else if ( valueType == "NULL"_L1 )
806 {
807 // This is the default ("fallback") category
808 return QVariant();
809 }
810 return value;
811 };
812
813 QDomElement catElem = catsElem.firstChildElement();
814 int i = 0;
815 QSet<QString> usedUuids;
816 while ( !catElem.isNull() )
817 {
818 if ( catElem.tagName() == "category"_L1 )
819 {
820 QVariant value;
821 if ( catElem.hasAttribute( u"value"_s ) )
822 {
823 value = valueFromString( catElem.attribute( u"value"_s ), catElem.attribute( u"type"_s, QString() ) ) ;
824 }
825 else
826 {
827 QVariantList values;
828 QDomElement valElem = catElem.firstChildElement();
829 while ( !valElem.isNull() )
830 {
831 if ( valElem.tagName() == "val"_L1 )
832 {
833 values << valueFromString( valElem.attribute( u"value"_s ), valElem.attribute( u"type"_s, QString() ) );
834 }
835 valElem = valElem.nextSiblingElement();
836 }
837 if ( !values.isEmpty() )
838 value = values;
839 }
840
841 QString symbolName = catElem.attribute( u"symbol"_s );
842 QString label = context.projectTranslator()->translate( u"project:layers:%1:legendsymbollabels"_s.arg( context.currentLayerId() ), catElem.attribute( u"label"_s ) );
843 QgsDebugMsgLevel( "context" + u"project:layers:%1:legendsymbollabels"_s.arg( context.currentLayerId() ) + " source " + catElem.attribute( u"label"_s ), 3 );
844
845 bool render = catElem.attribute( u"render"_s ) != "false"_L1;
846 QString uuid = catElem.attribute( u"uuid"_s, QString::number( i++ ) );
847
848 while ( usedUuids.contains( uuid ) )
849 {
850 uuid = QUuid::createUuid().toString();
851 }
852 if ( symbolMap.contains( symbolName ) )
853 {
854 QgsSymbol *symbol = symbolMap.take( symbolName );
855 cats.append( QgsRendererCategory( value, symbol, label, render, uuid ) );
856 usedUuids << uuid;
857 }
858 }
859 catElem = catElem.nextSiblingElement();
860 }
861
862 QString attrName = element.attribute( u"attr"_s );
863
865
866 // delete symbols if there are any more
868
869 // try to load source symbol (optional)
870 QDomElement sourceSymbolElem = element.firstChildElement( u"source-symbol"_s );
871 if ( !sourceSymbolElem.isNull() )
872 {
873 QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
874 if ( sourceSymbolMap.contains( u"0"_s ) )
875 {
876 r->setSourceSymbol( sourceSymbolMap.take( u"0"_s ) );
877 }
878 QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
879 }
880
881 // try to load color ramp (optional)
882 QDomElement sourceColorRampElem = element.firstChildElement( u"colorramp"_s );
883 if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( u"name"_s ) == "[source]"_L1 )
884 {
885 r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ).release() );
886 }
887
888 QDomElement rotationElem = element.firstChildElement( u"rotation"_s );
889 if ( !rotationElem.isNull() && !rotationElem.attribute( u"field"_s ).isEmpty() )
890 {
891 for ( const QgsRendererCategory &cat : r->mCategories )
892 {
893 convertSymbolRotation( cat.symbol(), rotationElem.attribute( u"field"_s ) );
894 }
895 if ( r->mSourceSymbol )
896 {
897 convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( u"field"_s ) );
898 }
899 }
900
901 QDomElement sizeScaleElem = element.firstChildElement( u"sizescale"_s );
902 if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( u"field"_s ).isEmpty() )
903 {
904 for ( const QgsRendererCategory &cat : r->mCategories )
905 {
907 QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( u"scalemethod"_s ) ),
908 sizeScaleElem.attribute( u"field"_s ) );
909 }
910 if ( r->mSourceSymbol && r->mSourceSymbol->type() == Qgis::SymbolType::Marker )
911 {
913 QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( u"scalemethod"_s ) ),
914 sizeScaleElem.attribute( u"field"_s ) );
915 }
916 }
917
918 QDomElement ddsLegendSizeElem = element.firstChildElement( u"data-defined-size-legend"_s );
919 if ( !ddsLegendSizeElem.isNull() )
920 {
921 r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
922 }
923
924 // TODO: symbol levels
925 return r;
926}
927
928QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
929{
930 // clazy:skip
931 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
932 rendererElem.setAttribute( u"type"_s, u"categorizedSymbol"_s );
933 rendererElem.setAttribute( u"attr"_s, mAttrName );
934
935 // String for type
936 // We just need string, bool, and three numeric types: double, ulong and long for unsigned, signed and float/double
937 const auto stringForType = []( const QMetaType::Type type ) -> QString
938 {
939 if ( type == QMetaType::Type::QChar || type == QMetaType::Type::Int || type == QMetaType::Type::LongLong )
940 {
941 return u"long"_s;
942 }
943 else if ( type == QMetaType::Type::UInt || type == QMetaType::Type::ULongLong )
944 {
945 return u"ulong"_s;
946 }
947 else if ( type == QMetaType::Type::Double )
948 {
949 return u"double"_s ;
950 }
951 else if ( type == QMetaType::Type::Bool )
952 {
953 return u"bool"_s;
954 }
955 else // Default: string
956 {
957 return u"string"_s;
958 }
959 };
960
961 // categories
962 if ( !mCategories.isEmpty() )
963 {
964 int i = 0;
966 QDomElement catsElem = doc.createElement( u"categories"_s );
967 QgsCategoryList::const_iterator it = mCategories.constBegin();
968 for ( ; it != mCategories.constEnd(); ++it )
969 {
970 const QgsRendererCategory &cat = *it;
971 QString symbolName = QString::number( i );
972 symbols.insert( symbolName, cat.symbol() );
973
974 QDomElement catElem = doc.createElement( u"category"_s );
975 if ( cat.value().userType() == QMetaType::Type::QVariantList )
976 {
977 const QVariantList list = cat.value().toList();
978 for ( const QVariant &v : list )
979 {
980 QDomElement valueElem = doc.createElement( u"val"_s );
981 valueElem.setAttribute( u"value"_s, v.toString() );
982 valueElem.setAttribute( u"type"_s, stringForType( static_cast<QMetaType::Type>( v.userType() ) ) );
983 catElem.appendChild( valueElem );
984 }
985 }
986 else
987 {
988 if ( QgsVariantUtils::isNull( cat.value() ) )
989 {
990 // We need to save NULL value as specific kind, it is the default ("fallback") category
991 catElem.setAttribute( u"value"_s, "NULL" );
992 catElem.setAttribute( u"type"_s, "NULL" );
993 }
994 else
995 {
996 catElem.setAttribute( u"value"_s, cat.value().toString() );
997 catElem.setAttribute( u"type"_s, stringForType( static_cast<QMetaType::Type>( cat.value().userType() ) ) );
998 }
999 }
1000 catElem.setAttribute( u"symbol"_s, symbolName );
1001 catElem.setAttribute( u"label"_s, cat.label() );
1002 catElem.setAttribute( u"render"_s, cat.renderState() ? "true" : "false" );
1003 catElem.setAttribute( u"uuid"_s, cat.uuid() );
1004 catsElem.appendChild( catElem );
1005 i++;
1006 }
1007 rendererElem.appendChild( catsElem );
1008
1009 // save symbols
1010 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, u"symbols"_s, doc, context );
1011 rendererElem.appendChild( symbolsElem );
1012 }
1013
1014 // save source symbol
1015 if ( mSourceSymbol )
1016 {
1017 QgsSymbolMap sourceSymbols;
1018 sourceSymbols.insert( u"0"_s, mSourceSymbol.get() );
1019 QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, u"source-symbol"_s, doc, context );
1020 rendererElem.appendChild( sourceSymbolElem );
1021 }
1022
1023 // save source color ramp
1024 if ( mSourceColorRamp )
1025 {
1026 QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( u"[source]"_s, mSourceColorRamp.get(), doc );
1027 rendererElem.appendChild( colorRampElem );
1028 }
1029
1030 QDomElement rotationElem = doc.createElement( u"rotation"_s );
1031 rendererElem.appendChild( rotationElem );
1032
1033 QDomElement sizeScaleElem = doc.createElement( u"sizescale"_s );
1034 rendererElem.appendChild( sizeScaleElem );
1035
1037 {
1038 QDomElement ddsLegendElem = doc.createElement( u"data-defined-size-legend"_s );
1039 mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
1040 rendererElem.appendChild( ddsLegendElem );
1041 }
1042
1043 saveRendererData( doc, rendererElem, context );
1044
1045 return rendererElem;
1046}
1047
1048
1049QgsLegendSymbolList QgsCategorizedSymbolRenderer::baseLegendSymbolItems() const
1050{
1052 for ( const QgsRendererCategory &cat : mCategories )
1053 {
1054 lst << QgsLegendSymbolItem( cat.symbol(), cat.label(), cat.uuid(), true );
1055 }
1056 return lst;
1057}
1058
1059QString QgsCategorizedSymbolRenderer::displayString( const QVariant &v, int precision )
1060{
1061 return QgsVariantUtils::displayString( v, precision );
1062}
1063
1065{
1067 {
1068 // check that all symbols that have the same size expression
1069 QgsProperty ddSize;
1070 for ( const QgsRendererCategory &category : mCategories )
1071 {
1072 const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( category.symbol() );
1073 if ( ddSize )
1074 {
1075 QgsProperty sSize( symbol->dataDefinedSize() );
1076 if ( sSize != ddSize )
1077 {
1078 // no common size expression
1079 return baseLegendSymbolItems();
1080 }
1081 }
1082 else
1083 {
1084 ddSize = symbol->dataDefinedSize();
1085 }
1086 }
1087
1088 if ( ddSize && ddSize.isActive() )
1089 {
1091
1093 ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
1094 lst += ddSizeLegend.legendSymbolList();
1095
1096 lst += baseLegendSymbolItems();
1097 return lst;
1098 }
1099 }
1100
1101 return baseLegendSymbolItems();
1102}
1103
1105{
1106 const QVariant value = valueForFeature( feature, context );
1107
1108 for ( const QgsRendererCategory &cat : mCategories )
1109 {
1110 bool match = false;
1111 if ( cat.value().userType() == QMetaType::Type::QVariantList )
1112 {
1113 const QVariantList list = cat.value().toList();
1114 for ( const QVariant &v : list )
1115 {
1116 if ( value == v )
1117 {
1118 match = true;
1119 break;
1120 }
1121 }
1122 }
1123 else
1124 {
1125 // NULL cat value may be stored as an empty string or an invalid variant, depending on how
1126 // the renderer was constructed and which QGIS version was used
1127 if ( QgsVariantUtils::isNull( value ) )
1128 {
1129 match = cat.value().toString().isEmpty() || QgsVariantUtils::isNull( cat.value() );
1130 }
1131 else
1132 {
1133 match = value == cat.value();
1134 }
1135 }
1136
1137 if ( match )
1138 {
1139 if ( cat.renderState() || mCounting )
1140 return QSet< QString >() << cat.uuid();
1141 else
1142 return QSet< QString >();
1143 }
1144 }
1145
1146 return QSet< QString >();
1147}
1148
1149QString QgsCategorizedSymbolRenderer::legendKeyToExpression( const QString &key, QgsVectorLayer *layer, bool &ok ) const
1150{
1151 ok = false;
1152 int i = 0;
1153 for ( i = 0; i < mCategories.size(); i++ )
1154 {
1155 if ( mCategories[i].uuid() == key )
1156 {
1157 ok = true;
1158 break;
1159 }
1160 }
1161
1162 if ( !ok )
1163 {
1164 ok = false;
1165 return QString();
1166 }
1167
1168 const int fieldIndex = layer ? layer->fields().lookupField( mAttrName ) : -1;
1169 const bool isNumeric = layer && fieldIndex >= 0 ? layer->fields().at( fieldIndex ).isNumeric() : false;
1170 const QMetaType::Type fieldType = layer && fieldIndex >= 0 ? layer->fields().at( fieldIndex ).type() : QMetaType::Type::UnknownType;
1171 const QString attributeComponent = QgsExpression::quoteFieldExpression( mAttrName, layer );
1172
1173 ok = true;
1174 const QgsRendererCategory &cat = mCategories[i];
1175 if ( cat.value().userType() == QMetaType::Type::QVariantList )
1176 {
1177 const QVariantList list = cat.value().toList();
1178 QStringList parts;
1179 parts.reserve( list.size() );
1180 for ( const QVariant &v : list )
1181 {
1182 parts.append( QgsExpression::quotedValue( v ) );
1183 }
1184
1185 return u"%1 IN (%2)"_s.arg( attributeComponent, parts.join( ", "_L1 ) );
1186 }
1187 else
1188 {
1189 // Numeric NULL cat value is stored as an empty string
1190 QVariant value = cat.value();
1191 if ( isNumeric && value.toString().isEmpty() )
1192 {
1193 value = QVariant();
1194 }
1195
1196 if ( QgsVariantUtils::isNull( value ) )
1197 return u"%1 IS NULL"_s.arg( attributeComponent );
1198 else if ( fieldType == QMetaType::Type::UnknownType )
1199 return u"%1 = %2"_s.arg( attributeComponent, QgsExpression::quotedValue( value ) );
1200 else
1201 return u"%1 = %2"_s.arg( attributeComponent, QgsExpression::quotedValue( value, fieldType ) );
1202 }
1203}
1204
1209
1211{
1212 return mSourceSymbol.get();
1213}
1214
1219
1224
1229
1234
1236{
1237 setSourceColorRamp( ramp );
1238 double num = mCategories.count() - 1;
1239 double count = 0;
1240
1241 QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp );
1242 if ( randomRamp )
1243 {
1244 //ramp is a random colors ramp, so inform it of the total number of required colors
1245 //this allows the ramp to pregenerate a set of visually distinctive colors
1246 randomRamp->setTotalColorCount( mCategories.count() );
1247 }
1248
1249 for ( const QgsRendererCategory &cat : mCategories )
1250 {
1251 double value = count / num;
1252 cat.symbol()->setColor( mSourceColorRamp->color( value ) );
1253 count += 1;
1254 }
1255}
1256
1258{
1259 int i = 0;
1260 for ( const QgsRendererCategory &cat : mCategories )
1261 {
1262 QgsSymbol *symbol = sym->clone();
1263 symbol->setColor( cat.symbol()->color() );
1264 updateCategorySymbol( i, symbol );
1265 ++i;
1266 }
1267 setSourceSymbol( sym->clone() );
1268}
1269
1271{
1272 return true;
1273}
1274
1276{
1277 for ( const QgsRendererCategory &category : std::as_const( mCategories ) )
1278 {
1279 if ( category.uuid() == key )
1280 {
1281 return category.renderState();
1282 }
1283 }
1284
1285 return true;
1286}
1287
1289{
1290 bool ok = false;
1291 int i = 0;
1292 for ( i = 0; i < mCategories.size(); i++ )
1293 {
1294 if ( mCategories[i].uuid() == key )
1295 {
1296 ok = true;
1297 break;
1298 }
1299 }
1300
1301 if ( ok )
1302 updateCategorySymbol( i, symbol );
1303 else
1304 delete symbol;
1305}
1306
1307void QgsCategorizedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
1308{
1309 for ( int i = 0; i < mCategories.size(); i++ )
1310 {
1311 if ( mCategories[i].uuid() == key )
1312 {
1313 updateCategoryRenderState( i, state );
1314 break;
1315 }
1316 }
1317}
1318
1320{
1321 std::unique_ptr< QgsCategorizedSymbolRenderer > r;
1322 if ( renderer->type() == "categorizedSymbol"_L1 )
1323 {
1324 r.reset( static_cast<QgsCategorizedSymbolRenderer *>( renderer->clone() ) );
1325 }
1326 else if ( renderer->type() == "graduatedSymbol"_L1 )
1327 {
1328 const QgsGraduatedSymbolRenderer *graduatedSymbolRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1329 if ( graduatedSymbolRenderer )
1330 {
1331 r = std::make_unique<QgsCategorizedSymbolRenderer>( QString(), QgsCategoryList() );
1332 if ( graduatedSymbolRenderer->sourceSymbol() )
1333 r->setSourceSymbol( graduatedSymbolRenderer->sourceSymbol()->clone() );
1334 if ( graduatedSymbolRenderer->sourceColorRamp() )
1335 {
1336 r->setSourceColorRamp( graduatedSymbolRenderer->sourceColorRamp()->clone() );
1337 }
1338 r->setClassAttribute( graduatedSymbolRenderer->classAttribute() );
1339 }
1340 }
1341 else if ( renderer->type() == "RuleRenderer"_L1 )
1342 {
1343 const QgsRuleBasedRenderer *ruleBasedSymbolRenderer = dynamic_cast<const QgsRuleBasedRenderer *>( renderer );
1344 if ( ruleBasedSymbolRenderer )
1345 {
1346 r = std::make_unique<QgsCategorizedSymbolRenderer>( QString(), QgsCategoryList() );
1347
1348 const QList< QgsRuleBasedRenderer::Rule * > rules = const_cast< QgsRuleBasedRenderer * >( ruleBasedSymbolRenderer )->rootRule()->children();
1349 bool canConvert = true;
1350
1351 bool isFirst = true;
1352 QString attribute;
1353 QVariant value;
1355
1356 for ( QgsRuleBasedRenderer::Rule *rule : rules )
1357 {
1358 if ( rule->isElse() || rule->minimumScale() != 0 || rule->maximumScale() != 0 || !rule->symbol() || !rule->children().isEmpty() )
1359 {
1360 canConvert = false;
1361 break;
1362 }
1363
1364 QgsExpression e( rule->filterExpression() );
1365
1366 if ( !e.rootNode() )
1367 {
1368 canConvert = false;
1369 break;
1370 }
1371
1372 if ( const QgsExpressionNodeBinaryOperator *binOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( e.rootNode() ) )
1373 {
1374 if ( binOp->op() == QgsExpressionNodeBinaryOperator::boEQ )
1375 {
1376 const QString left = binOp->opLeft()->dump();
1377 if ( !isFirst && left != attribute )
1378 {
1379 canConvert = false;
1380 break;
1381 }
1382 else if ( isFirst )
1383 {
1384 attribute = left;
1385 }
1386
1387 const QgsExpressionNodeLiteral *literal = dynamic_cast<const QgsExpressionNodeLiteral *>( binOp->opRight() );
1388 if ( literal )
1389 {
1391 cat.setValue( literal->value() );
1392 cat.setSymbol( rule->symbol()->clone() );
1393 cat.setLabel( rule->label().isEmpty() ? literal->value().toString() : rule->label() );
1394 cat.setRenderState( rule->active() );
1395 categories.append( cat );
1396 }
1397 else
1398 {
1399 canConvert = false;
1400 break;
1401 }
1402 }
1403 else
1404 {
1405 canConvert = false;
1406 }
1407 }
1408 else
1409 {
1410 canConvert = false;
1411 break;
1412 }
1413
1414 isFirst = false;
1415 }
1416
1417 if ( canConvert )
1418 {
1419 r = std::make_unique< QgsCategorizedSymbolRenderer >( attribute, categories );
1420 }
1421 else
1422 {
1423 r.reset();
1424 }
1425 }
1426 }
1427 else if ( renderer->type() == "pointDisplacement"_L1 || renderer->type() == "pointCluster"_L1 )
1428 {
1429 const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
1430 if ( pointDistanceRenderer )
1431 r.reset( convertFromRenderer( pointDistanceRenderer->embeddedRenderer() ) );
1432 }
1433 else if ( renderer->type() == "invertedPolygonRenderer"_L1 )
1434 {
1435 const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
1436 if ( invertedPolygonRenderer )
1437 r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1438 }
1439 else if ( renderer->type() == "embeddedSymbol"_L1 && layer )
1440 {
1441 const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
1445 req.setNoAttributes();
1446 QgsFeatureIterator it = layer->getFeatures( req );
1447 QgsFeature feature;
1448 while ( it.nextFeature( feature ) && categories.size() < 2000 )
1449 {
1450 if ( feature.embeddedSymbol() )
1451 categories.append( QgsRendererCategory( feature.id(), feature.embeddedSymbol()->clone(), QString::number( feature.id() ) ) );
1452 }
1453 categories.append( QgsRendererCategory( QVariant(), embeddedRenderer->defaultSymbol()->clone(), QString() ) );
1454 r = std::make_unique<QgsCategorizedSymbolRenderer>( u"$id"_s, categories );
1455 }
1456
1457 // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1458 // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
1459
1460 if ( !r )
1461 {
1462 r = std::make_unique< QgsCategorizedSymbolRenderer >( QString(), QgsCategoryList() );
1463 QgsRenderContext context;
1464 QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
1465 if ( !symbols.isEmpty() )
1466 {
1467 QgsSymbol *newSymbol = symbols.at( 0 )->clone();
1470 r->setSourceSymbol( newSymbol );
1471 }
1472 }
1473
1474 renderer->copyRendererData( r.get() );
1475
1476 return r.release();
1477}
1478
1483
1488
1489int QgsCategorizedSymbolRenderer::matchToSymbols( QgsStyle *style, Qgis::SymbolType type, QVariantList &unmatchedCategories, QStringList &unmatchedSymbols, const bool caseSensitive, const bool useTolerantMatch )
1490{
1491 if ( !style )
1492 return 0;
1493
1494 int matched = 0;
1495 unmatchedSymbols = style->symbolNames();
1496 const QSet< QString > allSymbolNames( unmatchedSymbols.begin(), unmatchedSymbols.end() );
1497
1498 const thread_local QRegularExpression tolerantMatchRe( u"[^\\w\\d ]"_s, QRegularExpression::UseUnicodePropertiesOption );
1499
1500 for ( int catIdx = 0; catIdx < mCategories.count(); ++catIdx )
1501 {
1502 const QVariant value = mCategories.at( catIdx ).value();
1503 const QString val = value.toString().trimmed();
1504 std::unique_ptr< QgsSymbol > symbol( style->symbol( val ) );
1505 // case-sensitive match
1506 if ( symbol && symbol->type() == type )
1507 {
1508 matched++;
1509 unmatchedSymbols.removeAll( val );
1510 updateCategorySymbol( catIdx, symbol.release() );
1511 continue;
1512 }
1513
1514 if ( !caseSensitive || useTolerantMatch )
1515 {
1516 QString testVal = val;
1517 if ( useTolerantMatch )
1518 testVal.replace( tolerantMatchRe, QString() );
1519
1520 bool foundMatch = false;
1521 for ( const QString &name : allSymbolNames )
1522 {
1523 QString testName = name.trimmed();
1524 if ( useTolerantMatch )
1525 testName.replace( tolerantMatchRe, QString() );
1526
1527 if ( testName == testVal || ( !caseSensitive && testName.trimmed().compare( testVal, Qt::CaseInsensitive ) == 0 ) )
1528 {
1529 // found a case-insensitive match
1530 std::unique_ptr< QgsSymbol > symbol( style->symbol( name ) );
1531 if ( symbol && symbol->type() == type )
1532 {
1533 matched++;
1534 unmatchedSymbols.removeAll( name );
1535 updateCategorySymbol( catIdx, symbol.release() );
1536 foundMatch = true;
1537 break;
1538 }
1539 }
1540 }
1541 if ( foundMatch )
1542 continue;
1543 }
1544
1545 unmatchedCategories << value;
1546 }
1547
1548 return matched;
1549}
1550
1551QgsCategoryList QgsCategorizedSymbolRenderer::createCategories( const QList<QVariant> &values, const QgsSymbol *symbol, QgsVectorLayer *layer, const QString &attributeName )
1552{
1553 QgsCategoryList cats;
1554 QVariantList vals = values;
1555 // sort the categories first
1556 QgsSymbolLayerUtils::sortVariantList( vals, Qt::AscendingOrder );
1557
1558 if ( layer && !attributeName.isNull() )
1559 {
1560 const QgsFields fields = layer->fields();
1561 for ( const QVariant &value : vals )
1562 {
1563 QgsSymbol *newSymbol = symbol->clone();
1565 if ( !QgsVariantUtils::isNull( value ) )
1566 {
1567 const int fieldIdx = fields.lookupField( attributeName );
1568 QString categoryName = QgsVariantUtils::displayString( value );
1569 if ( fieldIdx != -1 )
1570 {
1571 const QgsField field = fields.at( fieldIdx );
1572 const QgsEditorWidgetSetup setup = field.editorWidgetSetup();
1574 categoryName = formatter->representValue( layer, fieldIdx, setup.config(), QVariant(), value );
1575 }
1576 cats.append( QgsRendererCategory( value, newSymbol, categoryName, true ) );
1577 }
1578 }
1579 }
1580
1581 // add null (default) value
1582 QgsSymbol *newSymbol = symbol->clone();
1584 cats.append( QgsRendererCategory( QVariant(), newSymbol, QString(), true ) );
1585
1586 return cats;
1587}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2254
@ EmbeddedSymbols
Retrieve any embedded feature symbology.
Definition qgis.h:2258
QFlags< FeatureRendererFlag > FeatureRendererFlags
Flags controlling behavior of vector feature renderers.
Definition qgis.h:857
@ AffectsLabeling
If present, indicates that the renderer will participate in the map labeling problem.
Definition qgis.h:848
SymbolType
Symbol types.
Definition qgis.h:629
@ Marker
Marker symbol.
Definition qgis.h:630
@ AffectsLabeling
If present, indicates that the symbol will participate in the map labeling problem.
Definition qgis.h:868
static QgsFieldFormatterRegistry * fieldFormatterRegistry()
Gets the registry of available field formatters.
A vector of attributes.
void sortByValue(Qt::SortOrder order=Qt::AscendingOrder)
Sorts the existing categories by their value.
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...
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
QString legendKeyToExpression(const QString &key, QgsVectorLayer *layer, bool &ok) const override
Attempts to convert the specified legend rule key to a QGIS expression matching the features displaye...
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
Returns true if the legend symbology item with the specified key is checked.
bool legendSymbolItemsCheckable() const override
Returns true if symbology items in legend are 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.
Qgis::FeatureRendererFlags flags() const override
Returns flags associated with the renderer.
Q_DECL_DEPRECATED QgsSymbol * skipRender()
void checkLegendSymbolItem(const QString &key, bool state=true) override
Sets whether the legend symbology item with the specified ley should be 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.
Q_DECL_DEPRECATED 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 Q_DECL_DEPRECATED 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.
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.
QString type() const
Returns the widget type to use.
QVariantMap config() const
Returns the widget configuration.
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.
A binary expression operator, which operates on two values.
An expression node for literal values.
QVariant value() const
The value of the literal.
Handles 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.
static QString quoteFieldExpression(const QString &expression, const QgsVectorLayer *layer)
Validate if the expression is a field in the layer and ensure it is quoted.
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
bool isField() const
Checks whether an expression consists only of a single field reference.
QSet< QString > referencedColumns() const
Gets list of columns referenced by the expression.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes).
const QgsExpressionNode * rootNode() const
Returns the root node of 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)
Fetch next feature and stores in f, returns true on success.
QgsFeatureRenderer(const QString &type)
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
QString type() const
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
static void convertSymbolRotation(QgsSymbol *symbol, const QString &field)
Converts old rotation expressions to symbol level data defined angles.
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.
static void convertSymbolSizeScale(QgsSymbol *symbol, Qgis::ScaleMethod method, const QString &field)
Converts old sizeScale expressions to symbol level data defined sizes.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
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:60
QgsAttributes attributes
Definition qgsfeature.h:69
QgsFeatureId id
Definition qgsfeature.h:68
const QgsSymbol * embeddedSymbol() const
Returns the feature's embedded symbology, or nullptr if the feature has no embedded symbol.
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.
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.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:56
QMetaType::Type type
Definition qgsfield.h:63
bool isNumeric
Definition qgsfield.h:59
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:751
Container of fields for a vector layer.
Definition qgsfields.h:46
QgsField at(int i) const
Returns the field at particular index (must be in range 0..N-1).
Q_INVOKABLE int lookupField(const QString &fieldName) const
Looks up field's index from the field name.
A vector feature renderer which uses numeric attributes to classify features into different ranges.
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.
QString classAttribute() const
Returns the attribute name (or expression) used for the classification.
A polygon-only feature renderer used to display features inverted.
Stores information about one class/rule of a vector layer renderer in a unified way that can be used ...
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.
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
Translates a string using the Qt QTranslator mechanism.
A store for object properties.
bool isActive() const
Returns whether the property is currently active.
A color ramp consisting of random colors, constrained within component ranges.
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
A container for the context for various read/write operations on objects.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
const QString currentLayerId() const
Returns the currently used layer id as string.
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.
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.
QString uuid() const
Returns the unique identifier for this category.
QgsRendererCategory()=default
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...
Q_DECL_DEPRECATED 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)
Represents an individual rule for a rule-based renderer.
Rule based renderer.
Holds SLD export options and other information related to SLD export of a QGIS layer style.
void setExtraProperties(const QVariantMap &properties)
Sets the open ended set of properties that can drive/inform the SLD encoding.
QVariantMap extraProperties() const
Returns the open ended set of properties that can drive/inform the SLD encoding.
A color ramp entity for QgsStyle databases.
Definition qgsstyle.h:1430
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:1398
A database of saved style entities, including symbols, color ramps, text formats and others.
Definition qgsstyle.h:89
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition qgsstyle.cpp:320
QStringList symbolNames() const
Returns a list of names of symbols.
Definition qgsstyle.cpp:342
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 std::unique_ptr< QgsColorRamp > loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static Q_DECL_DEPRECATED bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
Creates an OGC function element.
static bool hasSldSymbolizer(const QDomElement &element)
Returns true if a DOM element contains an SLD Symbolizer element.
static void clearSymbolLayerMasks(QgsSymbol *symbol)
Remove recursively masks from all symbol symbol layers.
static Qgis::ScaleMethod decodeScaleMethod(const QString &str)
Decodes a symbol scale method from a string.
static QDomElement saveColorRamp(const QString &name, const QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp's settings to an XML element.
static void clearSymbolMap(QgsSymbolMap &symbols)
static void resetSymbolLayerIds(QgsSymbol *symbol)
Regenerate recursively unique id from all symbol symbol layers.
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:231
void setColor(const QColor &color) const
Sets the color for the symbol.
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns a list of attributes required to render this feature.
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
static QString displayString(const QVariant &variant, int precision=-1)
Returns a localized representation of value with the given precision, if precision is -1 then precisi...
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based dataset.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const final
Queries the layer for features specified in request.
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:596
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:601
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
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:63
#define QgsDebugError(str)
Definition qgslogger.h:59
#define RENDERER_TAG_NAME
Definition qgsrenderer.h:57
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition qgsrenderer.h:52
QList< QgsSymbol * > QgsSymbolList
Definition qgsrenderer.h:51
Contains information relating to the style entity currently being visited.