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