QGIS API Documentation 4.1.0-Master (31622b25bb0)
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"
22#include "qgscolorramp.h"
23#include "qgscolorrampimpl.h"
28#include "qgsfeature.h"
29#include "qgsfieldformatter.h"
33#include "qgslogger.h"
34#include "qgsmarkersymbol.h"
35#include "qgspainteffect.h"
37#include "qgsproperty.h"
39#include "qgssldexportcontext.h"
40#include "qgsstyle.h"
42#include "qgssymbol.h"
43#include "qgssymbollayer.h"
44#include "qgssymbollayerutils.h"
45#include "qgsvariantutils.h"
46#include "qgsvectorlayer.h"
47
48#include <QDomDocument>
49#include <QDomElement>
50#include <QRegularExpression>
51#include <QSettings>
52#include <QString>
53#include <QUuid>
54
55using namespace Qt::StringLiterals;
56
57QgsRendererCategory::QgsRendererCategory( const QVariant &value, QgsSymbol *symbol, const QString &label, bool render, const QString &uuid )
58 : mValue( value )
59 , mSymbol( symbol )
60 , mLabel( label )
61 , mRender( render )
62{
63 mUuid = !uuid.isEmpty() ? uuid : QUuid::createUuid().toString();
64}
65
67 : mValue( cat.mValue )
68 , mSymbol( cat.mSymbol ? cat.mSymbol->clone() : nullptr )
69 , mLabel( cat.mLabel )
70 , mRender( cat.mRender )
71 , mUuid( cat.mUuid )
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 )
122 mSymbol.reset( s );
123}
124
126{
127 mLabel = label;
128}
129
131{
132 mRender = render;
133}
134
136{
137 return u"%1::%2::%3:%4\n"_s.arg( mValue.toString(), mLabel, mSymbol->dump() ).arg( mRender );
138}
139
140void QgsRendererCategory::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
141{
142 if ( !mSymbol.get() || props.value( u"attribute"_s, QString() ).toString().isEmpty() )
143 return;
144
145 QString attrName = props[u"attribute"_s].toString();
146
147 QgsSldExportContext context;
148 context.setExtraProperties( props );
149 toSld( doc, element, attrName, context );
150}
151
152bool QgsRendererCategory::toSld( QDomDocument &doc, QDomElement &element, const QString &classAttribute, QgsSldExportContext &context ) const
153{
154 if ( !mSymbol.get() || classAttribute.isEmpty() )
155 return false;
156
157 QString attrName = classAttribute;
158
159 // try to determine if attribute name is actually a field reference or expression.
160 // If it's a field reference, we need to quote it.
161 // Because we don't have access to the layer or fields here, we treat a parser error
162 // as just an unquoted field name (eg a field name with spaces)
163 const QgsExpression attrExpression = QgsExpression( attrName );
164 if ( attrExpression.hasParserError() )
165 {
166 attrName = QgsExpression::quotedColumnRef( attrName );
167 }
168 else if ( attrExpression.isField() )
169 {
170 attrName = QgsExpression::quotedColumnRef( qgis::down_cast<const QgsExpressionNodeColumnRef *>( attrExpression.rootNode() )->name() );
171 }
172
173 QDomElement ruleElem = doc.createElement( u"se:Rule"_s );
174
175 QDomElement nameElem = doc.createElement( u"se:Name"_s );
176 nameElem.appendChild( doc.createTextNode( mLabel ) );
177 ruleElem.appendChild( nameElem );
178
179 QDomElement descrElem = doc.createElement( u"se:Description"_s );
180 QDomElement titleElem = doc.createElement( u"se:Title"_s );
181 QString descrStr = u"%1 is '%2'"_s.arg( attrName, mValue.toString() );
182 titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
183 descrElem.appendChild( titleElem );
184 ruleElem.appendChild( descrElem );
185
186 // create the ogc:Filter for the range
187 QString filterFunc;
188 if ( mValue.userType() == QMetaType::Type::QVariantList )
189 {
190 const QVariantList list = mValue.toList();
191 if ( list.size() == 1 )
192 {
193 filterFunc = u"%1 = %2"_s.arg( attrName, QgsExpression::quotedValue( list.at( 0 ) ) );
194 }
195 else
196 {
197 QStringList valuesList;
198 valuesList.reserve( list.size() );
199 for ( const QVariant &v : list )
200 {
201 valuesList << QgsExpression::quotedValue( v );
202 }
203 filterFunc = u"%1 IN (%2)"_s.arg( attrName, valuesList.join( ',' ) );
204 }
205 }
206 else if ( QgsVariantUtils::isNull( mValue ) || mValue.toString().isEmpty() )
207 {
208 filterFunc = u"ELSE"_s;
209 }
210 else
211 {
212 filterFunc = u"%1 = %2"_s.arg( attrName, QgsExpression::quotedValue( mValue ) );
213 }
214
215 QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc, context );
216
217 // add the mix/max scale denoms if we got any from the callers
218 const QVariantMap oldProps = context.extraProperties();
219 QVariantMap props = oldProps;
220 QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
221 context.setExtraProperties( props );
222 mSymbol->toSld( doc, ruleElem, context );
223 context.setExtraProperties( oldProps );
224 if ( !QgsSymbolLayerUtils::hasSldSymbolizer( ruleElem ) )
225 {
226 // symbol could not be converted to SLD, or is an "empty" symbol. In this case we do not generate a rule, as
227 // SLD spec requires a Symbolizer element to be present
228 return false;
229 }
230
231 element.appendChild( ruleElem );
232 return true;
233}
234
236
238 : QgsFeatureRenderer( u"categorizedSymbol"_s )
239 , mAttrName( attrName )
240{
241 //important - we need a deep copy of the categories list, not a shared copy. This is required because
242 //QgsRendererCategory::symbol() is marked const, and so retrieving the symbol via this method does not
243 //trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
244 for ( const QgsRendererCategory &cat : categories )
245 {
246 if ( !cat.symbol() )
247 {
248 QgsDebugError( u"invalid symbol in a category! ignoring..."_s );
249 }
250 mCategories << cat;
251 }
252}
253
255{
257 QgsCategoryList::const_iterator catIt = mCategories.constBegin();
258 for ( ; catIt != mCategories.constEnd(); ++catIt )
259 {
260 if ( QgsSymbol *catSymbol = catIt->symbol() )
261 {
262 if ( catSymbol->flags().testFlag( Qgis::SymbolFlag::AffectsLabeling ) )
264 }
265 }
266
267 return res;
268}
269
271
273{
274 mSymbolHash.clear();
275
276 for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
277 {
278 const QVariant val = cat.value();
279 if ( val.userType() == QMetaType::Type::QVariantList )
280 {
281 const QVariantList list = val.toList();
282 for ( const QVariant &v : list )
283 {
284 mSymbolHash.insert( v.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
285 }
286 }
287 else
288 {
289 mSymbolHash.insert( val.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
290 }
291 }
292}
293
298
300{
301 bool found = false;
302 return symbolForValue( value, found );
303}
304
305QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value, bool &foundMatchingSymbol ) const
306{
307 foundMatchingSymbol = false;
308
309 // TODO: special case for int, double
310 QHash<QString, QgsSymbol *>::const_iterator it = mSymbolHash.constFind( QgsVariantUtils::isNull( value ) ? QString() : value.toString() );
311 if ( it == mSymbolHash.constEnd() )
312 {
313 if ( mSymbolHash.isEmpty() )
314 {
315 QgsDebugError( u"there are no hashed symbols!!!"_s );
316 }
317 else
318 {
319 QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
320 }
321 return nullptr;
322 }
323
324 foundMatchingSymbol = true;
325
326 return *it;
327}
328
330{
331 return originalSymbolForFeature( feature, context );
332}
333
334QVariant QgsCategorizedSymbolRenderer::valueForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
335{
336 QgsAttributes attrs = feature.attributes();
337 QVariant value;
338 if ( mAttrNum == -1 )
339 {
340 Q_ASSERT( mExpression );
341
342 value = mExpression->evaluate( &context.expressionContext() );
343 }
344 else
345 {
346 value = attrs.value( mAttrNum );
347 }
348
349 return value;
350}
351
353{
354 QVariant value = valueForFeature( feature, context );
355
356 bool foundCategory = false;
357 // find the right symbol for the category
358 QgsSymbol *symbol = symbolForValue( value, foundCategory );
359
360 if ( !foundCategory )
361 {
362 // if no symbol found, use default symbol
363 return symbolForValue( QVariant( "" ), foundCategory );
364 }
365
366 return symbol;
367}
368
369
371{
372 for ( int i = 0; i < mCategories.count(); i++ )
373 {
374 if ( mCategories[i].value() == val )
375 return i;
376 }
377 return -1;
378}
379
381{
382 int idx = -1;
383 for ( int i = 0; i < mCategories.count(); i++ )
384 {
385 if ( mCategories[i].label() == val )
386 {
387 if ( idx != -1 )
388 return -1;
389 else
390 idx = i;
391 }
392 }
393 return idx;
394}
395
396bool QgsCategorizedSymbolRenderer::updateCategoryValue( int catIndex, const QVariant &value )
397{
398 if ( catIndex < 0 || catIndex >= mCategories.size() )
399 return false;
400 mCategories[catIndex].setValue( value );
401 return true;
402}
403
405{
406 if ( catIndex < 0 || catIndex >= mCategories.size() )
407 return false;
408 mCategories[catIndex].setSymbol( symbol );
409 return true;
410}
411
412bool QgsCategorizedSymbolRenderer::updateCategoryLabel( int catIndex, const QString &label )
413{
414 if ( catIndex < 0 || catIndex >= mCategories.size() )
415 return false;
416 mCategories[catIndex].setLabel( label );
417 return true;
418}
419
421{
422 if ( catIndex < 0 || catIndex >= mCategories.size() )
423 return false;
424 mCategories[catIndex].setRenderState( render );
425 return true;
426}
427
429{
430 if ( !cat.symbol() )
431 {
432 QgsDebugError( u"invalid symbol in a category! ignoring..."_s );
433 return;
434 }
435
436 mCategories.append( cat );
437}
438
440{
441 if ( catIndex < 0 || catIndex >= mCategories.size() )
442 return false;
443
444 mCategories.removeAt( catIndex );
445 return true;
446}
447
452
454{
455 if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() )
456 return;
457 mCategories.move( from, to );
458}
459
461{
462 return qgsVariantLessThan( c1.value(), c2.value() );
463}
465{
466 return qgsVariantGreaterThan( c1.value(), c2.value() );
467}
468
470{
471 if ( order == Qt::AscendingOrder )
472 {
473 std::sort( mCategories.begin(), mCategories.end(), valueLessThan );
474 }
475 else
476 {
477 std::sort( mCategories.begin(), mCategories.end(), valueGreaterThan );
478 }
479}
480
482{
483 return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
484}
485
487{
488 return QString::localeAwareCompare( c1.label(), c2.label() ) > 0;
489}
490
492{
493 if ( order == Qt::AscendingOrder )
494 {
495 std::sort( mCategories.begin(), mCategories.end(), labelLessThan );
496 }
497 else
498 {
499 std::sort( mCategories.begin(), mCategories.end(), labelGreaterThan );
500 }
501}
502
504{
505 QgsFeatureRenderer::startRender( context, fields );
506
507 mCounting = context.rendererScale() == 0.0;
508
509 // make sure that the hash table is up to date
510 rebuildHash();
511
512 // find out classification attribute index from name
513 mAttrNum = fields.lookupField( mAttrName );
514 if ( mAttrNum == -1 )
515 {
516 mExpression = std::make_unique<QgsExpression>( mAttrName );
517 mExpression->prepare( &context.expressionContext() );
518 }
519
520 mAttrIsNumeric = mAttrNum != -1 && fields.at( mAttrNum ).isNumeric();
521
522 for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
523 {
524 cat.symbol()->startRender( context, fields );
525 }
526}
527
529{
531
532 for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
533 {
534 cat.symbol()->stopRender( context );
535 }
536 mExpression.reset();
537}
538
540{
541 QSet<QString> attributes;
542
543 // mAttrName can contain either attribute name or an expression.
544 // Sometimes it is not possible to distinguish between those two,
545 // e.g. "a - b" can be both a valid attribute name or expression.
546 // Since we do not have access to fields here, try both options.
547 attributes << mAttrName;
548
549 QgsExpression testExpr( mAttrName );
550 if ( !testExpr.hasParserError() )
551 attributes.unite( testExpr.referencedColumns() );
552
553 QgsCategoryList::const_iterator catIt = mCategories.constBegin();
554 for ( ; catIt != mCategories.constEnd(); ++catIt )
555 {
556 QgsSymbol *catSymbol = catIt->symbol();
557 if ( catSymbol )
558 {
559 attributes.unite( catSymbol->usedAttributes( context ) );
560 }
561 }
562 return attributes;
563}
564
566{
567 QgsExpression testExpr( mAttrName );
568 if ( !testExpr.hasParserError() )
569 {
570 QgsExpressionContext context;
571 context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) ); // unfortunately no layer access available!
572 testExpr.prepare( &context );
573 return testExpr.needsGeometry();
574 }
575 return false;
576}
577
579{
580 QString s = u"CATEGORIZED: idx %1\n"_s.arg( mAttrName );
581 for ( int i = 0; i < mCategories.count(); i++ )
582 s += mCategories[i].dump();
583 return s;
584}
585
600
601void QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
602{
603 QgsSldExportContext context;
604 context.setExtraProperties( props );
605 toSld( doc, element, context );
606}
607
608bool QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
609{
610 const QVariantMap oldProps = context.extraProperties();
611 QVariantMap newProps = oldProps;
612 newProps[u"attribute"_s] = mAttrName;
613 context.setExtraProperties( newProps );
614
615 // create a Rule for each range
616 bool result = true;
617 for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); ++it )
618 {
619 if ( !it->toSld( doc, element, mAttrName, context ) )
620 result = false;
621 }
622 context.setExtraProperties( oldProps );
623 return result;
624}
625
630
632{
633 Q_UNUSED( context )
634 QgsSymbolList lst;
635 lst.reserve( mCategories.count() );
636 for ( const QgsRendererCategory &cat : mCategories )
637 {
638 lst.append( cat.symbol() );
639 }
640 return lst;
641}
642
644{
645 for ( const QgsRendererCategory &cat : mCategories )
646 {
647 QgsStyleSymbolEntity entity( cat.symbol() );
648 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, cat.value().toString(), cat.label() ) ) )
649 return false;
650 }
651
652 if ( mSourceColorRamp )
653 {
655 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
656 return false;
657 }
658
659 return true;
660}
661
663{
664 QDomElement symbolsElem = element.firstChildElement( u"symbols"_s );
665 if ( symbolsElem.isNull() )
666 return nullptr;
667
668 QDomElement catsElem = element.firstChildElement( u"categories"_s );
669 if ( catsElem.isNull() )
670 return nullptr;
671
672 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
673 QgsCategoryList cats;
674
675 // Value from string (long, ulong, double and string)
676 const auto valueFromString = []( const QString &value, const QString &valueType ) -> QVariant {
677 if ( valueType == "double"_L1 )
678 {
679 bool ok;
680 const auto val { value.toDouble( &ok ) };
681 if ( ok )
682 {
683 return val;
684 }
685 }
686 else if ( valueType == "ulong"_L1 )
687 {
688 bool ok;
689 const auto val { value.toULongLong( &ok ) };
690 if ( ok )
691 {
692 return val;
693 }
694 }
695 else if ( valueType == "long"_L1 )
696 {
697 bool ok;
698 const auto val { value.toLongLong( &ok ) };
699 if ( ok )
700 {
701 return val;
702 }
703 }
704 else if ( valueType == "bool"_L1 )
705 {
706 if ( value.toLower() == "false"_L1 )
707 return false;
708 if ( value.toLower() == "true"_L1 )
709 return true;
710 }
711 else if ( valueType == "NULL"_L1 )
712 {
713 // This is the default ("fallback") category
714 return QVariant();
715 }
716 return value;
717 };
718
719 QDomElement catElem = catsElem.firstChildElement();
720 int i = 0;
721 QSet<QString> usedUuids;
722 while ( !catElem.isNull() )
723 {
724 if ( catElem.tagName() == "category"_L1 )
725 {
726 QVariant value;
727 if ( catElem.hasAttribute( u"value"_s ) )
728 {
729 value = valueFromString( catElem.attribute( u"value"_s ), catElem.attribute( u"type"_s, QString() ) );
730 }
731 else
732 {
733 QVariantList values;
734 QDomElement valElem = catElem.firstChildElement();
735 while ( !valElem.isNull() )
736 {
737 if ( valElem.tagName() == "val"_L1 )
738 {
739 values << valueFromString( valElem.attribute( u"value"_s ), valElem.attribute( u"type"_s, QString() ) );
740 }
741 valElem = valElem.nextSiblingElement();
742 }
743 if ( !values.isEmpty() )
744 value = values;
745 }
746
747 QString symbolName = catElem.attribute( u"symbol"_s );
748 QString label = context.projectTranslator()->translate( u"project:layers:%1:legendsymbollabels"_s.arg( context.currentLayerId() ), catElem.attribute( u"label"_s ) );
749 QgsDebugMsgLevel( "context" + u"project:layers:%1:legendsymbollabels"_s.arg( context.currentLayerId() ) + " source " + catElem.attribute( u"label"_s ), 3 );
750
751 bool render = catElem.attribute( u"render"_s ) != "false"_L1;
752 QString uuid = catElem.attribute( u"uuid"_s, QString::number( i++ ) );
753
754 while ( usedUuids.contains( uuid ) )
755 {
756 uuid = QUuid::createUuid().toString();
757 }
758 if ( symbolMap.contains( symbolName ) )
759 {
760 QgsSymbol *symbol = symbolMap.take( symbolName );
761 cats.append( QgsRendererCategory( value, symbol, label, render, uuid ) );
762 usedUuids << uuid;
763 }
764 }
765 catElem = catElem.nextSiblingElement();
766 }
767
768 QString attrName = element.attribute( u"attr"_s );
769
771
772 // delete symbols if there are any more
774
775 // try to load source symbol (optional)
776 QDomElement sourceSymbolElem = element.firstChildElement( u"source-symbol"_s );
777 if ( !sourceSymbolElem.isNull() )
778 {
779 QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
780 if ( sourceSymbolMap.contains( u"0"_s ) )
781 {
782 r->setSourceSymbol( sourceSymbolMap.take( u"0"_s ) );
783 }
784 QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
785 }
786
787 // try to load color ramp (optional)
788 QDomElement sourceColorRampElem = element.firstChildElement( u"colorramp"_s );
789 if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( u"name"_s ) == "[source]"_L1 )
790 {
791 r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ).release() );
792 }
793
794 QDomElement rotationElem = element.firstChildElement( u"rotation"_s );
795 if ( !rotationElem.isNull() && !rotationElem.attribute( u"field"_s ).isEmpty() )
796 {
797 for ( const QgsRendererCategory &cat : r->mCategories )
798 {
799 convertSymbolRotation( cat.symbol(), rotationElem.attribute( u"field"_s ) );
800 }
801 if ( r->mSourceSymbol )
802 {
803 convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( u"field"_s ) );
804 }
805 }
806
807 QDomElement sizeScaleElem = element.firstChildElement( u"sizescale"_s );
808 if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( u"field"_s ).isEmpty() )
809 {
810 for ( const QgsRendererCategory &cat : r->mCategories )
811 {
812 convertSymbolSizeScale( cat.symbol(), QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( u"scalemethod"_s ) ), sizeScaleElem.attribute( u"field"_s ) );
813 }
814 if ( r->mSourceSymbol && r->mSourceSymbol->type() == Qgis::SymbolType::Marker )
815 {
816 convertSymbolSizeScale( r->mSourceSymbol.get(), QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( u"scalemethod"_s ) ), sizeScaleElem.attribute( u"field"_s ) );
817 }
818 }
819
820 QDomElement ddsLegendSizeElem = element.firstChildElement( u"data-defined-size-legend"_s );
821 if ( !ddsLegendSizeElem.isNull() )
822 {
823 r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
824 }
825
826 // TODO: symbol levels
827 return r;
828}
829
830QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
831{
832 // clazy:skip
833 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
834 rendererElem.setAttribute( u"type"_s, u"categorizedSymbol"_s );
835 rendererElem.setAttribute( u"attr"_s, mAttrName );
836
837 // String for type
838 // We just need string, bool, and three numeric types: double, ulong and long for unsigned, signed and float/double
839 const auto stringForType = []( const QMetaType::Type type ) -> QString {
840 if ( type == QMetaType::Type::QChar || type == QMetaType::Type::Int || type == QMetaType::Type::LongLong )
841 {
842 return u"long"_s;
843 }
844 else if ( type == QMetaType::Type::UInt || type == QMetaType::Type::ULongLong )
845 {
846 return u"ulong"_s;
847 }
848 else if ( type == QMetaType::Type::Double )
849 {
850 return u"double"_s;
851 }
852 else if ( type == QMetaType::Type::Bool )
853 {
854 return u"bool"_s;
855 }
856 else // Default: string
857 {
858 return u"string"_s;
859 }
860 };
861
862 // categories
863 if ( !mCategories.isEmpty() )
864 {
865 int i = 0;
867 QDomElement catsElem = doc.createElement( u"categories"_s );
868 QgsCategoryList::const_iterator it = mCategories.constBegin();
869 for ( ; it != mCategories.constEnd(); ++it )
870 {
871 const QgsRendererCategory &cat = *it;
872 QString symbolName = QString::number( i );
873 symbols.insert( symbolName, cat.symbol() );
874
875 QDomElement catElem = doc.createElement( u"category"_s );
876 if ( cat.value().userType() == QMetaType::Type::QVariantList )
877 {
878 const QVariantList list = cat.value().toList();
879 for ( const QVariant &v : list )
880 {
881 QDomElement valueElem = doc.createElement( u"val"_s );
882 valueElem.setAttribute( u"value"_s, v.toString() );
883 valueElem.setAttribute( u"type"_s, stringForType( static_cast<QMetaType::Type>( v.userType() ) ) );
884 catElem.appendChild( valueElem );
885 }
886 }
887 else
888 {
889 if ( QgsVariantUtils::isNull( cat.value() ) )
890 {
891 // We need to save NULL value as specific kind, it is the default ("fallback") category
892 catElem.setAttribute( u"value"_s, "NULL" );
893 catElem.setAttribute( u"type"_s, "NULL" );
894 }
895 else
896 {
897 catElem.setAttribute( u"value"_s, cat.value().toString() );
898 catElem.setAttribute( u"type"_s, stringForType( static_cast<QMetaType::Type>( cat.value().userType() ) ) );
899 }
900 }
901 catElem.setAttribute( u"symbol"_s, symbolName );
902 catElem.setAttribute( u"label"_s, cat.label() );
903 catElem.setAttribute( u"render"_s, cat.renderState() ? "true" : "false" );
904 catElem.setAttribute( u"uuid"_s, cat.uuid() );
905 catsElem.appendChild( catElem );
906 i++;
907 }
908 rendererElem.appendChild( catsElem );
909
910 // save symbols
911 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, u"symbols"_s, doc, context );
912 rendererElem.appendChild( symbolsElem );
913 }
914
915 // save source symbol
916 if ( mSourceSymbol )
917 {
918 QgsSymbolMap sourceSymbols;
919 sourceSymbols.insert( u"0"_s, mSourceSymbol.get() );
920 QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, u"source-symbol"_s, doc, context );
921 rendererElem.appendChild( sourceSymbolElem );
922 }
923
924 // save source color ramp
925 if ( mSourceColorRamp )
926 {
927 QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( u"[source]"_s, mSourceColorRamp.get(), doc );
928 rendererElem.appendChild( colorRampElem );
929 }
930
931 QDomElement rotationElem = doc.createElement( u"rotation"_s );
932 rendererElem.appendChild( rotationElem );
933
934 QDomElement sizeScaleElem = doc.createElement( u"sizescale"_s );
935 rendererElem.appendChild( sizeScaleElem );
936
938 {
939 QDomElement ddsLegendElem = doc.createElement( u"data-defined-size-legend"_s );
940 mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
941 rendererElem.appendChild( ddsLegendElem );
942 }
943
944 saveRendererData( doc, rendererElem, context );
945
946 return rendererElem;
947}
948
949
950QgsLegendSymbolList QgsCategorizedSymbolRenderer::baseLegendSymbolItems() const
951{
953 for ( const QgsRendererCategory &cat : mCategories )
954 {
955 lst << QgsLegendSymbolItem( cat.symbol(), cat.label(), cat.uuid(), true );
956 }
957 return lst;
958}
959
960QString QgsCategorizedSymbolRenderer::displayString( const QVariant &v, int precision )
961{
962 return QgsVariantUtils::displayString( v, precision );
963}
964
966{
968 {
969 // check that all symbols that have the same size expression
970 QgsProperty ddSize;
971 for ( const QgsRendererCategory &category : mCategories )
972 {
973 const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( category.symbol() );
974 if ( ddSize )
975 {
976 QgsProperty sSize( symbol->dataDefinedSize() );
977 if ( sSize != ddSize )
978 {
979 // no common size expression
980 return baseLegendSymbolItems();
981 }
982 }
983 else
984 {
985 ddSize = symbol->dataDefinedSize();
986 }
987 }
988
989 if ( ddSize && ddSize.isActive() )
990 {
992
994 ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
995 lst += ddSizeLegend.legendSymbolList();
996
997 lst += baseLegendSymbolItems();
998 return lst;
999 }
1000 }
1001
1002 return baseLegendSymbolItems();
1003}
1004
1006{
1007 const QVariant value = valueForFeature( feature, context );
1008
1009 // "all other values" category value (AKA "else" rule) is represented with an invalid QVariant
1010 QString elseRuleUUID;
1011
1012 for ( const QgsRendererCategory &cat : mCategories )
1013 {
1014 bool match = false;
1015
1016 if ( QgsVariantUtils::isNull( cat.value() ) || ( mAttrIsNumeric && cat.value().toString().isEmpty() ) )
1017 {
1018 elseRuleUUID = cat.uuid();
1019 }
1020
1021 if ( cat.value().userType() == QMetaType::Type::QVariantList )
1022 {
1023 const QVariantList list = cat.value().toList();
1024 for ( const QVariant &v : list )
1025 {
1026 if ( value == v )
1027 {
1028 match = true;
1029 break;
1030 }
1031 }
1032 }
1033 else
1034 {
1035 // NULL cat value may be stored as an empty string or an invalid variant, depending on how
1036 // the renderer was constructed and which QGIS version was used
1037 if ( QgsVariantUtils::isNull( value ) )
1038 {
1039 match = cat.value().toString().isEmpty() || QgsVariantUtils::isNull( cat.value() );
1040 }
1041 else
1042 {
1043 match = value == cat.value();
1044 }
1045 }
1046
1047 if ( match )
1048 {
1049 if ( cat.renderState() || mCounting )
1050 return QSet< QString >() << cat.uuid();
1051 else
1052 return QSet< QString >();
1053 }
1054 }
1055
1056 // if there is an "else" rule category, then the feature will be rendered with that category symbol
1057 if ( !elseRuleUUID.isEmpty() )
1058 {
1059 return QSet< QString >() << elseRuleUUID;
1060 }
1061
1062 return QSet< QString >();
1063}
1064
1065QString QgsCategorizedSymbolRenderer::legendKeyToExpression( const QString &key, QgsVectorLayer *layer, bool &ok ) const
1066{
1067 ok = false;
1068 int i = 0;
1069 for ( i = 0; i < mCategories.size(); i++ )
1070 {
1071 if ( mCategories[i].uuid() == key )
1072 {
1073 ok = true;
1074 break;
1075 }
1076 }
1077
1078 if ( !ok )
1079 {
1080 ok = false;
1081 return QString();
1082 }
1083
1084 const int fieldIndex = layer ? layer->fields().lookupField( mAttrName ) : -1;
1085 const bool isNumeric = layer && fieldIndex >= 0 ? layer->fields().at( fieldIndex ).isNumeric() : false;
1086 const QMetaType::Type fieldType = layer && fieldIndex >= 0 ? layer->fields().at( fieldIndex ).type() : QMetaType::Type::UnknownType;
1087 const QString attributeComponent = QgsExpression::quoteFieldExpression( mAttrName, layer );
1088
1089 ok = true;
1090 const QgsRendererCategory &cat = mCategories[i];
1091 if ( cat.value().userType() == QMetaType::Type::QVariantList )
1092 {
1093 const QVariantList list = cat.value().toList();
1094 QStringList parts;
1095 parts.reserve( list.size() );
1096 for ( const QVariant &v : list )
1097 {
1098 parts.append( QgsExpression::quotedValue( v ) );
1099 }
1100
1101 return u"%1 IN (%2)"_s.arg( attributeComponent, parts.join( ", "_L1 ) );
1102 }
1103 else
1104 {
1105 // Numeric NULL cat value is stored as an empty string
1106 QVariant value = cat.value();
1107 if ( isNumeric && value.toString().isEmpty() )
1108 {
1109 value = QVariant();
1110 }
1111
1112 if ( QgsVariantUtils::isNull( value ) )
1113 return u"%1 IS NULL"_s.arg( attributeComponent );
1114 else if ( fieldType == QMetaType::Type::UnknownType )
1115 return u"%1 = %2"_s.arg( attributeComponent, QgsExpression::quotedValue( value ) );
1116 else
1117 return u"%1 = %2"_s.arg( attributeComponent, QgsExpression::quotedValue( value, fieldType ) );
1118 }
1119}
1120
1125
1127{
1128 return mSourceSymbol.get();
1129}
1130
1135
1140
1145
1150
1152{
1153 setSourceColorRamp( ramp );
1154 double num = mCategories.count() - 1;
1155 double count = 0;
1156
1157 QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp );
1158 if ( randomRamp )
1159 {
1160 //ramp is a random colors ramp, so inform it of the total number of required colors
1161 //this allows the ramp to pregenerate a set of visually distinctive colors
1162 randomRamp->setTotalColorCount( mCategories.count() );
1163 }
1164
1165 for ( const QgsRendererCategory &cat : mCategories )
1166 {
1167 double value = count / num;
1168 cat.symbol()->setColor( mSourceColorRamp->color( value ) );
1169 count += 1;
1170 }
1171}
1172
1174{
1175 int i = 0;
1176 for ( const QgsRendererCategory &cat : mCategories )
1177 {
1178 QgsSymbol *symbol = sym->clone();
1179 symbol->setColor( cat.symbol()->color() );
1180 updateCategorySymbol( i, symbol );
1181 ++i;
1182 }
1183 setSourceSymbol( sym->clone() );
1184}
1185
1187{
1188 return true;
1189}
1190
1192{
1193 for ( const QgsRendererCategory &category : std::as_const( mCategories ) )
1194 {
1195 if ( category.uuid() == key )
1196 {
1197 return category.renderState();
1198 }
1199 }
1200
1201 return true;
1202}
1203
1205{
1206 bool ok = false;
1207 int i = 0;
1208 for ( i = 0; i < mCategories.size(); i++ )
1209 {
1210 if ( mCategories[i].uuid() == key )
1211 {
1212 ok = true;
1213 break;
1214 }
1215 }
1216
1217 if ( ok )
1218 updateCategorySymbol( i, symbol );
1219 else
1220 delete symbol;
1221}
1222
1223void QgsCategorizedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
1224{
1225 for ( int i = 0; i < mCategories.size(); i++ )
1226 {
1227 if ( mCategories[i].uuid() == key )
1228 {
1229 updateCategoryRenderState( i, state );
1230 break;
1231 }
1232 }
1233}
1234
1236{
1237 std::unique_ptr< QgsCategorizedSymbolRenderer > r;
1238 if ( renderer->type() == "categorizedSymbol"_L1 )
1239 {
1240 r.reset( static_cast<QgsCategorizedSymbolRenderer *>( renderer->clone() ) );
1241 }
1242 else if ( renderer->type() == "graduatedSymbol"_L1 )
1243 {
1244 const QgsGraduatedSymbolRenderer *graduatedSymbolRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1245 if ( graduatedSymbolRenderer )
1246 {
1247 r = std::make_unique<QgsCategorizedSymbolRenderer>( QString(), QgsCategoryList() );
1248 if ( graduatedSymbolRenderer->sourceSymbol() )
1249 r->setSourceSymbol( graduatedSymbolRenderer->sourceSymbol()->clone() );
1250 if ( graduatedSymbolRenderer->sourceColorRamp() )
1251 {
1252 r->setSourceColorRamp( graduatedSymbolRenderer->sourceColorRamp()->clone() );
1253 }
1254 r->setClassAttribute( graduatedSymbolRenderer->classAttribute() );
1255 }
1256 }
1257 else if ( renderer->type() == "RuleRenderer"_L1 )
1258 {
1259 const QgsRuleBasedRenderer *ruleBasedSymbolRenderer = dynamic_cast<const QgsRuleBasedRenderer *>( renderer );
1260 if ( ruleBasedSymbolRenderer )
1261 {
1262 r = std::make_unique<QgsCategorizedSymbolRenderer>( QString(), QgsCategoryList() );
1263
1264 const QList< QgsRuleBasedRenderer::Rule * > rules = const_cast< QgsRuleBasedRenderer * >( ruleBasedSymbolRenderer )->rootRule()->children();
1265 bool canConvert = true;
1266
1267 bool isFirst = true;
1268 QString attribute;
1269 QVariant value;
1271
1272 for ( QgsRuleBasedRenderer::Rule *rule : rules )
1273 {
1274 if ( rule->isElse() || rule->minimumScale() != 0 || rule->maximumScale() != 0 || !rule->symbol() || !rule->children().isEmpty() )
1275 {
1276 canConvert = false;
1277 break;
1278 }
1279
1280 QgsExpression e( rule->filterExpression() );
1281
1282 if ( !e.rootNode() )
1283 {
1284 canConvert = false;
1285 break;
1286 }
1287
1288 if ( const QgsExpressionNodeBinaryOperator *binOp = dynamic_cast<const QgsExpressionNodeBinaryOperator *>( e.rootNode() ) )
1289 {
1290 if ( binOp->op() == QgsExpressionNodeBinaryOperator::boEQ )
1291 {
1292 const QString left = binOp->opLeft()->dump();
1293 if ( !isFirst && left != attribute )
1294 {
1295 canConvert = false;
1296 break;
1297 }
1298 else if ( isFirst )
1299 {
1300 attribute = left;
1301 }
1302
1303 const QgsExpressionNodeLiteral *literal = dynamic_cast<const QgsExpressionNodeLiteral *>( binOp->opRight() );
1304 if ( literal )
1305 {
1307 cat.setValue( literal->value() );
1308 cat.setSymbol( rule->symbol()->clone() );
1309 cat.setLabel( rule->label().isEmpty() ? literal->value().toString() : rule->label() );
1310 cat.setRenderState( rule->active() );
1311 categories.append( cat );
1312 }
1313 else
1314 {
1315 canConvert = false;
1316 break;
1317 }
1318 }
1319 else
1320 {
1321 canConvert = false;
1322 }
1323 }
1324 else
1325 {
1326 canConvert = false;
1327 break;
1328 }
1329
1330 isFirst = false;
1331 }
1332
1333 if ( canConvert )
1334 {
1335 r = std::make_unique< QgsCategorizedSymbolRenderer >( attribute, categories );
1336 }
1337 else
1338 {
1339 r.reset();
1340 }
1341 }
1342 }
1343 else if ( renderer->type() == "pointDisplacement"_L1 || renderer->type() == "pointCluster"_L1 )
1344 {
1345 const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
1346 if ( pointDistanceRenderer )
1347 r.reset( convertFromRenderer( pointDistanceRenderer->embeddedRenderer() ) );
1348 }
1349 else if ( renderer->type() == "invertedPolygonRenderer"_L1 )
1350 {
1351 const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
1352 if ( invertedPolygonRenderer )
1353 r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1354 }
1355 else if ( renderer->type() == "embeddedSymbol"_L1 && layer )
1356 {
1357 const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
1361 req.setNoAttributes();
1362 QgsFeatureIterator it = layer->getFeatures( req );
1363 QgsFeature feature;
1364 while ( it.nextFeature( feature ) && categories.size() < 2000 )
1365 {
1366 if ( feature.embeddedSymbol() )
1367 categories.append( QgsRendererCategory( feature.id(), feature.embeddedSymbol()->clone(), QString::number( feature.id() ) ) );
1368 }
1369 categories.append( QgsRendererCategory( QVariant(), embeddedRenderer->defaultSymbol()->clone(), QString() ) );
1370 r = std::make_unique<QgsCategorizedSymbolRenderer>( u"$id"_s, categories );
1371 }
1372
1373 // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1374 // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
1375
1376 if ( !r )
1377 {
1378 r = std::make_unique< QgsCategorizedSymbolRenderer >( QString(), QgsCategoryList() );
1379 QgsRenderContext context;
1380 QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
1381 if ( !symbols.isEmpty() )
1382 {
1383 QgsSymbol *newSymbol = symbols.at( 0 )->clone();
1386 r->setSourceSymbol( newSymbol );
1387 }
1388 }
1389
1390 renderer->copyRendererData( r.get() );
1391
1392 return r.release();
1393}
1394
1399
1404
1406 QgsStyle *style, Qgis::SymbolType type, QVariantList &unmatchedCategories, QStringList &unmatchedSymbols, const bool caseSensitive, const bool useTolerantMatch
1407)
1408{
1409 if ( !style )
1410 return 0;
1411
1412 int matched = 0;
1413 unmatchedSymbols = style->symbolNames();
1414 const QSet< QString > allSymbolNames( unmatchedSymbols.begin(), unmatchedSymbols.end() );
1415
1416 const thread_local QRegularExpression tolerantMatchRe( u"[^\\w\\d ]"_s, QRegularExpression::UseUnicodePropertiesOption );
1417
1418 for ( int catIdx = 0; catIdx < mCategories.count(); ++catIdx )
1419 {
1420 const QVariant value = mCategories.at( catIdx ).value();
1421 const QString val = value.toString().trimmed();
1422 std::unique_ptr< QgsSymbol > symbol( style->symbol( val ) );
1423 // case-sensitive match
1424 if ( symbol && symbol->type() == type )
1425 {
1426 matched++;
1427 unmatchedSymbols.removeAll( val );
1428 updateCategorySymbol( catIdx, symbol.release() );
1429 continue;
1430 }
1431
1432 if ( !caseSensitive || useTolerantMatch )
1433 {
1434 QString testVal = val;
1435 if ( useTolerantMatch )
1436 testVal.replace( tolerantMatchRe, QString() );
1437
1438 bool foundMatch = false;
1439 for ( const QString &name : allSymbolNames )
1440 {
1441 QString testName = name.trimmed();
1442 if ( useTolerantMatch )
1443 testName.replace( tolerantMatchRe, QString() );
1444
1445 if ( testName == testVal || ( !caseSensitive && testName.trimmed().compare( testVal, Qt::CaseInsensitive ) == 0 ) )
1446 {
1447 // found a case-insensitive match
1448 std::unique_ptr< QgsSymbol > symbol( style->symbol( name ) );
1449 if ( symbol && symbol->type() == type )
1450 {
1451 matched++;
1452 unmatchedSymbols.removeAll( name );
1453 updateCategorySymbol( catIdx, symbol.release() );
1454 foundMatch = true;
1455 break;
1456 }
1457 }
1458 }
1459 if ( foundMatch )
1460 continue;
1461 }
1462
1463 unmatchedCategories << value;
1464 }
1465
1466 return matched;
1467}
1468
1469QgsCategoryList QgsCategorizedSymbolRenderer::createCategories( const QList<QVariant> &values, const QgsSymbol *symbol, QgsVectorLayer *layer, const QString &attributeName )
1470{
1471 QgsCategoryList cats;
1472 QVariantList vals = values;
1473 // sort the categories first
1474 QgsSymbolLayerUtils::sortVariantList( vals, Qt::AscendingOrder );
1475
1476 if ( layer && !attributeName.isNull() )
1477 {
1478 const QgsFields fields = layer->fields();
1479 for ( const QVariant &value : vals )
1480 {
1481 QgsSymbol *newSymbol = symbol->clone();
1483 if ( !QgsVariantUtils::isNull( value ) )
1484 {
1485 const int fieldIdx = fields.lookupField( attributeName );
1486 QString categoryName = QgsVariantUtils::displayString( value );
1487 if ( fieldIdx != -1 )
1488 {
1489 const QgsField field = fields.at( fieldIdx );
1490 const QgsEditorWidgetSetup setup = field.editorWidgetSetup();
1492 categoryName = formatter->representValue( layer, fieldIdx, setup.config(), QVariant(), value );
1493 }
1494 cats.append( QgsRendererCategory( value, newSymbol, categoryName, true ) );
1495 }
1496 }
1497 }
1498
1499 // add null (default) value
1500 QgsSymbol *newSymbol = symbol->clone();
1502 cats.append( QgsRendererCategory( QVariant(), newSymbol, QString(), true ) );
1503
1504 return cats;
1505}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2329
@ EmbeddedSymbols
Retrieve any embedded feature symbology.
Definition qgis.h:2334
QFlags< FeatureRendererFlag > FeatureRendererFlags
Flags controlling behavior of vector feature renderers.
Definition qgis.h:886
@ AffectsLabeling
If present, indicates that the renderer will participate in the map labeling problem.
Definition qgis.h:877
SymbolType
Symbol types.
Definition qgis.h:637
@ Marker
Marker symbol.
Definition qgis.h:638
@ AffectsLabeling
If present, indicates that the symbol will participate in the map labeling problem.
Definition qgis.h:897
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 mAttrIsNumeric
whether the attribute is numeric (derived from attribute name in startRender)
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,...
static QString buildCategorizedFilter(const QString &attributeName, const QgsFields &fields, const Categories &categories)
Builds a filter expression from categories.
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:64
QgsFeatureId id
Definition qgsfeature.h:63
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:749
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:1491
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:1462
A database of saved style entities, including symbols, color ramps, text formats and others.
Definition qgsstyle.h:91
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition qgsstyle.cpp:336
QStringList symbolNames() const
Returns a list of names of symbols.
Definition qgsstyle.cpp:358
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:227
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.