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