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