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