QGIS API Documentation 3.39.0-Master (8f1a6e30482)
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 ***************************************************************************/
15#include <algorithm>
16
18
20#include "qgssymbol.h"
21#include "qgssymbollayerutils.h"
22#include "qgscolorramp.h"
23#include "qgscolorrampimpl.h"
27#include "qgspainteffect.h"
28#include "qgssymbollayer.h"
29#include "qgsfeature.h"
30#include "qgsvectorlayer.h"
31#include "qgslogger.h"
32#include "qgsproperty.h"
33#include "qgsstyle.h"
34#include "qgsfieldformatter.h"
36#include "qgsapplication.h"
40#include "qgsmarkersymbol.h"
42
43#include <QDomDocument>
44#include <QDomElement>
45#include <QSettings> // for legend
46#include <QRegularExpression>
47#include <QUuid>
48
49QgsRendererCategory::QgsRendererCategory( const QVariant &value, QgsSymbol *symbol, const QString &label, bool render, const QString &uuid )
50 : mValue( value )
51 , mSymbol( symbol )
52 , mLabel( label )
53 , mRender( render )
54{
55 mUuid = !uuid.isEmpty() ? uuid : QUuid::createUuid().toString();
56}
57
59 : mValue( cat.mValue )
60 , mSymbol( cat.mSymbol ? cat.mSymbol->clone() : nullptr )
61 , mLabel( cat.mLabel )
62 , mRender( cat.mRender )
63 , mUuid( cat.mUuid )
64{
65}
66
68{
69 mValue = cat.mValue;
70 mSymbol.reset( cat.mSymbol ? cat.mSymbol->clone() : nullptr );
71 mLabel = cat.mLabel;
72 mRender = cat.mRender;
73 mUuid = cat.mUuid;
74 return *this;
75}
76
78
80{
81 return mUuid;
82}
83
85{
86 return mValue;
87}
88
90{
91 return mSymbol.get();
92}
93
95{
96 return mLabel;
97}
98
100{
101 return mRender;
102}
103
104void QgsRendererCategory::setValue( const QVariant &value )
105{
106 mValue = value;
107}
108
110{
111 if ( mSymbol.get() != s ) mSymbol.reset( s );
112}
113
114void QgsRendererCategory::setLabel( const QString &label )
115{
116 mLabel = label;
117}
118
120{
121 mRender = render;
122}
123
125{
126 return QStringLiteral( "%1::%2::%3:%4\n" ).arg( mValue.toString(), mLabel, mSymbol->dump() ).arg( mRender );
127}
128
129void QgsRendererCategory::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
130{
131 if ( !mSymbol.get() || props.value( QStringLiteral( "attribute" ), QString() ).toString().isEmpty() )
132 return;
133
134 QString attrName = props[ QStringLiteral( "attribute" )].toString();
135
136 // try to determine if attribute name is actually a field reference or expression.
137 // If it's a field reference, we need to quote it.
138 // Because we don't have access to the layer or fields here, we treat a parser error
139 // as just an unquoted field name (eg a field name with spaces)
140 const QgsExpression attrExpression = QgsExpression( attrName );
141 if ( attrExpression.hasParserError() )
142 {
143 attrName = QgsExpression::quotedColumnRef( attrName );
144 }
145 else if ( attrExpression.isField() )
146 {
148 qgis::down_cast<const QgsExpressionNodeColumnRef *>( attrExpression.rootNode() )->name()
149 );
150 }
151
152 QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
153 element.appendChild( ruleElem );
154
155 QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
156 nameElem.appendChild( doc.createTextNode( mLabel ) );
157 ruleElem.appendChild( nameElem );
158
159 QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
160 QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
161 QString descrStr = QStringLiteral( "%1 is '%2'" ).arg( attrName, mValue.toString() );
162 titleElem.appendChild( doc.createTextNode( !mLabel.isEmpty() ? mLabel : descrStr ) );
163 descrElem.appendChild( titleElem );
164 ruleElem.appendChild( descrElem );
165
166 // create the ogc:Filter for the range
167 QString filterFunc;
168 if ( mValue.userType() == QMetaType::Type::QVariantList )
169 {
170 const QVariantList list = mValue.toList();
171 if ( list.size() == 1 )
172 {
173 filterFunc = QStringLiteral( "%1 = %2" ).arg( attrName, QgsExpression::quotedValue( list.at( 0 ) ) );
174 }
175 else
176 {
177 QStringList valuesList;
178 valuesList.reserve( list.size() );
179 for ( const QVariant &v : list )
180 {
181 valuesList << QgsExpression::quotedValue( v );
182 }
183 filterFunc = QStringLiteral( "%1 IN (%2)" ).arg( attrName,
184 valuesList.join( ',' ) );
185 }
186 }
187 else if ( QgsVariantUtils::isNull( mValue ) || mValue.toString().isEmpty() )
188 {
189 filterFunc = QStringLiteral( "ELSE" );
190 }
191 else
192 {
193 filterFunc = QStringLiteral( "%1 = %2" ).arg( attrName, QgsExpression::quotedValue( mValue ) );
194 }
195
196 QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, filterFunc );
197
198 // add the mix/max scale denoms if we got any from the callers
199 QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
200
201 mSymbol->toSld( doc, ruleElem, props );
202}
203
205
207 : QgsFeatureRenderer( QStringLiteral( "categorizedSymbol" ) )
208 , mAttrName( attrName )
209{
210 //important - we need a deep copy of the categories list, not a shared copy. This is required because
211 //QgsRendererCategory::symbol() is marked const, and so retrieving the symbol via this method does not
212 //trigger a detachment and copy of mCategories BUT that same method CAN be used to modify a symbol in place
213 for ( const QgsRendererCategory &cat : categories )
214 {
215 if ( !cat.symbol() )
216 {
217 QgsDebugError( QStringLiteral( "invalid symbol in a category! ignoring..." ) );
218 }
219 mCategories << cat;
220 }
221}
222
224{
226 QgsCategoryList::const_iterator catIt = mCategories.constBegin();
227 for ( ; catIt != mCategories.constEnd(); ++catIt )
228 {
229 if ( QgsSymbol *catSymbol = catIt->symbol() )
230 {
231 if ( catSymbol->flags().testFlag( Qgis::SymbolFlag::AffectsLabeling ) )
233 }
234 }
235
236 return res;
237}
238
240
242{
243 mSymbolHash.clear();
244
245 for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
246 {
247 const QVariant val = cat.value();
248 if ( val.userType() == QMetaType::Type::QVariantList )
249 {
250 const QVariantList list = val.toList();
251 for ( const QVariant &v : list )
252 {
253 mSymbolHash.insert( v.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
254 }
255 }
256 else
257 {
258 mSymbolHash.insert( val.toString(), ( cat.renderState() || mCounting ) ? cat.symbol() : nullptr );
259 }
260 }
261}
262
267
269{
270 bool found = false;
271 return symbolForValue( value, found );
272}
273
274QgsSymbol *QgsCategorizedSymbolRenderer::symbolForValue( const QVariant &value, bool &foundMatchingSymbol ) const
275{
276 foundMatchingSymbol = false;
277
278 // TODO: special case for int, double
279 QHash<QString, QgsSymbol *>::const_iterator it = mSymbolHash.constFind( QgsVariantUtils::isNull( value ) ? QString() : value.toString() );
280 if ( it == mSymbolHash.constEnd() )
281 {
282 if ( mSymbolHash.isEmpty() )
283 {
284 QgsDebugError( QStringLiteral( "there are no hashed symbols!!!" ) );
285 }
286 else
287 {
288 QgsDebugMsgLevel( "attribute value not found: " + value.toString(), 3 );
289 }
290 return nullptr;
291 }
292
293 foundMatchingSymbol = true;
294
295 return *it;
296}
297
299{
300 return originalSymbolForFeature( feature, context );
301}
302
303QVariant QgsCategorizedSymbolRenderer::valueForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
304{
305 QgsAttributes attrs = feature.attributes();
306 QVariant value;
307 if ( mAttrNum == -1 )
308 {
309 Q_ASSERT( mExpression );
310
311 value = mExpression->evaluate( &context.expressionContext() );
312 }
313 else
314 {
315 value = attrs.value( mAttrNum );
316 }
317
318 return value;
319}
320
322{
323 QVariant value = valueForFeature( feature, context );
324
325 bool foundCategory = false;
326 // find the right symbol for the category
327 QgsSymbol *symbol = symbolForValue( value, foundCategory );
328
329 if ( !foundCategory )
330 {
331 // if no symbol found, use default symbol
332 return symbolForValue( QVariant( "" ), foundCategory );
333 }
334
335 return symbol;
336}
337
338
340{
341 for ( int i = 0; i < mCategories.count(); i++ )
342 {
343 if ( mCategories[i].value() == val )
344 return i;
345 }
346 return -1;
347}
348
350{
351 int idx = -1;
352 for ( int i = 0; i < mCategories.count(); i++ )
353 {
354 if ( mCategories[i].label() == val )
355 {
356 if ( idx != -1 )
357 return -1;
358 else
359 idx = i;
360 }
361 }
362 return idx;
363}
364
365bool QgsCategorizedSymbolRenderer::updateCategoryValue( int catIndex, const QVariant &value )
366{
367 if ( catIndex < 0 || catIndex >= mCategories.size() )
368 return false;
369 mCategories[catIndex].setValue( value );
370 return true;
371}
372
374{
375 if ( catIndex < 0 || catIndex >= mCategories.size() )
376 return false;
377 mCategories[catIndex].setSymbol( symbol );
378 return true;
379}
380
381bool QgsCategorizedSymbolRenderer::updateCategoryLabel( int catIndex, const QString &label )
382{
383 if ( catIndex < 0 || catIndex >= mCategories.size() )
384 return false;
385 mCategories[catIndex].setLabel( label );
386 return true;
387}
388
390{
391 if ( catIndex < 0 || catIndex >= mCategories.size() )
392 return false;
393 mCategories[catIndex].setRenderState( render );
394 return true;
395}
396
398{
399 if ( !cat.symbol() )
400 {
401 QgsDebugError( QStringLiteral( "invalid symbol in a category! ignoring..." ) );
402 return;
403 }
404
405 mCategories.append( cat );
406}
407
409{
410 if ( catIndex < 0 || catIndex >= mCategories.size() )
411 return false;
412
413 mCategories.removeAt( catIndex );
414 return true;
415}
416
421
423{
424 if ( from < 0 || from >= mCategories.size() || to < 0 || to >= mCategories.size() ) return;
425 mCategories.move( from, to );
426}
427
429{
430 return qgsVariantLessThan( c1.value(), c2.value() );
431}
433{
434 return qgsVariantGreaterThan( c1.value(), c2.value() );
435}
436
438{
439 if ( order == Qt::AscendingOrder )
440 {
441 std::sort( mCategories.begin(), mCategories.end(), valueLessThan );
442 }
443 else
444 {
445 std::sort( mCategories.begin(), mCategories.end(), valueGreaterThan );
446 }
447}
448
450{
451 return QString::localeAwareCompare( c1.label(), c2.label() ) < 0;
452}
453
455{
456 return !labelLessThan( c1, c2 );
457}
458
460{
461 if ( order == Qt::AscendingOrder )
462 {
463 std::sort( mCategories.begin(), mCategories.end(), labelLessThan );
464 }
465 else
466 {
467 std::sort( mCategories.begin(), mCategories.end(), labelGreaterThan );
468 }
469}
470
472{
473 QgsFeatureRenderer::startRender( context, fields );
474
475 mCounting = context.rendererScale() == 0.0;
476
477 // make sure that the hash table is up to date
478 rebuildHash();
479
480 // find out classification attribute index from name
481 mAttrNum = fields.lookupField( mAttrName );
482 if ( mAttrNum == -1 )
483 {
484 mExpression.reset( new QgsExpression( mAttrName ) );
485 mExpression->prepare( &context.expressionContext() );
486 }
487
488 for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
489 {
490 cat.symbol()->startRender( context, fields );
491 }
492}
493
495{
497
498 for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
499 {
500 cat.symbol()->stopRender( context );
501 }
502 mExpression.reset();
503}
504
506{
507 QSet<QString> attributes;
508
509 // mAttrName can contain either attribute name or an expression.
510 // Sometimes it is not possible to distinguish between those two,
511 // e.g. "a - b" can be both a valid attribute name or expression.
512 // Since we do not have access to fields here, try both options.
513 attributes << mAttrName;
514
515 QgsExpression testExpr( mAttrName );
516 if ( !testExpr.hasParserError() )
517 attributes.unite( testExpr.referencedColumns() );
518
519 QgsCategoryList::const_iterator catIt = mCategories.constBegin();
520 for ( ; catIt != mCategories.constEnd(); ++catIt )
521 {
522 QgsSymbol *catSymbol = catIt->symbol();
523 if ( catSymbol )
524 {
525 attributes.unite( catSymbol->usedAttributes( context ) );
526 }
527 }
528 return attributes;
529}
530
532{
533 QgsExpression testExpr( mAttrName );
534 if ( !testExpr.hasParserError() )
535 {
536 QgsExpressionContext context;
537 context.appendScopes( QgsExpressionContextUtils::globalProjectLayerScopes( nullptr ) ); // unfortunately no layer access available!
538 testExpr.prepare( &context );
539 return testExpr.needsGeometry();
540 }
541 return false;
542}
543
545{
546 QString s = QStringLiteral( "CATEGORIZED: idx %1\n" ).arg( mAttrName );
547 for ( int i = 0; i < mCategories.count(); i++ )
548 s += mCategories[i].dump();
549 return s;
550}
551
566
567void QgsCategorizedSymbolRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
568{
569 QVariantMap newProps = props;
570 newProps[ QStringLiteral( "attribute" )] = mAttrName;
571
572 // create a Rule for each range
573 for ( QgsCategoryList::const_iterator it = mCategories.constBegin(); it != mCategories.constEnd(); ++it )
574 {
575 it->toSld( doc, element, newProps );
576 }
577}
578
580{
581 int attrNum = fields.lookupField( mAttrName );
582 bool isExpression = ( attrNum == -1 );
583
584 bool hasDefault = false;
585 bool defaultActive = false;
586 bool allActive = true;
587 bool noneActive = true;
588
589 //we need to build lists of both inactive and active values, as either list may be required
590 //depending on whether the default category is active or not
591 QString activeValues;
592 QString inactiveValues;
593
594 for ( const QgsRendererCategory &cat : std::as_const( mCategories ) )
595 {
596 if ( cat.value() == "" || QgsVariantUtils::isNull( cat.value() ) )
597 {
598 hasDefault = true;
599 defaultActive = cat.renderState();
600 }
601
602 noneActive = noneActive && !cat.renderState();
603 allActive = allActive && cat.renderState();
604
605 const bool isList = cat.value().userType() == QMetaType::Type::QVariantList;
606 QString value = QgsExpression::quotedValue( cat.value(), static_cast<QMetaType::Type>( cat.value().userType() ) );
607
608 if ( !cat.renderState() )
609 {
610 if ( value != "" )
611 {
612 if ( isList )
613 {
614 const QVariantList list = cat.value().toList();
615 for ( const QVariant &v : list )
616 {
617 if ( !inactiveValues.isEmpty() )
618 inactiveValues.append( ',' );
619
620 inactiveValues.append( QgsExpression::quotedValue( v, isExpression ? static_cast<QMetaType::Type>( v.userType() ) : fields.at( attrNum ).type() ) );
621 }
622 }
623 else
624 {
625 if ( !inactiveValues.isEmpty() )
626 inactiveValues.append( ',' );
627
628 inactiveValues.append( value );
629 }
630 }
631 }
632 else
633 {
634 if ( value != "" )
635 {
636 if ( isList )
637 {
638 const QVariantList list = cat.value().toList();
639 for ( const QVariant &v : list )
640 {
641 if ( !activeValues.isEmpty() )
642 activeValues.append( ',' );
643
644 activeValues.append( QgsExpression::quotedValue( v, isExpression ? static_cast<QMetaType::Type>( v.userType() ) : fields.at( attrNum ).type() ) );
645 }
646 }
647 else
648 {
649 if ( !activeValues.isEmpty() )
650 activeValues.append( ',' );
651
652 activeValues.append( value );
653 }
654 }
655 }
656 }
657
658 QString attr = isExpression ? mAttrName : QStringLiteral( "\"%1\"" ).arg( mAttrName );
659
660 if ( allActive && hasDefault )
661 {
662 return QString();
663 }
664 else if ( noneActive )
665 {
666 return QStringLiteral( "FALSE" );
667 }
668 else if ( defaultActive )
669 {
670 return QStringLiteral( "(%1) NOT IN (%2) OR (%1) IS NULL" ).arg( attr, inactiveValues );
671 }
672 else
673 {
674 return QStringLiteral( "(%1) IN (%2)" ).arg( attr, activeValues );
675 }
676}
677
679{
680 Q_UNUSED( context )
681 QgsSymbolList lst;
682 lst.reserve( mCategories.count() );
683 for ( const QgsRendererCategory &cat : mCategories )
684 {
685 lst.append( cat.symbol() );
686 }
687 return lst;
688}
689
691{
692 for ( const QgsRendererCategory &cat : mCategories )
693 {
694 QgsStyleSymbolEntity entity( cat.symbol() );
695 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity, cat.value().toString(), cat.label() ) ) )
696 return false;
697 }
698
699 if ( mSourceColorRamp )
700 {
702 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
703 return false;
704 }
705
706 return true;
707}
708
710{
711 QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
712 if ( symbolsElem.isNull() )
713 return nullptr;
714
715 QDomElement catsElem = element.firstChildElement( QStringLiteral( "categories" ) );
716 if ( catsElem.isNull() )
717 return nullptr;
718
719 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
720 QgsCategoryList cats;
721
722 // Value from string (long, ulong, double and string)
723 const auto valueFromString = []( const QString & value, const QString & valueType ) -> QVariant
724 {
725 if ( valueType == QLatin1String( "double" ) )
726 {
727 bool ok;
728 const auto val { value.toDouble( &ok ) };
729 if ( ok )
730 {
731 return val;
732 }
733 }
734 else if ( valueType == QLatin1String( "ulong" ) )
735 {
736 bool ok;
737 const auto val { value.toULongLong( &ok ) };
738 if ( ok )
739 {
740 return val;
741 }
742 }
743 else if ( valueType == QLatin1String( "long" ) )
744 {
745 bool ok;
746 const auto val { value.toLongLong( &ok ) };
747 if ( ok )
748 {
749 return val;
750 }
751 }
752 else if ( valueType == QLatin1String( "bool" ) )
753 {
754 if ( value.toLower() == QLatin1String( "false" ) )
755 return false;
756 if ( value.toLower() == QLatin1String( "true" ) )
757 return true;
758 }
759 else if ( valueType == QLatin1String( "NULL" ) )
760 {
761 // This is the default ("fallback") category
762 return QVariant();
763 }
764 return value;
765 };
766
767 QDomElement catElem = catsElem.firstChildElement();
768 int i = 0;
769 while ( !catElem.isNull() )
770 {
771 if ( catElem.tagName() == QLatin1String( "category" ) )
772 {
773 QVariant value;
774 if ( catElem.hasAttribute( QStringLiteral( "value" ) ) )
775 {
776 value = valueFromString( catElem.attribute( QStringLiteral( "value" ) ), catElem.attribute( QStringLiteral( "type" ), QString() ) ) ;
777 }
778 else
779 {
780 QVariantList values;
781 QDomElement valElem = catElem.firstChildElement();
782 while ( !valElem.isNull() )
783 {
784 if ( valElem.tagName() == QLatin1String( "val" ) )
785 {
786 values << valueFromString( valElem.attribute( QStringLiteral( "value" ) ), valElem.attribute( QStringLiteral( "type" ), QString() ) );
787 }
788 valElem = valElem.nextSiblingElement();
789 }
790 if ( !values.isEmpty() )
791 value = values;
792 }
793 QString symbolName = catElem.attribute( QStringLiteral( "symbol" ) );
794 QString label = catElem.attribute( QStringLiteral( "label" ) );
795 bool render = catElem.attribute( QStringLiteral( "render" ) ) != QLatin1String( "false" );
796 QString uuid = catElem.attribute( QStringLiteral( "uuid" ), QString::number( i++ ) );
797 if ( symbolMap.contains( symbolName ) )
798 {
799 QgsSymbol *symbol = symbolMap.take( symbolName );
800 cats.append( QgsRendererCategory( value, symbol, label, render, uuid ) );
801 }
802 }
803 catElem = catElem.nextSiblingElement();
804 }
805
806 QString attrName = element.attribute( QStringLiteral( "attr" ) );
807
809
810 // delete symbols if there are any more
812
813 // try to load source symbol (optional)
814 QDomElement sourceSymbolElem = element.firstChildElement( QStringLiteral( "source-symbol" ) );
815 if ( !sourceSymbolElem.isNull() )
816 {
817 QgsSymbolMap sourceSymbolMap = QgsSymbolLayerUtils::loadSymbols( sourceSymbolElem, context );
818 if ( sourceSymbolMap.contains( QStringLiteral( "0" ) ) )
819 {
820 r->setSourceSymbol( sourceSymbolMap.take( QStringLiteral( "0" ) ) );
821 }
822 QgsSymbolLayerUtils::clearSymbolMap( sourceSymbolMap );
823 }
824
825 // try to load color ramp (optional)
826 QDomElement sourceColorRampElem = element.firstChildElement( QStringLiteral( "colorramp" ) );
827 if ( !sourceColorRampElem.isNull() && sourceColorRampElem.attribute( QStringLiteral( "name" ) ) == QLatin1String( "[source]" ) )
828 {
829 r->setSourceColorRamp( QgsSymbolLayerUtils::loadColorRamp( sourceColorRampElem ) );
830 }
831
832 QDomElement rotationElem = element.firstChildElement( QStringLiteral( "rotation" ) );
833 if ( !rotationElem.isNull() && !rotationElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
834 {
835 for ( const QgsRendererCategory &cat : r->mCategories )
836 {
837 convertSymbolRotation( cat.symbol(), rotationElem.attribute( QStringLiteral( "field" ) ) );
838 }
839 if ( r->mSourceSymbol )
840 {
841 convertSymbolRotation( r->mSourceSymbol.get(), rotationElem.attribute( QStringLiteral( "field" ) ) );
842 }
843 }
844
845 QDomElement sizeScaleElem = element.firstChildElement( QStringLiteral( "sizescale" ) );
846 if ( !sizeScaleElem.isNull() && !sizeScaleElem.attribute( QStringLiteral( "field" ) ).isEmpty() )
847 {
848 for ( const QgsRendererCategory &cat : r->mCategories )
849 {
850 convertSymbolSizeScale( cat.symbol(),
851 QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
852 sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
853 }
854 if ( r->mSourceSymbol && r->mSourceSymbol->type() == Qgis::SymbolType::Marker )
855 {
857 QgsSymbolLayerUtils::decodeScaleMethod( sizeScaleElem.attribute( QStringLiteral( "scalemethod" ) ) ),
858 sizeScaleElem.attribute( QStringLiteral( "field" ) ) );
859 }
860 }
861
862 QDomElement ddsLegendSizeElem = element.firstChildElement( QStringLiteral( "data-defined-size-legend" ) );
863 if ( !ddsLegendSizeElem.isNull() )
864 {
865 r->mDataDefinedSizeLegend.reset( QgsDataDefinedSizeLegend::readXml( ddsLegendSizeElem, context ) );
866 }
867
868 // TODO: symbol levels
869 return r;
870}
871
872QDomElement QgsCategorizedSymbolRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
873{
874 // clazy:skip
875 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
876 rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "categorizedSymbol" ) );
877 rendererElem.setAttribute( QStringLiteral( "attr" ), mAttrName );
878
879 // String for type
880 // We just need string, bool, and three numeric types: double, ulong and long for unsigned, signed and float/double
881 const auto stringForType = []( const QMetaType::Type type ) -> QString
882 {
883 if ( type == QMetaType::Type::QChar || type == QMetaType::Type::Int || type == QMetaType::Type::LongLong )
884 {
885 return QStringLiteral( "long" );
886 }
887 else if ( type == QMetaType::Type::UInt || type == QMetaType::Type::ULongLong )
888 {
889 return QStringLiteral( "ulong" );
890 }
891 else if ( type == QMetaType::Type::Double )
892 {
893 return QStringLiteral( "double" ) ;
894 }
895 else if ( type == QMetaType::Type::Bool )
896 {
897 return QStringLiteral( "bool" );
898 }
899 else // Default: string
900 {
901 return QStringLiteral( "string" );
902 }
903 };
904
905 // categories
906 if ( !mCategories.isEmpty() )
907 {
908 int i = 0;
910 QDomElement catsElem = doc.createElement( QStringLiteral( "categories" ) );
911 QgsCategoryList::const_iterator it = mCategories.constBegin();
912 for ( ; it != mCategories.constEnd(); ++it )
913 {
914 const QgsRendererCategory &cat = *it;
915 QString symbolName = QString::number( i );
916 symbols.insert( symbolName, cat.symbol() );
917
918 QDomElement catElem = doc.createElement( QStringLiteral( "category" ) );
919 if ( cat.value().userType() == QMetaType::Type::QVariantList )
920 {
921 const QVariantList list = cat.value().toList();
922 for ( const QVariant &v : list )
923 {
924 QDomElement valueElem = doc.createElement( QStringLiteral( "val" ) );
925 valueElem.setAttribute( QStringLiteral( "value" ), v.toString() );
926 valueElem.setAttribute( QStringLiteral( "type" ), stringForType( static_cast<QMetaType::Type>( v.userType() ) ) );
927 catElem.appendChild( valueElem );
928 }
929 }
930 else
931 {
932 if ( QgsVariantUtils::isNull( cat.value() ) )
933 {
934 // We need to save NULL value as specific kind, it is the default ("fallback") category
935 catElem.setAttribute( QStringLiteral( "value" ), "NULL" );
936 catElem.setAttribute( QStringLiteral( "type" ), "NULL" );
937 }
938 else
939 {
940 catElem.setAttribute( QStringLiteral( "value" ), cat.value().toString() );
941 catElem.setAttribute( QStringLiteral( "type" ), stringForType( static_cast<QMetaType::Type>( cat.value().userType() ) ) );
942 }
943 }
944 catElem.setAttribute( QStringLiteral( "symbol" ), symbolName );
945 catElem.setAttribute( QStringLiteral( "label" ), cat.label() );
946 catElem.setAttribute( QStringLiteral( "render" ), cat.renderState() ? "true" : "false" );
947 catElem.setAttribute( QStringLiteral( "uuid" ), cat.uuid() );
948 catsElem.appendChild( catElem );
949 i++;
950 }
951 rendererElem.appendChild( catsElem );
952
953 // save symbols
954 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
955 rendererElem.appendChild( symbolsElem );
956 }
957
958 // save source symbol
959 if ( mSourceSymbol )
960 {
961 QgsSymbolMap sourceSymbols;
962 sourceSymbols.insert( QStringLiteral( "0" ), mSourceSymbol.get() );
963 QDomElement sourceSymbolElem = QgsSymbolLayerUtils::saveSymbols( sourceSymbols, QStringLiteral( "source-symbol" ), doc, context );
964 rendererElem.appendChild( sourceSymbolElem );
965 }
966
967 // save source color ramp
968 if ( mSourceColorRamp )
969 {
970 QDomElement colorRampElem = QgsSymbolLayerUtils::saveColorRamp( QStringLiteral( "[source]" ), mSourceColorRamp.get(), doc );
971 rendererElem.appendChild( colorRampElem );
972 }
973
974 QDomElement rotationElem = doc.createElement( QStringLiteral( "rotation" ) );
975 rendererElem.appendChild( rotationElem );
976
977 QDomElement sizeScaleElem = doc.createElement( QStringLiteral( "sizescale" ) );
978 rendererElem.appendChild( sizeScaleElem );
979
981 {
982 QDomElement ddsLegendElem = doc.createElement( QStringLiteral( "data-defined-size-legend" ) );
983 mDataDefinedSizeLegend->writeXml( ddsLegendElem, context );
984 rendererElem.appendChild( ddsLegendElem );
985 }
986
987 saveRendererData( doc, rendererElem, context );
988
989 return rendererElem;
990}
991
992
993QgsLegendSymbolList QgsCategorizedSymbolRenderer::baseLegendSymbolItems() const
994{
996 for ( const QgsRendererCategory &cat : mCategories )
997 {
998 lst << QgsLegendSymbolItem( cat.symbol(), cat.label(), cat.uuid(), true );
999 }
1000 return lst;
1001}
1002
1004{
1005
1006 auto _displayString = [ ]( const QVariant & v, int precision ) -> QString
1007 {
1008
1009 if ( QgsVariantUtils::isNull( v ) )
1010 {
1012 }
1013
1014 const bool isNumeric {v.userType() == QMetaType::Type::Double || v.userType() == QMetaType::Type::Int || v.userType() == QMetaType::Type::UInt || v.userType() == QMetaType::Type::LongLong || v.userType() == QMetaType::Type::ULongLong};
1015
1016 // Special treatment for numeric types if group separator is set or decimalPoint is not a dot
1017 if ( v.userType() == QMetaType::Type::Double )
1018 {
1019 // if value doesn't contain a double (a default value expression for instance),
1020 // apply no transformation
1021 bool ok;
1022 v.toDouble( &ok );
1023 if ( !ok )
1024 return v.toString();
1025
1026 // Locales with decimal point != '.' or that require group separator: use QLocale
1027 if ( QLocale().decimalPoint() != '.' ||
1028 !( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
1029 {
1030 if ( precision > 0 )
1031 {
1032 if ( -1 < v.toDouble() && v.toDouble() < 1 )
1033 {
1034 return QLocale().toString( v.toDouble(), 'g', precision );
1035 }
1036 else
1037 {
1038 return QLocale().toString( v.toDouble(), 'f', precision );
1039 }
1040 }
1041 else
1042 {
1043 // Precision is not set, let's guess it from the
1044 // standard conversion to string
1045 const QString s( v.toString() );
1046 const int dotPosition( s.indexOf( '.' ) );
1047 int precision;
1048 if ( dotPosition < 0 && s.indexOf( 'e' ) < 0 )
1049 {
1050 precision = 0;
1051 return QLocale().toString( v.toDouble(), 'f', precision );
1052 }
1053 else
1054 {
1055 if ( dotPosition < 0 ) precision = 0;
1056 else precision = s.length() - dotPosition - 1;
1057
1058 if ( -1 < v.toDouble() && v.toDouble() < 1 )
1059 {
1060 return QLocale().toString( v.toDouble(), 'g', precision );
1061 }
1062 else
1063 {
1064 return QLocale().toString( v.toDouble(), 'f', precision );
1065 }
1066 }
1067 }
1068 }
1069 // Default for doubles with precision
1070 else if ( precision > 0 )
1071 {
1072 if ( -1 < v.toDouble() && v.toDouble() < 1 )
1073 {
1074 return QString::number( v.toDouble(), 'g', precision );
1075 }
1076 else
1077 {
1078 return QString::number( v.toDouble(), 'f', precision );
1079 }
1080 }
1081 }
1082 // Other numeric types than doubles
1083 else if ( isNumeric &&
1084 !( QLocale().numberOptions() & QLocale::NumberOption::OmitGroupSeparator ) )
1085 {
1086 bool ok;
1087 const qlonglong converted( v.toLongLong( &ok ) );
1088 if ( ok )
1089 return QLocale().toString( converted );
1090 }
1091 else if ( v.userType() == QMetaType::Type::QByteArray )
1092 {
1093 return QObject::tr( "BLOB" );
1094 }
1095
1096 // Fallback if special rules do not apply
1097 return v.toString();
1098 };
1099
1100 if ( v.userType() == QMetaType::Type::QStringList || v.userType() == QMetaType::Type::QVariantList )
1101 {
1102 // Note that this code is never hit because the joining of lists (merged categories) happens
1103 // in data(); I'm leaving this here anyway because it is tested and it may be useful for
1104 // other purposes in the future.
1105 QString result;
1106 const QVariantList list = v.toList();
1107 for ( const QVariant &var : list )
1108 {
1109 if ( !result.isEmpty() )
1110 {
1111 result.append( ';' );
1112 }
1113 result.append( _displayString( var, precision ) );
1114 }
1115 return result;
1116 }
1117 else
1118 {
1119 return _displayString( v, precision );
1120 }
1121}
1122
1124{
1126 {
1127 // check that all symbols that have the same size expression
1128 QgsProperty ddSize;
1129 for ( const QgsRendererCategory &category : mCategories )
1130 {
1131 const QgsMarkerSymbol *symbol = static_cast<const QgsMarkerSymbol *>( category.symbol() );
1132 if ( ddSize )
1133 {
1134 QgsProperty sSize( symbol->dataDefinedSize() );
1135 if ( sSize != ddSize )
1136 {
1137 // no common size expression
1138 return baseLegendSymbolItems();
1139 }
1140 }
1141 else
1142 {
1143 ddSize = symbol->dataDefinedSize();
1144 }
1145 }
1146
1147 if ( ddSize && ddSize.isActive() )
1148 {
1150
1152 ddSizeLegend.updateFromSymbolAndProperty( static_cast<const QgsMarkerSymbol *>( mSourceSymbol.get() ), ddSize );
1153 lst += ddSizeLegend.legendSymbolList();
1154
1155 lst += baseLegendSymbolItems();
1156 return lst;
1157 }
1158 }
1159
1160 return baseLegendSymbolItems();
1161}
1162
1164{
1165 const QVariant value = valueForFeature( feature, context );
1166
1167 for ( const QgsRendererCategory &cat : mCategories )
1168 {
1169 bool match = false;
1170 if ( cat.value().userType() == QMetaType::Type::QVariantList )
1171 {
1172 const QVariantList list = cat.value().toList();
1173 for ( const QVariant &v : list )
1174 {
1175 if ( value == v )
1176 {
1177 match = true;
1178 break;
1179 }
1180 }
1181 }
1182 else
1183 {
1184 // Numeric NULL cat value is stored as an empty string
1185 if ( QgsVariantUtils::isNull( value ) && ( value.userType() == QMetaType::Type::Double || value.userType() == QMetaType::Type::Int ||
1186 value.userType() == QMetaType::Type::UInt || value.userType() == QMetaType::Type::LongLong ||
1187 value.userType() == QMetaType::Type::ULongLong || value.userType() == QMetaType::Type::Bool ) )
1188 {
1189 match = cat.value().toString().isEmpty();
1190 }
1191 else
1192 {
1193 match = value == cat.value();
1194 }
1195 }
1196
1197 if ( match )
1198 {
1199 if ( cat.renderState() || mCounting )
1200 return QSet< QString >() << cat.uuid();
1201 else
1202 return QSet< QString >();
1203 }
1204 }
1205
1206 return QSet< QString >();
1207}
1208
1209QString QgsCategorizedSymbolRenderer::legendKeyToExpression( const QString &key, QgsVectorLayer *layer, bool &ok ) const
1210{
1211 ok = false;
1212 int i = 0;
1213 for ( i = 0; i < mCategories.size(); i++ )
1214 {
1215 if ( mCategories[i].uuid() == key )
1216 {
1217 ok = true;
1218 break;
1219 }
1220 }
1221
1222 if ( !ok )
1223 {
1224 ok = false;
1225 return QString();
1226 }
1227
1228 const int fieldIndex = layer ? layer->fields().lookupField( mAttrName ) : -1;
1229 const bool isNumeric = layer && fieldIndex >= 0 ? layer->fields().at( fieldIndex ).isNumeric() : false;
1230 const QMetaType::Type fieldType = layer && fieldIndex >= 0 ? layer->fields().at( fieldIndex ).type() : QMetaType::Type::UnknownType;
1231 const QString attributeComponent = QgsExpression::quoteFieldExpression( mAttrName, layer );
1232
1233 ok = true;
1234 const QgsRendererCategory &cat = mCategories[i];
1235 if ( cat.value().userType() == QMetaType::Type::QVariantList )
1236 {
1237 const QVariantList list = cat.value().toList();
1238 QStringList parts;
1239 parts.reserve( list.size() );
1240 for ( const QVariant &v : list )
1241 {
1242 parts.append( QgsExpression::quotedValue( v ) );
1243 }
1244
1245 return QStringLiteral( "%1 IN (%2)" ).arg( attributeComponent, parts.join( QLatin1String( ", " ) ) );
1246 }
1247 else
1248 {
1249 // Numeric NULL cat value is stored as an empty string
1250 QVariant value = cat.value();
1251 if ( isNumeric && value.toString().isEmpty() )
1252 {
1253 value = QVariant();
1254 }
1255
1256 if ( QgsVariantUtils::isNull( value ) )
1257 return QStringLiteral( "%1 IS NULL" ).arg( attributeComponent );
1258 else if ( fieldType == QMetaType::Type::UnknownType )
1259 return QStringLiteral( "%1 = %2" ).arg( attributeComponent, QgsExpression::quotedValue( value ) );
1260 else
1261 return QStringLiteral( "%1 = %2" ).arg( attributeComponent, QgsExpression::quotedValue( value, fieldType ) );
1262 }
1263}
1264
1269
1271{
1272 return mSourceSymbol.get();
1273}
1274
1279
1284
1289
1294
1296{
1297 setSourceColorRamp( ramp );
1298 double num = mCategories.count() - 1;
1299 double count = 0;
1300
1301 QgsRandomColorRamp *randomRamp = dynamic_cast<QgsRandomColorRamp *>( ramp );
1302 if ( randomRamp )
1303 {
1304 //ramp is a random colors ramp, so inform it of the total number of required colors
1305 //this allows the ramp to pregenerate a set of visually distinctive colors
1306 randomRamp->setTotalColorCount( mCategories.count() );
1307 }
1308
1309 for ( const QgsRendererCategory &cat : mCategories )
1310 {
1311 double value = count / num;
1312 cat.symbol()->setColor( mSourceColorRamp->color( value ) );
1313 count += 1;
1314 }
1315}
1316
1318{
1319 int i = 0;
1320 for ( const QgsRendererCategory &cat : mCategories )
1321 {
1322 QgsSymbol *symbol = sym->clone();
1323 symbol->setColor( cat.symbol()->color() );
1324 updateCategorySymbol( i, symbol );
1325 ++i;
1326 }
1327 setSourceSymbol( sym->clone() );
1328}
1329
1331{
1332 return true;
1333}
1334
1336{
1337 for ( const QgsRendererCategory &category : std::as_const( mCategories ) )
1338 {
1339 if ( category.uuid() == key )
1340 {
1341 return category.renderState();
1342 }
1343 }
1344
1345 return true;
1346}
1347
1349{
1350 bool ok = false;
1351 int i = 0;
1352 for ( i = 0; i < mCategories.size(); i++ )
1353 {
1354 if ( mCategories[i].uuid() == key )
1355 {
1356 ok = true;
1357 break;
1358 }
1359 }
1360
1361 if ( ok )
1362 updateCategorySymbol( i, symbol );
1363 else
1364 delete symbol;
1365}
1366
1367void QgsCategorizedSymbolRenderer::checkLegendSymbolItem( const QString &key, bool state )
1368{
1369 for ( int i = 0; i < mCategories.size(); i++ )
1370 {
1371 if ( mCategories[i].uuid() == key )
1372 {
1373 updateCategoryRenderState( i, state );
1374 break;
1375 }
1376 }
1377}
1378
1380{
1381 std::unique_ptr< QgsCategorizedSymbolRenderer > r;
1382 if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
1383 {
1384 r.reset( static_cast<QgsCategorizedSymbolRenderer *>( renderer->clone() ) );
1385 }
1386 else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1387 {
1388 const QgsGraduatedSymbolRenderer *graduatedSymbolRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1389 if ( graduatedSymbolRenderer )
1390 {
1391 r.reset( new QgsCategorizedSymbolRenderer( QString(), QgsCategoryList() ) );
1392 if ( graduatedSymbolRenderer->sourceSymbol() )
1393 r->setSourceSymbol( graduatedSymbolRenderer->sourceSymbol()->clone() );
1394 if ( graduatedSymbolRenderer->sourceColorRamp() )
1395 {
1396 r->setSourceColorRamp( graduatedSymbolRenderer->sourceColorRamp()->clone() );
1397 }
1398 r->setClassAttribute( graduatedSymbolRenderer->classAttribute() );
1399 }
1400 }
1401 else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1402 {
1403 const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer );
1404 if ( pointDistanceRenderer )
1405 r.reset( convertFromRenderer( pointDistanceRenderer->embeddedRenderer() ) );
1406 }
1407 else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1408 {
1409 const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer );
1410 if ( invertedPolygonRenderer )
1411 r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1412 }
1413 else if ( renderer->type() == QLatin1String( "embeddedSymbol" ) && layer )
1414 {
1415 const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
1419 req.setNoAttributes();
1420 QgsFeatureIterator it = layer->getFeatures( req );
1421 QgsFeature feature;
1422 while ( it.nextFeature( feature ) && categories.size() < 2000 )
1423 {
1424 if ( feature.embeddedSymbol() )
1425 categories.append( QgsRendererCategory( feature.id(), feature.embeddedSymbol()->clone(), QString::number( feature.id() ) ) );
1426 }
1427 categories.append( QgsRendererCategory( QVariant(), embeddedRenderer->defaultSymbol()->clone(), QString() ) );
1428 r.reset( new QgsCategorizedSymbolRenderer( QStringLiteral( "$id" ), categories ) );
1429 }
1430
1431 // If not one of the specifically handled renderers, then just grab the symbol from the renderer
1432 // Could have applied this to specific renderer types (singleSymbol, graduatedSymbol)
1433
1434 if ( !r )
1435 {
1436 r = std::make_unique< QgsCategorizedSymbolRenderer >( QString(), QgsCategoryList() );
1437 QgsRenderContext context;
1438 QgsSymbolList symbols = const_cast<QgsFeatureRenderer *>( renderer )->symbols( context );
1439 if ( !symbols.isEmpty() )
1440 {
1441 r->setSourceSymbol( symbols.at( 0 )->clone() );
1442 }
1443 }
1444
1445 renderer->copyRendererData( r.get() );
1446
1447 return r.release();
1448}
1449
1454
1459
1460int QgsCategorizedSymbolRenderer::matchToSymbols( QgsStyle *style, Qgis::SymbolType type, QVariantList &unmatchedCategories, QStringList &unmatchedSymbols, const bool caseSensitive, const bool useTolerantMatch )
1461{
1462 if ( !style )
1463 return 0;
1464
1465 int matched = 0;
1466 unmatchedSymbols = style->symbolNames();
1467 const QSet< QString > allSymbolNames( unmatchedSymbols.begin(), unmatchedSymbols.end() );
1468
1469 const thread_local QRegularExpression tolerantMatchRe( QStringLiteral( "[^\\w\\d ]" ), QRegularExpression::UseUnicodePropertiesOption );
1470
1471 for ( int catIdx = 0; catIdx < mCategories.count(); ++catIdx )
1472 {
1473 const QVariant value = mCategories.at( catIdx ).value();
1474 const QString val = value.toString().trimmed();
1475 std::unique_ptr< QgsSymbol > symbol( style->symbol( val ) );
1476 // case-sensitive match
1477 if ( symbol && symbol->type() == type )
1478 {
1479 matched++;
1480 unmatchedSymbols.removeAll( val );
1481 updateCategorySymbol( catIdx, symbol.release() );
1482 continue;
1483 }
1484
1485 if ( !caseSensitive || useTolerantMatch )
1486 {
1487 QString testVal = val;
1488 if ( useTolerantMatch )
1489 testVal.replace( tolerantMatchRe, QString() );
1490
1491 bool foundMatch = false;
1492 for ( const QString &name : allSymbolNames )
1493 {
1494 QString testName = name.trimmed();
1495 if ( useTolerantMatch )
1496 testName.replace( tolerantMatchRe, QString() );
1497
1498 if ( testName == testVal || ( !caseSensitive && testName.trimmed().compare( testVal, Qt::CaseInsensitive ) == 0 ) )
1499 {
1500 // found a case-insensitive match
1501 std::unique_ptr< QgsSymbol > symbol( style->symbol( name ) );
1502 if ( symbol && symbol->type() == type )
1503 {
1504 matched++;
1505 unmatchedSymbols.removeAll( name );
1506 updateCategorySymbol( catIdx, symbol.release() );
1507 foundMatch = true;
1508 break;
1509 }
1510 }
1511 }
1512 if ( foundMatch )
1513 continue;
1514 }
1515
1516 unmatchedCategories << value;
1517 }
1518
1519 return matched;
1520}
1521
1522QgsCategoryList QgsCategorizedSymbolRenderer::createCategories( const QList<QVariant> &values, const QgsSymbol *symbol, QgsVectorLayer *layer, const QString &attributeName )
1523{
1524 QgsCategoryList cats;
1525 QVariantList vals = values;
1526 // sort the categories first
1527 QgsSymbolLayerUtils::sortVariantList( vals, Qt::AscendingOrder );
1528
1529 if ( layer && !attributeName.isNull() )
1530 {
1531 const QgsFields fields = layer->fields();
1532 for ( const QVariant &value : vals )
1533 {
1534 QgsSymbol *newSymbol = symbol->clone();
1535 if ( !QgsVariantUtils::isNull( value ) )
1536 {
1537 const int fieldIdx = fields.lookupField( attributeName );
1538 QString categoryName = displayString( value );
1539 if ( fieldIdx != -1 )
1540 {
1541 const QgsField field = fields.at( fieldIdx );
1542 const QgsEditorWidgetSetup setup = field.editorWidgetSetup();
1544 categoryName = formatter->representValue( layer, fieldIdx, setup.config(), QVariant(), value );
1545 }
1546 cats.append( QgsRendererCategory( value, newSymbol, categoryName, true ) );
1547 }
1548 }
1549 }
1550
1551 // add null (default) value
1552 QgsSymbol *newSymbol = symbol->clone();
1553 cats.append( QgsRendererCategory( QVariant(), newSymbol, QString(), true ) );
1554
1555 return cats;
1556}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ EmbeddedSymbols
Retrieve any embedded feature symbology.
QFlags< FeatureRendererFlag > FeatureRendererFlags
Flags controlling behavior of vector feature renderers.
Definition qgis.h:772
@ AffectsLabeling
If present, indicates that the renderer will participate in the map labeling problem.
SymbolType
Symbol types.
Definition qgis.h:574
@ Marker
Marker symbol.
@ AffectsLabeling
If present, indicates that the symbol will participate in the map labeling problem.
static QString nullRepresentation()
Returns the string used to represent the value NULL throughout QGIS.
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.
void toSld(QDomDocument &doc, QDomElement &element, const QVariantMap &props=QVariantMap()) const override
used from subclasses to create SLD Rule elements following SLD v1.1 specs
QgsCategorizedSymbolRenderer * clone() const override
Create a deep copy of this renderer.
void deleteAllCategories()
Deletes all existing categories from the renderer.
void setDataDefinedSizeLegend(QgsDataDefinedSizeLegend *settings)
Configures appearance of legend when renderer is configured to use data-defined size for marker symbo...
bool updateCategoryLabel(int catIndex, const QString &label)
Changes the label for the category with the specified index.
static QString displayString(const QVariant &value, int precision=-1)
Returns a localized representation of value with the given precision, if precision is -1 then precisi...
~QgsCategorizedSymbolRenderer() override
int categoryIndexForLabel(const QString &val)
Returns the index of the category with the specified label (or -1 if the label was not found,...
Abstract base class for color ramps.
virtual QgsColorRamp * clone() const =0
Creates a clone of the color ramp.
Object that keeps configuration of appearance of marker symbol's data-defined size in legend.
static QgsDataDefinedSizeLegend * readXml(const QDomElement &elem, const QgsReadWriteContext &context) SIP_FACTORY
Creates instance from given element and returns it (caller takes ownership). Returns nullptr on error...
void updateFromSymbolAndProperty(const QgsMarkerSymbol *symbol, const QgsProperty &ddSize)
Updates the list of classes, source symbol and title label from given symbol and property.
QgsLegendSymbolList legendSymbolList() const
Generates legend symbol items according to the configuration.
Holder for the widget type and its configuration for a field.
QVariantMap config() const
A vector feature renderer which uses embedded feature symbology to render per-feature symbols.
static QList< QgsExpressionContextScope * > globalProjectLayerScopes(const QgsMapLayer *layer)
Creates a list of three scopes: global, layer's project and layer.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScopes(const QList< QgsExpressionContextScope * > &scopes)
Appends a list of scopes to the end of the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
bool prepare(const QgsExpressionContext *context)
Gets the expression ready for evaluation - find out column indexes.
static QString quotedValue(const QVariant &value)
Returns a string representation of a literal value, including appropriate quotations where required.
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.
Abstract base class for all 2D vector feature renderers.
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)
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)
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
This class 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:58
QgsAttributes attributes
Definition qgsfeature.h:67
QgsFeatureId id
Definition qgsfeature.h:66
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.
Encapsulate a field in an attribute table or data source.
Definition qgsfield.h:53
QMetaType::Type type
Definition qgsfield.h:60
bool isNumeric
Definition qgsfield.h:56
QgsEditorWidgetSetup editorWidgetSetup() const
Gets the editor widget setup for the field.
Definition qgsfield.cpp:739
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.
void setSourceSymbol(QgsSymbol *sym)
Sets the source symbol for the renderer, which is the base symbol used for the each classes' symbol b...
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted,...
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
A marker symbol type, for rendering Point and MultiPoint geometries.
QgsProperty dataDefinedSize() const
Returns data defined size for whole symbol (including all symbol layers).
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
const QgsFeatureRenderer * embeddedRenderer() const override
Returns the current embedded renderer (subrenderer) for this feature renderer.
A store for object properties.
bool isActive() const
Returns whether the property is currently active.
Totally random color ramp.
virtual void setTotalColorCount(int colorCount)
Sets the desired total number of unique colors for the resultant ramp.
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
double rendererScale() const
Returns the renderer map scale.
QgsExpressionContext & expressionContext()
Gets the expression context.
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
void setRenderState(bool render)
Sets whether the category is currently enabled and should be rendered.
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...
void toSld(QDomDocument &doc, QDomElement &element, QVariantMap props) const
Converts the category to a matching SLD rule, within the specified DOM document and element.
void setValue(const QVariant &value)
Sets the value corresponding to this category.
QVariant value() const
Returns the value corresponding to this category.
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
QgsRendererCategory & operator=(QgsRendererCategory cat)
A color ramp entity for QgsStyle databases.
Definition qgsstyle.h:1428
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:1396
QgsSymbol * symbol(const QString &name)
Returns a NEW copy of symbol.
Definition qgsstyle.cpp:317
QStringList symbolNames() const
Returns a list of names of symbols.
Definition qgsstyle.cpp:339
static void sortVariantList(QList< QVariant > &list, Qt::SortOrder order)
Sorts the passed list in requested order.
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, QVariantMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
static QgsColorRamp * loadColorRamp(QDomElement &element)
Creates a color ramp from the settings encoded in an XML element.
static Qgis::ScaleMethod decodeScaleMethod(const QString &str)
Decodes a symbol scale method from a string.
static QDomElement saveColorRamp(const QString &name, QgsColorRamp *ramp, QDomDocument &doc)
Encodes a color ramp's settings to an XML element.
static void clearSymbolMap(QgsSymbolMap &symbols)
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
Abstract base class for all rendered symbols.
Definition qgssymbol.h: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 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 data sets.
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:120
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:188
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:39
#define QgsDebugError(str)
Definition qgslogger.h:38
#define RENDERER_TAG_NAME
Definition qgsrenderer.h:53
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition qgsrenderer.h:48
QList< QgsSymbol * > QgsSymbolList
Definition qgsrenderer.h:47
int precision
Contains information relating to the style entity currently being visited.