QGIS API Documentation 3.30.0-'s-Hertogenbosch (f186b8efe0)
qgsrulebasedrenderer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrulebasedrenderer.cpp - Rule-based renderer (symbology)
3 ---------------------
4 begin : May 2010
5 copyright : (C) 2010 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
17#include "qgssymbollayer.h"
18#include "qgsexpression.h"
19#include "qgssymbollayerutils.h"
20#include "qgsrendercontext.h"
21#include "qgsvectorlayer.h"
22#include "qgslogger.h"
23#include "qgsogcutils.h"
27#include "qgspainteffect.h"
29#include "qgsproperty.h"
32#include "qgslinesymbol.h"
33#include "qgsfillsymbol.h"
34#include "qgsmarkersymbol.h"
35
36#include <QSet>
37
38#include <QDomDocument>
39#include <QDomElement>
40#include <QUuid>
41
42
43QgsRuleBasedRenderer::Rule::Rule( QgsSymbol *symbol, int scaleMinDenom, int scaleMaxDenom, const QString &filterExp, const QString &label, const QString &description, bool elseRule )
44 : mParent( nullptr )
45 , mSymbol( symbol )
46 , mMaximumScale( scaleMinDenom )
47 , mMinimumScale( scaleMaxDenom )
48 , mFilterExp( filterExp )
49 , mLabel( label )
50 , mDescription( description )
51 , mElseRule( elseRule )
52{
53 if ( mElseRule )
54 mFilterExp = QStringLiteral( "ELSE" );
55
56 mRuleKey = QUuid::createUuid().toString();
57 initFilter();
58}
59
61{
62 qDeleteAll( mChildren );
63 // do NOT delete parent
64}
65
67{
68 if ( mFilterExp.trimmed().compare( QLatin1String( "ELSE" ), Qt::CaseInsensitive ) == 0 )
69 {
70 mElseRule = true;
71 mFilter.reset();
72 }
73 else if ( mFilterExp.trimmed().isEmpty() )
74 {
75 mElseRule = false;
76 mFilter.reset();
77 }
78 else
79 {
80 mElseRule = false;
81 mFilter = std::make_unique< QgsExpression >( mFilterExp );
82 }
83}
84
86{
87 mChildren.append( rule );
88 rule->mParent = this;
89 updateElseRules();
90}
91
93{
94 mChildren.insert( i, rule );
95 rule->mParent = this;
96 updateElseRules();
97}
98
100{
101 mChildren.removeAll( rule );
102 delete rule;
103 updateElseRules();
104}
105
107{
108 delete mChildren.takeAt( i );
109 updateElseRules();
110}
111
113{
114 mChildren.removeAll( rule );
115 rule->mParent = nullptr;
116 updateElseRules();
117 return rule;
118}
119
121{
122 Rule *rule = mChildren.takeAt( i );
123 rule->mParent = nullptr;
124 updateElseRules();
125 return rule;
126}
127
129{
130 // we could use a hash / map for search if this will be slow...
131
132 if ( key == mRuleKey )
133 return this;
134
135 const auto constMChildren = mChildren;
136 for ( Rule *rule : constMChildren )
137 {
138 Rule *r = rule->findRuleByKey( key );
139 if ( r )
140 return r;
141 }
142 return nullptr;
143}
144
145void QgsRuleBasedRenderer::Rule::updateElseRules()
146{
147 mElseRules.clear();
148 const auto constMChildren = mChildren;
149 for ( Rule *rule : constMChildren )
150 {
151 if ( rule->isElse() )
152 mElseRules << rule;
153 }
154}
155
157{
158 mFilterExp = QStringLiteral( "ELSE" );
159 mElseRule = iselse;
160 mFilter.reset();
161}
162
164{
165 // NOTE: if visitEnter returns false it means "don't visit the rule", not "abort all further visitations"
167 return true;
168
169 if ( mSymbol )
170 {
171 QgsStyleSymbolEntity entity( mSymbol.get() );
172 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
173 return false;
174 }
175
176 if ( !mChildren.empty() )
177 {
178 for ( const Rule *rule : mChildren )
179 {
180
181 if ( !rule->accept( visitor ) )
182 return false;
183 }
184 }
185
187 return false;
188
189 return true;
190}
191
192QString QgsRuleBasedRenderer::Rule::dump( int indent ) const
193{
194 QString off;
195 off.fill( QChar( ' ' ), indent );
196 QString symbolDump = ( mSymbol ? mSymbol->dump() : QStringLiteral( "[]" ) );
197 QString msg = off + QStringLiteral( "RULE %1 - scale [%2,%3] - filter %4 - symbol %5\n" )
198 .arg( mLabel ).arg( mMaximumScale ).arg( mMinimumScale )
199 .arg( mFilterExp, symbolDump );
200
201 QStringList lst;
202 const auto constMChildren = mChildren;
203 for ( Rule *rule : constMChildren )
204 {
205 lst.append( rule->dump( indent + 2 ) );
206 }
207 msg += lst.join( QLatin1Char( '\n' ) );
208 return msg;
209}
210
212{
213 // attributes needed by this rule
214 QSet<QString> attrs;
215 if ( mFilter )
216 attrs.unite( mFilter->referencedColumns() );
217 if ( mSymbol )
218 attrs.unite( mSymbol->usedAttributes( context ) );
219
220 // attributes needed by child rules
221 const auto constMChildren = mChildren;
222 for ( Rule *rule : constMChildren )
223 {
224 attrs.unite( rule->usedAttributes( context ) );
225 }
226 return attrs;
227}
228
230{
231 if ( mFilter && mFilter->needsGeometry() )
232 return true;
233
234 const auto constMChildren = mChildren;
235 for ( Rule *rule : constMChildren )
236 {
237 if ( rule->needsGeometry() )
238 return true;
239 }
240
241 return false;
242}
243
245{
246 QgsSymbolList lst;
247 if ( mSymbol )
248 lst.append( mSymbol.get() );
249
250 const auto constMChildren = mChildren;
251 for ( Rule *rule : constMChildren )
252 {
253 lst += rule->symbols( context );
254 }
255 return lst;
256}
257
259{
260 mSymbol.reset( sym );
261}
262
264{
265 mFilterExp = filterExp;
266 initFilter();
267}
268
270{
272 if ( currentLevel != -1 ) // root rule should not be shown
273 {
274 lst << QgsLegendSymbolItem( mSymbol.get(), mLabel, mRuleKey, true, mMaximumScale, mMinimumScale, currentLevel, mParent ? mParent->mRuleKey : QString() );
275 }
276
277 for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
278 {
279 Rule *rule = *it;
280 lst << rule->legendSymbolItems( currentLevel + 1 );
281 }
282 return lst;
283}
284
285
287{
288 if ( ! mFilter || mElseRule || ! context )
289 return true;
290
291 context->expressionContext().setFeature( f );
292 QVariant res = mFilter->evaluate( &context->expressionContext() );
293 return res.toBool();
294}
295
297{
298 if ( qgsDoubleNear( scale, 0.0 ) ) // so that we can count features in classes without scale context
299 return true;
300 if ( qgsDoubleNear( mMaximumScale, 0.0 ) && qgsDoubleNear( mMinimumScale, 0.0 ) )
301 return true;
302 if ( !qgsDoubleNear( mMaximumScale, 0.0 ) && mMaximumScale > scale )
303 return false;
304 if ( !qgsDoubleNear( mMinimumScale, 0.0 ) && mMinimumScale < scale )
305 return false;
306 return true;
307}
308
310{
311 QgsSymbol *sym = mSymbol ? mSymbol->clone() : nullptr;
312 Rule *newrule = new Rule( sym, mMaximumScale, mMinimumScale, mFilterExp, mLabel, mDescription );
313 newrule->setActive( mIsActive );
314 // clone children
315 const auto constMChildren = mChildren;
316 for ( Rule *rule : constMChildren )
317 newrule->appendChild( rule->clone() );
318 return newrule;
319}
320
321QDomElement QgsRuleBasedRenderer::Rule::save( QDomDocument &doc, QgsSymbolMap &symbolMap ) const
322{
323 QDomElement ruleElem = doc.createElement( QStringLiteral( "rule" ) );
324
325 if ( mSymbol )
326 {
327 int symbolIndex = symbolMap.size();
328 symbolMap[QString::number( symbolIndex )] = mSymbol.get();
329 ruleElem.setAttribute( QStringLiteral( "symbol" ), symbolIndex );
330 }
331 if ( !mFilterExp.isEmpty() )
332 ruleElem.setAttribute( QStringLiteral( "filter" ), mFilterExp );
333 if ( mMaximumScale != 0 )
334 ruleElem.setAttribute( QStringLiteral( "scalemindenom" ), mMaximumScale );
335 if ( mMinimumScale != 0 )
336 ruleElem.setAttribute( QStringLiteral( "scalemaxdenom" ), mMinimumScale );
337 if ( !mLabel.isEmpty() )
338 ruleElem.setAttribute( QStringLiteral( "label" ), mLabel );
339 if ( !mDescription.isEmpty() )
340 ruleElem.setAttribute( QStringLiteral( "description" ), mDescription );
341 if ( !mIsActive )
342 ruleElem.setAttribute( QStringLiteral( "checkstate" ), 0 );
343 ruleElem.setAttribute( QStringLiteral( "key" ), mRuleKey );
344
345 const auto constMChildren = mChildren;
346 for ( Rule *rule : constMChildren )
347 {
348 ruleElem.appendChild( rule->save( doc, symbolMap ) );
349 }
350 return ruleElem;
351}
352
353void QgsRuleBasedRenderer::Rule::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
354{
355 // do not convert this rule if there are no symbols
356 QgsRenderContext context;
357 if ( symbols( context ).isEmpty() )
358 return;
359
360 if ( !mFilterExp.isEmpty() )
361 {
362 QString filter = props.value( QStringLiteral( "filter" ), QString() ).toString();
363 if ( !filter.isEmpty() )
364 filter += QLatin1String( " AND " );
365 filter += mFilterExp;
366 props[ QStringLiteral( "filter" )] = filter;
367 }
368
369 QgsSymbolLayerUtils::mergeScaleDependencies( mMaximumScale, mMinimumScale, props );
370
371 if ( mSymbol )
372 {
373 QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
374 element.appendChild( ruleElem );
375
376 //XXX: <se:Name> is the rule identifier, but our the Rule objects
377 // have no properties could be used as identifier. Use the label.
378 QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
379 nameElem.appendChild( doc.createTextNode( mLabel ) );
380 ruleElem.appendChild( nameElem );
381
382 if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
383 {
384 QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
385 if ( !mLabel.isEmpty() )
386 {
387 QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
388 titleElem.appendChild( doc.createTextNode( mLabel ) );
389 descrElem.appendChild( titleElem );
390 }
391 if ( !mDescription.isEmpty() )
392 {
393 QDomElement abstractElem = doc.createElement( QStringLiteral( "se:Abstract" ) );
394 abstractElem.appendChild( doc.createTextNode( mDescription ) );
395 descrElem.appendChild( abstractElem );
396 }
397 ruleElem.appendChild( descrElem );
398 }
399
400 if ( !props.value( QStringLiteral( "filter" ), QString() ).toString().isEmpty() )
401 {
402 QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, props.value( QStringLiteral( "filter" ), QString() ).toString() );
403 }
404
405 QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
406
407 mSymbol->toSld( doc, ruleElem, props );
408 }
409
410 // loop into children rule list
411 const auto constMChildren = mChildren;
412 for ( Rule *rule : constMChildren )
413 {
414 rule->toSld( doc, element, props );
415 }
416}
417
419{
420 mActiveChildren.clear();
421
422 if ( ! mIsActive )
423 return false;
424
425 // filter out rules which are not compatible with this scale
426 if ( !isScaleOK( context.rendererScale() ) )
427 return false;
428
429 // init this rule
430 if ( mFilter )
431 mFilter->prepare( &context.expressionContext() );
432 if ( mSymbol )
433 mSymbol->startRender( context, fields );
434
435 // init children
436 // build temporary list of active rules (usable with this scale)
437 QStringList subfilters;
438 const auto constMChildren = mChildren;
439 for ( Rule *rule : constMChildren )
440 {
441 QString subfilter;
442 if ( rule->startRender( context, fields, subfilter ) )
443 {
444 // only add those which are active with current scale
445 mActiveChildren.append( rule );
446 subfilters.append( subfilter );
447 }
448 }
449
450 // subfilters (on the same level) are joined with OR
451 // Finally they are joined with their parent (this) with AND
452 QString sf;
453 // If there are subfilters present (and it's not a single empty one), group them and join them with OR
454 if ( subfilters.length() > 1 || !subfilters.value( 0 ).isEmpty() )
455 {
456 if ( subfilters.contains( QStringLiteral( "TRUE" ) ) )
457 {
458 sf = QStringLiteral( "TRUE" );
459 }
460 else
461 {
462 // test for a common case -- all subfilters can be combined into a single "field in (...)" expression
463 if ( QgsExpression::attemptReduceToInClause( subfilters, sf ) )
464 {
465 // success! we can use a simple "field IN (...)" list!
466 }
467 // If we have more than 50 rules (to stay on the safe side) make a binary tree or SQLITE will fail,
468 // see: https://github.com/qgis/QGIS/issues/27269
469 else if ( subfilters.count() > 50 )
470 {
471 std::function<QString( const QStringList & )>bt = [ &bt ]( const QStringList & subf )
472 {
473 if ( subf.count( ) == 1 )
474 {
475 return subf.at( 0 );
476 }
477 else if ( subf.count( ) == 2 )
478 {
479 return subf.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
480 }
481 else
482 {
483 int midpos = static_cast<int>( subf.length() / 2 );
484 return QStringLiteral( "(%1) OR (%2)" ).arg( bt( subf.mid( 0, midpos ) ), bt( subf.mid( midpos ) ) );
485 }
486 };
487 sf = bt( subfilters );
488 }
489 else
490 {
491 sf = subfilters.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
492 }
493 }
494 }
495
496 // Now join the subfilters with their parent (this) based on if
497 // * The parent is an else rule
498 // * The existence of parent filter and subfilters
499
500 // No filter expression: ELSE rule or catchall rule
501 if ( !mFilter )
502 {
503 if ( mSymbol || sf.isEmpty() )
504 filter = QStringLiteral( "TRUE" );
505 else
506 filter = sf;
507 }
508 else if ( mSymbol )
509 filter = mFilterExp;
510 else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
511 filter = QStringLiteral( "(%1) AND (%2)" ).arg( mFilterExp, sf );
512 else if ( !mFilterExp.trimmed().isEmpty() )
513 filter = mFilterExp;
514 else if ( sf.isEmpty() )
515 filter = QStringLiteral( "TRUE" );
516 else
517 filter = sf;
518
519 filter = filter.trimmed();
520
521 return true;
522}
523
525{
526 return !mActiveChildren.empty();
527}
528
530{
531 QSet<int> symbolZLevelsSet;
532
533 // process this rule
534 if ( mSymbol )
535 {
536 // find out which Z-levels are used
537 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
538 {
539 symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
540 }
541 }
542
543 // process children
544 QList<Rule *>::iterator it;
545 for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
546 {
547 Rule *rule = *it;
548 symbolZLevelsSet.unite( rule->collectZLevels() );
549 }
550 return symbolZLevelsSet;
551}
552
553void QgsRuleBasedRenderer::Rule::setNormZLevels( const QMap<int, int> &zLevelsToNormLevels )
554{
555 if ( mSymbol )
556 {
557 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
558 {
559 int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
560 mSymbolNormZLevels.insert( normLevel );
561 }
562 }
563
564 // prepare list of normalized levels for each rule
565 const auto constMActiveChildren = mActiveChildren;
566 for ( Rule *rule : constMActiveChildren )
567 {
568 rule->setNormZLevels( zLevelsToNormLevels );
569 }
570}
571
572
574{
575 if ( !isFilterOK( featToRender.feat, &context ) )
576 return Filtered;
577
578 bool rendered = false;
579
580 // create job for this feature and this symbol, add to list of jobs
581 if ( mSymbol && mIsActive )
582 {
583 // add job to the queue: each symbol's zLevel must be added
584 const auto constMSymbolNormZLevels = mSymbolNormZLevels;
585 for ( int normZLevel : constMSymbolNormZLevels )
586 {
587 //QgsDebugMsg(QString("add job at level %1").arg(normZLevel));
588 renderQueue[normZLevel].jobs.append( new RenderJob( featToRender, mSymbol.get() ) );
589 rendered = true;
590 }
591 }
592
593 bool matchedAChild = false;
594
595 // process children
596 const auto constMChildren = mChildren;
597 for ( Rule *rule : constMChildren )
598 {
599 // Don't process else rules yet
600 if ( !rule->isElse() )
601 {
602 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
603 // consider inactive items as "matched" so the else rule will ignore them
604 matchedAChild |= ( res == Rendered || res == Inactive );
605 rendered |= ( res == Rendered );
606 }
607 }
608
609 // If none of the rules passed then we jump into the else rules and process them.
610 if ( !matchedAChild )
611 {
612 const auto constMElseRules = mElseRules;
613 for ( Rule *rule : constMElseRules )
614 {
615 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
616 matchedAChild |= ( res == Rendered || res == Inactive );
617 rendered |= res == Rendered;
618 }
619 }
620 if ( !mIsActive || ( mSymbol && !rendered ) || ( matchedAChild && !rendered ) )
621 return Inactive;
622 else if ( rendered )
623 return Rendered;
624 else
625 return Filtered;
626}
627
629{
630 if ( !isFilterOK( feature, context ) )
631 return false;
632
633 if ( mSymbol )
634 return true;
635
636 const auto constMActiveChildren = mActiveChildren;
637 for ( Rule *rule : constMActiveChildren )
638 {
639 if ( rule->isElse() )
640 {
641 if ( rule->children().isEmpty() )
642 {
643 RuleList lst = rulesForFeature( feature, context, false );
644 lst.removeOne( rule );
645
646 if ( lst.empty() )
647 {
648 return true;
649 }
650 }
651 else
652 {
653 return rule->willRenderFeature( feature, context );
654 }
655 }
656 else if ( rule->willRenderFeature( feature, context ) )
657 {
658 return true;
659 }
660 }
661 return false;
662}
663
665{
666 QgsSymbolList lst;
667 if ( !isFilterOK( feature, context ) )
668 return lst;
669 if ( mSymbol )
670 lst.append( mSymbol.get() );
671
672 const auto constMActiveChildren = mActiveChildren;
673 for ( Rule *rule : constMActiveChildren )
674 {
675 lst += rule->symbolsForFeature( feature, context );
676 }
677 return lst;
678}
679
681{
682 QSet< QString> lst;
683 if ( !isFilterOK( feature, context ) )
684 return lst;
685 lst.insert( mRuleKey );
686
687 const auto constMActiveChildren = mActiveChildren;
688 for ( Rule *rule : constMActiveChildren )
689 {
690 bool validKey = false;
691 if ( rule->isElse() )
692 {
693 if ( rule->children().isEmpty() )
694 {
695 RuleList lst = rulesForFeature( feature, context, false );
696 lst.removeOne( rule );
697
698 if ( lst.empty() )
699 {
700 validKey = true;
701 }
702 }
703 else
704 {
705 validKey = true;
706 }
707 }
708 else if ( rule->willRenderFeature( feature, context ) )
709 {
710 validKey = true;
711 }
712
713 if ( validKey )
714 {
715 lst.unite( rule->legendKeysForFeature( feature, context ) );
716 }
717 }
718 return lst;
719}
720
722{
723 RuleList lst;
724 if ( ! isFilterOK( feature, context ) || ( context && ! isScaleOK( context->rendererScale() ) ) )
725 return lst;
726
727 if ( mSymbol )
728 lst.append( this );
729
730 RuleList listChildren = children();
731 if ( onlyActive )
732 listChildren = mActiveChildren;
733
734 const auto constListChildren = listChildren;
735 for ( Rule *rule : constListChildren )
736 {
737 lst += rule->rulesForFeature( feature, context, onlyActive );
738 }
739 return lst;
740}
741
743{
744 if ( mSymbol )
745 mSymbol->stopRender( context );
746
747 const auto constMActiveChildren = mActiveChildren;
748 for ( Rule *rule : constMActiveChildren )
749 {
750 rule->stopRender( context );
751 }
752
753 mActiveChildren.clear();
754 mSymbolNormZLevels.clear();
755}
756
757QgsRuleBasedRenderer::Rule *QgsRuleBasedRenderer::Rule::create( QDomElement &ruleElem, QgsSymbolMap &symbolMap, bool reuseId )
758{
759 QString symbolIdx = ruleElem.attribute( QStringLiteral( "symbol" ) );
760 QgsSymbol *symbol = nullptr;
761 if ( !symbolIdx.isEmpty() )
762 {
763 if ( symbolMap.contains( symbolIdx ) )
764 {
765 symbol = symbolMap.take( symbolIdx );
766 }
767 else
768 {
769 QgsDebugMsg( "symbol for rule " + symbolIdx + " not found!" );
770 }
771 }
772
773 QString filterExp = ruleElem.attribute( QStringLiteral( "filter" ) );
774 QString label = ruleElem.attribute( QStringLiteral( "label" ) );
775 QString description = ruleElem.attribute( QStringLiteral( "description" ) );
776 int scaleMinDenom = ruleElem.attribute( QStringLiteral( "scalemindenom" ), QStringLiteral( "0" ) ).toInt();
777 int scaleMaxDenom = ruleElem.attribute( QStringLiteral( "scalemaxdenom" ), QStringLiteral( "0" ) ).toInt();
778 QString ruleKey;
779 if ( reuseId )
780 ruleKey = ruleElem.attribute( QStringLiteral( "key" ) );
781 else
782 ruleKey = QUuid::createUuid().toString();
783 Rule *rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
784
785 if ( !ruleKey.isEmpty() )
786 rule->mRuleKey = ruleKey;
787
788 rule->setActive( ruleElem.attribute( QStringLiteral( "checkstate" ), QStringLiteral( "1" ) ).toInt() );
789
790 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
791 while ( !childRuleElem.isNull() )
792 {
793 Rule *childRule = create( childRuleElem, symbolMap );
794 if ( childRule )
795 {
796 rule->appendChild( childRule );
797 }
798 else
799 {
800 QgsDebugMsg( QStringLiteral( "failed to init a child rule!" ) );
801 }
802 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
803 }
804
805 return rule;
806}
807
809{
810 RuleList l;
811 for ( QgsRuleBasedRenderer::Rule *c : mChildren )
812 {
813 l += c;
814 l += c->descendants();
815 }
816 return l;
817}
818
820{
821 if ( ruleElem.localName() != QLatin1String( "Rule" ) )
822 {
823 QgsDebugMsg( QStringLiteral( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
824 return nullptr;
825 }
826
827 QString label, description, filterExp;
828 int scaleMinDenom = 0, scaleMaxDenom = 0;
829 QgsSymbolLayerList layers;
830
831 // retrieve the Rule element child nodes
832 QDomElement childElem = ruleElem.firstChildElement();
833 while ( !childElem.isNull() )
834 {
835 if ( childElem.localName() == QLatin1String( "Name" ) )
836 {
837 // <se:Name> tag contains the rule identifier,
838 // so prefer title tag for the label property value
839 if ( label.isEmpty() )
840 label = childElem.firstChild().nodeValue();
841 }
842 else if ( childElem.localName() == QLatin1String( "Description" ) )
843 {
844 // <se:Description> can contains a title and an abstract
845 QDomElement titleElem = childElem.firstChildElement( QStringLiteral( "Title" ) );
846 if ( !titleElem.isNull() )
847 {
848 label = titleElem.firstChild().nodeValue();
849 }
850
851 QDomElement abstractElem = childElem.firstChildElement( QStringLiteral( "Abstract" ) );
852 if ( !abstractElem.isNull() )
853 {
854 description = abstractElem.firstChild().nodeValue();
855 }
856 }
857 else if ( childElem.localName() == QLatin1String( "Abstract" ) )
858 {
859 // <sld:Abstract> (v1.0)
860 description = childElem.firstChild().nodeValue();
861 }
862 else if ( childElem.localName() == QLatin1String( "Title" ) )
863 {
864 // <sld:Title> (v1.0)
865 label = childElem.firstChild().nodeValue();
866 }
867 else if ( childElem.localName() == QLatin1String( "Filter" ) )
868 {
870 if ( filter )
871 {
872 if ( filter->hasParserError() )
873 {
874 QgsDebugMsg( "parser error: " + filter->parserErrorString() );
875 }
876 else
877 {
878 filterExp = filter->expression();
879 }
880 delete filter;
881 }
882 }
883 else if ( childElem.localName() == QLatin1String( "ElseFilter" ) )
884 {
885 filterExp = QLatin1String( "ELSE" );
886
887 }
888 else if ( childElem.localName() == QLatin1String( "MinScaleDenominator" ) )
889 {
890 bool ok;
891 int v = childElem.firstChild().nodeValue().toInt( &ok );
892 if ( ok )
893 scaleMinDenom = v;
894 }
895 else if ( childElem.localName() == QLatin1String( "MaxScaleDenominator" ) )
896 {
897 bool ok;
898 int v = childElem.firstChild().nodeValue().toInt( &ok );
899 if ( ok )
900 scaleMaxDenom = v;
901 }
902 else if ( childElem.localName().endsWith( QLatin1String( "Symbolizer" ) ) )
903 {
904 // create symbol layers for this symbolizer
905 QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
906 }
907
908 childElem = childElem.nextSiblingElement();
909 }
910
911 // now create the symbol
912 QgsSymbol *symbol = nullptr;
913 if ( !layers.isEmpty() )
914 {
915 switch ( geomType )
916 {
917 case Qgis::GeometryType::Line:
918 symbol = new QgsLineSymbol( layers );
919 break;
920
921 case Qgis::GeometryType::Polygon:
922 symbol = new QgsFillSymbol( layers );
923 break;
924
925 case Qgis::GeometryType::Point:
926 symbol = new QgsMarkerSymbol( layers );
927 break;
928
929 default:
930 QgsDebugMsg( QStringLiteral( "invalid geometry type: found %1" ).arg( qgsEnumValueToKey( geomType ) ) );
931 return nullptr;
932 }
933 }
934
935 // and then create and return the new rule
936 return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
937}
938
939
941
943 : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
944 , mRootRule( root )
945{
946}
947
949 : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
950{
951 mRootRule = new Rule( nullptr ); // root has no symbol, no filter etc - just a container
952 mRootRule->appendChild( new Rule( defaultSymbol ) );
953}
954
956{
957 delete mRootRule;
958}
959
960
962{
963 // not used at all
964 return nullptr;
965}
966
968 QgsRenderContext &context,
969 int layer,
970 bool selected,
971 bool drawVertexMarker )
972{
973 Q_UNUSED( layer )
974
975 int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
976 mCurrentFeatures.append( FeatureToRender( feature, flags ) );
977
978 // check each active rule
980}
981
982
984{
985 QgsFeatureRenderer::startRender( context, fields );
986
987 // prepare active children
988 mRootRule->startRender( context, fields, mFilter );
989
990 QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
991 QList<int> symbolZLevels( symbolZLevelsSet.begin(), symbolZLevelsSet.end() );
992 std::sort( symbolZLevels.begin(), symbolZLevels.end() );
993
994 // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
995 // and prepare rendering queue
996 QMap<int, int> zLevelsToNormLevels;
997 int maxNormLevel = -1;
998 const auto constSymbolZLevels = symbolZLevels;
999 for ( int zLevel : constSymbolZLevels )
1000 {
1001 zLevelsToNormLevels[zLevel] = ++maxNormLevel;
1002 mRenderQueue.append( RenderLevel( zLevel ) );
1003 QgsDebugMsgLevel( QStringLiteral( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ), 4 );
1004 }
1005
1006 mRootRule->setNormZLevels( zLevelsToNormLevels );
1007}
1008
1010{
1011 return !mRootRule->hasActiveChildren();
1012}
1013
1015{
1017
1018 //
1019 // do the actual rendering
1020 //
1021
1022 // go through all levels
1023 if ( !context.renderingStopped() )
1024 {
1025 const auto constMRenderQueue = mRenderQueue;
1026 for ( const RenderLevel &level : constMRenderQueue )
1027 {
1028 //QgsDebugMsg(QString("level %1").arg(level.zIndex));
1029 // go through all jobs at the level
1030 for ( const RenderJob *job : std::as_const( level.jobs ) )
1031 {
1032 context.expressionContext().setFeature( job->ftr.feat );
1033 //QgsDebugMsg(QString("job fid %1").arg(job->f->id()));
1034 // render feature - but only with symbol layers with specified zIndex
1035 QgsSymbol *s = job->symbol;
1036 int count = s->symbolLayerCount();
1037 for ( int i = 0; i < count; i++ )
1038 {
1039 // TODO: better solution for this
1040 // renderFeatureWithSymbol asks which symbol layer to draw
1041 // but there are multiple transforms going on!
1042 if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
1043 {
1044 int flags = job->ftr.flags;
1045 renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
1046 }
1047 }
1048 }
1049 }
1050 }
1051
1052 // clean current features
1053 mCurrentFeatures.clear();
1054
1055 // clean render queue
1056 mRenderQueue.clear();
1057
1058 // clean up rules from temporary stuff
1059 mRootRule->stopRender( context );
1060}
1061
1063{
1064 return mFilter;
1065}
1066
1067QSet<QString> QgsRuleBasedRenderer::usedAttributes( const QgsRenderContext &context ) const
1068{
1069 return mRootRule->usedAttributes( context );
1070}
1071
1073{
1074 return mRootRule->needsGeometry();
1075}
1076
1078{
1080
1081 // normally with clone() the individual rules get new keys (UUID), but here we want to keep
1082 // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
1083 clonedRoot->setRuleKey( mRootRule->ruleKey() );
1084 RuleList origDescendants = mRootRule->descendants();
1085 RuleList clonedDescendants = clonedRoot->descendants();
1086 Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
1087 for ( int i = 0; i < origDescendants.count(); ++i )
1088 clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
1089
1090 QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( clonedRoot );
1091
1092 copyRendererData( r );
1093 return r;
1094}
1095
1096void QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1097{
1098 mRootRule->toSld( doc, element, props );
1099}
1100
1101// TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
1103{
1104 return mRootRule->symbols( context );
1105}
1106
1107QDomElement QgsRuleBasedRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
1108{
1109 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1110 rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "RuleRenderer" ) );
1111
1113
1114 QDomElement rulesElem = mRootRule->save( doc, symbols );
1115 rulesElem.setTagName( QStringLiteral( "rules" ) ); // instead of just "rule"
1116 rendererElem.appendChild( rulesElem );
1117
1118 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
1119 rendererElem.appendChild( symbolsElem );
1120
1121 saveRendererData( doc, rendererElem, context );
1122
1123 return rendererElem;
1124}
1125
1127{
1128 return true;
1129}
1130
1132{
1133 Rule *rule = mRootRule->findRuleByKey( key );
1134 return rule ? rule->active() : true;
1135}
1136
1137void QgsRuleBasedRenderer::checkLegendSymbolItem( const QString &key, bool state )
1138{
1139 Rule *rule = mRootRule->findRuleByKey( key );
1140 if ( rule )
1141 rule->setActive( state );
1142}
1143
1144QString QgsRuleBasedRenderer::legendKeyToExpression( const QString &key, QgsVectorLayer *, bool &ok ) const
1145{
1146 ok = false;
1147 Rule *rule = mRootRule->findRuleByKey( key );
1148 if ( !rule )
1149 return QString();
1150
1151 std::function<QString( Rule *rule )> ruleToExpression;
1152 ruleToExpression = [&ruleToExpression]( Rule * rule ) -> QString
1153 {
1154 if ( rule->isElse() && rule->parent() )
1155 {
1156 // gather the expressions for all other rules on this level and invert them
1157
1158 QStringList otherRules;
1159 const QList<QgsRuleBasedRenderer::Rule *> siblings = rule->parent()->children();
1160 for ( Rule *sibling : siblings )
1161 {
1162 if ( sibling == rule || sibling->isElse() )
1163 continue;
1164
1165 const QString siblingExpression = ruleToExpression( sibling );
1166 if ( siblingExpression.isEmpty() )
1167 return QStringLiteral( "FALSE" ); // nothing will match this rule
1168
1169 otherRules.append( siblingExpression );
1170 }
1171
1172 if ( otherRules.empty() )
1173 return QStringLiteral( "TRUE" ); // all features will match the else rule
1174 else
1175 return (
1176 otherRules.size() > 1
1177 ? QStringLiteral( "NOT ((%1))" ).arg( otherRules.join( QLatin1String( ") OR (" ) ) )
1178 : QStringLiteral( "NOT (%1)" ).arg( otherRules.at( 0 ) )
1179 );
1180 }
1181 else
1182 {
1183 QStringList ruleParts;
1184 if ( !rule->filterExpression().isEmpty() )
1185 ruleParts.append( rule->filterExpression() );
1186
1187 if ( !qgsDoubleNear( rule->minimumScale(), 0.0 ) )
1188 ruleParts.append( QStringLiteral( "@map_scale <= %1" ).arg( rule->minimumScale() ) );
1189
1190 if ( !qgsDoubleNear( rule->maximumScale(), 0.0 ) )
1191 ruleParts.append( QStringLiteral( "@map_scale >= %1" ).arg( rule->maximumScale() ) );
1192
1193 if ( !ruleParts.empty() )
1194 {
1195 return (
1196 ruleParts.size() > 1
1197 ? QStringLiteral( "(%1)" ).arg( ruleParts.join( QLatin1String( ") AND (" ) ) )
1198 : ruleParts.at( 0 )
1199 );
1200 }
1201 else
1202 {
1203 return QString();
1204 }
1205 }
1206 };
1207
1208 QStringList parts;
1209 while ( rule )
1210 {
1211 const QString ruleFilter = ruleToExpression( rule );
1212 if ( !ruleFilter.isEmpty() )
1213 parts.append( ruleFilter );
1214
1215 rule = rule->parent();
1216 }
1217
1218 ok = true;
1219 return parts.empty() ? QStringLiteral( "TRUE" )
1220 : ( parts.size() > 1
1221 ? QStringLiteral( "(%1)" ).arg( parts.join( QLatin1String( ") AND (" ) ) )
1222 : parts.at( 0 ) );
1223}
1224
1225void QgsRuleBasedRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
1226{
1227 Rule *rule = mRootRule->findRuleByKey( key );
1228 if ( rule )
1229 rule->setSymbol( symbol );
1230 else
1231 delete symbol;
1232}
1233
1235{
1236 return mRootRule->legendSymbolItems();
1237}
1238
1239
1241{
1242 // load symbols
1243 QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
1244 if ( symbolsElem.isNull() )
1245 return nullptr;
1246
1247 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
1248
1249 QDomElement rulesElem = element.firstChildElement( QStringLiteral( "rules" ) );
1250
1251 Rule *root = Rule::create( rulesElem, symbolMap );
1252 if ( !root )
1253 return nullptr;
1254
1256
1257 // delete symbols if there are any more
1259
1260 return r;
1261}
1262
1264{
1265 // retrieve child rules
1266 Rule *root = nullptr;
1267
1268 QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
1269 while ( !ruleElem.isNull() )
1270 {
1271 Rule *child = Rule::createFromSld( ruleElem, geomType );
1272 if ( child )
1273 {
1274 // create the root rule if not done before
1275 if ( !root )
1276 root = new Rule( nullptr );
1277
1278 root->appendChild( child );
1279 }
1280
1281 ruleElem = ruleElem.nextSiblingElement( QStringLiteral( "Rule" ) );
1282 }
1283
1284 if ( !root )
1285 {
1286 // no valid rules was found
1287 return nullptr;
1288 }
1289
1290 // create and return the new renderer
1291 return new QgsRuleBasedRenderer( root );
1292}
1293
1296
1298{
1299 QString attr = r->classAttribute();
1300 // categorizedAttr could be either an attribute name or an expression.
1301 // the only way to differentiate is to test it as an expression...
1302 QgsExpression testExpr( attr );
1303 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1304 {
1305 //not an expression, so need to quote column name
1306 attr = QgsExpression::quotedColumnRef( attr );
1307 }
1308
1309 const auto constCategories = r->categories();
1310 for ( const QgsRendererCategory &cat : constCategories )
1311 {
1312 QString value;
1313 // not quoting numbers saves a type cast
1314 if ( QgsVariantUtils::isNull( cat.value() ) )
1315 value = "NULL";
1316 else if ( cat.value().type() == QVariant::Int )
1317 value = cat.value().toString();
1318 else if ( cat.value().type() == QVariant::Double )
1319 // we loose precision here - so we may miss some categories :-(
1320 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1321 value = QString::number( cat.value().toDouble(), 'f', 4 );
1322 else
1323 value = QgsExpression::quotedString( cat.value().toString() );
1324 const QString filter = QStringLiteral( "%1 %2 %3" ).arg( attr, QgsVariantUtils::isNull( cat.value() ) ? QStringLiteral( "IS" ) : QStringLiteral( "=" ), value );
1325 const QString label = !cat.label().isEmpty() ? cat.label() :
1326 cat.value().isValid() ? value : QString();
1327 initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1328 }
1329}
1330
1332{
1333 QString attr = r->classAttribute();
1334 // categorizedAttr could be either an attribute name or an expression.
1335 // the only way to differentiate is to test it as an expression...
1336 QgsExpression testExpr( attr );
1337 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1338 {
1339 //not an expression, so need to quote column name
1340 attr = QgsExpression::quotedColumnRef( attr );
1341 }
1342 else if ( !testExpr.isField() )
1343 {
1344 //otherwise wrap expression in brackets
1345 attr = QStringLiteral( "(%1)" ).arg( attr );
1346 }
1347
1348 bool firstRange = true;
1349 const auto constRanges = r->ranges();
1350 for ( const QgsRendererRange &rng : constRanges )
1351 {
1352 // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1353 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1354 QString filter = QStringLiteral( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
1355 QString::number( rng.lowerValue(), 'f', 4 ),
1356 QString::number( rng.upperValue(), 'f', 4 ) );
1357 firstRange = false;
1358 QString label = rng.label().isEmpty() ? filter : rng.label();
1359 initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1360 }
1361}
1362
1364{
1365 std::sort( scales.begin(), scales.end() ); // make sure the scales are in ascending order
1366 double oldScale = initialRule->maximumScale();
1367 double maxDenom = initialRule->minimumScale();
1368 QgsSymbol *symbol = initialRule->symbol();
1369 const auto constScales = scales;
1370 for ( int scale : constScales )
1371 {
1372 if ( initialRule->maximumScale() >= scale )
1373 continue; // jump over the first scales out of the interval
1374 if ( maxDenom != 0 && maxDenom <= scale )
1375 break; // ignore the latter scales out of the interval
1376 initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1377 oldScale = scale;
1378 }
1379 // last rule
1380 initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1381}
1382
1384{
1385 QString msg( QStringLiteral( "Rule-based renderer:\n" ) );
1386 msg += mRootRule->dump();
1387 return msg;
1388}
1389
1391{
1392 return mRootRule->willRenderFeature( feature, &context );
1393}
1394
1396{
1397 return mRootRule->symbolsForFeature( feature, &context );
1398}
1399
1401{
1402 return mRootRule->symbolsForFeature( feature, &context );
1403}
1404
1405QSet< QString > QgsRuleBasedRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
1406{
1407 return mRootRule->legendKeysForFeature( feature, &context );
1408}
1409
1411{
1412 return mRootRule->accept( visitor );
1413}
1414
1416{
1417 std::unique_ptr< QgsRuleBasedRenderer > r;
1418 if ( renderer->type() == QLatin1String( "RuleRenderer" ) )
1419 {
1420 r.reset( dynamic_cast<QgsRuleBasedRenderer *>( renderer->clone() ) );
1421 }
1422 else if ( renderer->type() == QLatin1String( "singleSymbol" ) )
1423 {
1424 const QgsSingleSymbolRenderer *singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRenderer *>( renderer );
1425 if ( !singleSymbolRenderer )
1426 return nullptr;
1427
1428 std::unique_ptr< QgsSymbol > origSymbol( singleSymbolRenderer->symbol()->clone() );
1429 r = std::make_unique< QgsRuleBasedRenderer >( origSymbol.release() );
1430 }
1431 else if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
1432 {
1433 const QgsCategorizedSymbolRenderer *categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRenderer *>( renderer );
1434 if ( !categorizedRenderer )
1435 return nullptr;
1436
1437 QString attr = categorizedRenderer->classAttribute();
1438 // categorizedAttr could be either an attribute name or an expression.
1439 bool isField = false;
1440 if ( layer )
1441 {
1442 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1443 }
1444 else
1445 {
1446 QgsExpression testExpr( attr );
1447 isField = testExpr.hasParserError() || testExpr.isField();
1448 }
1449 if ( isField && !attr.contains( '\"' ) )
1450 {
1451 //not an expression, so need to quote column name
1452 attr = QgsExpression::quotedColumnRef( attr );
1453 }
1454
1455 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1456
1457 QString expression;
1458 QString value;
1459 QgsRendererCategory category;
1460 for ( const QgsRendererCategory &category : categorizedRenderer->categories() )
1461 {
1462 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1463
1464 rule->setLabel( category.label() );
1465
1466 //We first define the rule corresponding to the category
1467 if ( category.value().type() == QVariant::List )
1468 {
1469 QStringList values;
1470 const QVariantList list = category.value().toList();
1471 for ( const QVariant &v : list )
1472 {
1473 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1474 if ( QVariant( v ).convert( QVariant::Double ) )
1475 {
1476 values << v.toString();
1477 }
1478 else
1479 {
1480 values << QgsExpression::quotedString( v.toString() );
1481 }
1482 }
1483
1484 if ( values.empty() )
1485 {
1486 expression = QStringLiteral( "ELSE" );
1487 }
1488 else
1489 {
1490 expression = QStringLiteral( "%1 IN (%2)" ).arg( attr, values.join( ',' ) );
1491 }
1492 }
1493 else
1494 {
1495 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1496 if ( category.value().convert( QVariant::Double ) )
1497 {
1498 value = category.value().toString();
1499 }
1500 else
1501 {
1502 value = QgsExpression::quotedString( category.value().toString() );
1503 }
1504
1505 //An empty category is equivalent to the ELSE keyword
1506 if ( value == QLatin1String( "''" ) )
1507 {
1508 expression = QStringLiteral( "ELSE" );
1509 }
1510 else
1511 {
1512 expression = QStringLiteral( "%1 = %2" ).arg( attr, value );
1513 }
1514 }
1515 rule->setFilterExpression( expression );
1516
1517 //Then we construct an equivalent symbol.
1518 //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1519 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1520
1521 std::unique_ptr< QgsSymbol > origSymbol( category.symbol()->clone() );
1522 rule->setSymbol( origSymbol.release() );
1523
1524 rootrule->appendChild( rule.release() );
1525 }
1526
1527 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1528 }
1529 else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1530 {
1531 const QgsGraduatedSymbolRenderer *graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1532 if ( !graduatedRenderer )
1533 return nullptr;
1534
1535 QString attr = graduatedRenderer->classAttribute();
1536 // categorizedAttr could be either an attribute name or an expression.
1537 // the only way to differentiate is to test it as an expression...
1538 bool isField = false;
1539 if ( layer )
1540 {
1541 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1542 }
1543 else
1544 {
1545 QgsExpression testExpr( attr );
1546 isField = testExpr.hasParserError() || testExpr.isField();
1547 }
1548 if ( isField && !attr.contains( '\"' ) )
1549 {
1550 //not an expression, so need to quote column name
1551 attr = QgsExpression::quotedColumnRef( attr );
1552 }
1553 else if ( !isField )
1554 {
1555 //otherwise wrap expression in brackets
1556 attr = QStringLiteral( "(%1)" ).arg( attr );
1557 }
1558
1559 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1560
1561 QString expression;
1562 QgsRendererRange range;
1563 for ( int i = 0; i < graduatedRenderer->ranges().size(); ++i )
1564 {
1565 range = graduatedRenderer->ranges().value( i );
1566 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1567 rule->setLabel( range.label() );
1568 if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
1569 {
1570 expression = attr + " >= " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1571 attr + " <= " + QString::number( range.upperValue(), 'f' );
1572 }
1573 else
1574 {
1575 expression = attr + " > " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1576 attr + " <= " + QString::number( range.upperValue(), 'f' );
1577 }
1578 rule->setFilterExpression( expression );
1579
1580 //Then we construct an equivalent symbol.
1581 //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1582 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1583
1584 std::unique_ptr< QgsSymbol > symbol( range.symbol()->clone() );
1585 rule->setSymbol( symbol.release() );
1586
1587 rootrule->appendChild( rule.release() );
1588 }
1589
1590 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1591 }
1592 else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1593 {
1594 if ( const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer ) )
1595 return convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1596 }
1597 else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1598 {
1599 if ( const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer ) )
1600 r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1601 }
1602 else if ( renderer->type() == QLatin1String( "mergedFeatureRenderer" ) )
1603 {
1604 if ( const QgsMergedFeatureRenderer *mergedRenderer = dynamic_cast<const QgsMergedFeatureRenderer *>( renderer ) )
1605 r.reset( convertFromRenderer( mergedRenderer->embeddedRenderer() ) );
1606 }
1607 else if ( renderer->type() == QLatin1String( "embeddedSymbol" ) && layer )
1608 {
1609 const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
1610
1611 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1612
1615 req.setNoAttributes();
1616 QgsFeatureIterator it = layer->getFeatures( req );
1617 QgsFeature feature;
1618 while ( it.nextFeature( feature ) && rootrule->children().size() < 500 )
1619 {
1620 if ( feature.embeddedSymbol() )
1621 {
1622 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1623 rule->setFilterExpression( QStringLiteral( "$id=%1" ).arg( feature.id() ) );
1624 rule->setLabel( QString::number( feature.id() ) );
1625 rule->setSymbol( feature.embeddedSymbol()->clone() );
1626 rootrule->appendChild( rule.release() );
1627 }
1628 }
1629
1630 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1631 rule->setFilterExpression( QStringLiteral( "ELSE" ) );
1632 rule->setLabel( QObject::tr( "All other features" ) );
1633 rule->setSymbol( embeddedRenderer->defaultSymbol()->clone() );
1634 rootrule->appendChild( rule.release() );
1635
1636 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1637 }
1638
1639 if ( r )
1640 {
1641 renderer->copyRendererData( r.get() );
1642 }
1643
1644 return r.release();
1645}
1646
1647void QgsRuleBasedRenderer::convertToDataDefinedSymbology( QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField )
1648{
1649 QString sizeExpression;
1650 switch ( symbol->type() )
1651 {
1653 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1654 {
1655 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( symbol->symbolLayer( j ) );
1656 if ( ! sizeScaleField.isEmpty() )
1657 {
1658 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1660 }
1661 if ( ! rotationField.isEmpty() )
1662 {
1664 }
1665 }
1666 break;
1668 if ( ! sizeScaleField.isEmpty() )
1669 {
1670 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1671 {
1672 if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "SimpleLine" ) )
1673 {
1674 QgsLineSymbolLayer *lsl = static_cast<QgsLineSymbolLayer *>( symbol->symbolLayer( j ) );
1675 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
1677 }
1678 if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "MarkerLine" ) )
1679 {
1680 QgsSymbol *marker = symbol->symbolLayer( j )->subSymbol();
1681 for ( int k = 0; k < marker->symbolLayerCount(); ++k )
1682 {
1683 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( marker->symbolLayer( k ) );
1684 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1686 }
1687 }
1688 }
1689 }
1690 break;
1691 default:
1692 break;
1693 }
1694}
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition: qgis.h:228
@ Marker
Marker symbol.
@ Line
Line symbol.
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
QString classAttribute() const
Returns the class attribute for the renderer, which is the field name or expression string from the l...
A vector feature renderer which uses embedded feature symbology to render per-feature symbols.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
bool isField() const
Checks whether an expression consists only of a single field reference.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
static int expressionToLayerFieldIndex(const QString &expression, const QgsVectorLayer *layer)
Attempts to resolve an expression to a field index from the given layer.
static bool attemptReduceToInClause(const QStringList &expressions, QString &result)
Attempts to reduce a list of expressions to a single "field IN (val1, val2, ... )" type expression.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
void renderFeatureWithSymbol(const QgsFeature &feature, QgsSymbol *symbol, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker) SIP_THROW(QgsCsException)
Render the feature with the symbol using context.
virtual void stopRender(QgsRenderContext &context)
Must be called when a render cycle has finished, to allow the renderer to clean up.
QString type() const
Definition: qgsrenderer.h:142
void copyRendererData(QgsFeatureRenderer *destRenderer) const
Clones generic renderer data to another renderer.
Definition: qgsrenderer.cpp:46
void saveRendererData(QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context)
Saves generic renderer data into the specified element.
virtual const QgsFeatureRenderer * embeddedRenderer() const
Returns the current embedded renderer (subrenderer) for this feature renderer.
virtual void startRender(QgsRenderContext &context, const QgsFields &fields)
Must be called when a new render cycle is started.
Definition: qgsrenderer.cpp:90
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
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
Container of fields for a vector layer.
Definition: qgsfields.h:45
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
Definition: qgsfillsymbol.h:30
QString classAttribute() const
Returns the attribute name (or expression) used for the classification.
const QgsRangeList & ranges() const
Returns a list of all ranges used in the classification.
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...
virtual double width() const
Returns the estimated width for the line symbol layer.
A line symbol type, for rendering LineString and MultiLineString geometries.
Definition: qgslinesymbol.h:30
Abstract base class for marker symbol layers.
double size() const
Returns the symbol size.
A marker symbol type, for rendering Point and MultiPoint geometries.
QgsMergedFeatureRenderer is a polygon or line-only feature renderer used to renderer a set of feature...
static QgsExpression * expressionFromOgcFilter(const QDomElement &element, QgsVectorLayer *layer=nullptr)
Parse XML with OGC filter into QGIS expression.
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
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.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
QgsSymbol * symbol() const
Returns the symbol which will be used to render 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...
QString label() const
Returns the label used for the range.
QgsSymbol * symbol() const
Returns the symbol used for the range.
double upperValue() const
Returns the upper bound of the range.
double lowerValue() const
Returns the lower bound of the range.
This class keeps data about a rules for rule-based renderer.
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified symbology visitor, causing it to visit all child rules associated with the rule...
QgsRuleBasedRenderer::RuleList descendants() const
Returns all children, grand-children, grand-grand-children, grand-gra... you get it.
void setSymbol(QgsSymbol *sym)
Sets a new symbol (or nullptr). Deletes old symbol.
void removeChild(QgsRuleBasedRenderer::Rule *rule)
delete child rule
QgsRuleBasedRenderer::Rule * findRuleByKey(const QString &key)
Try to find a rule given its unique key.
void insertChild(int i, QgsRuleBasedRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
QString ruleKey() const
Unique rule identifier (for identification of rule within renderer)
bool needsGeometry() const
Returns true if this rule or one of its children needs the geometry to be applied.
QgsRuleBasedRenderer::Rule * takeChild(QgsRuleBasedRenderer::Rule *rule)
take child rule out, set parent as nullptr
const QgsRuleBasedRenderer::RuleList & children() const
Returns all children rules of this rule.
RenderResult
The result of rendering a rule.
@ Rendered
Something was rendered.
QgsRuleBasedRenderer::RuleList rulesForFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr, bool onlyActive=true)
Returns the list of rules used to render the feature in a specific context.
double maximumScale() const
Returns the maximum map scale (i.e.
QgsRuleBasedRenderer::Rule * parent()
The parent rule.
void setIsElse(bool iselse)
Sets if this rule is an ELSE rule.
QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
tell which symbols will be used to render the feature
bool isElse() const
Check if this rule is an ELSE rule.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
Returns which legend keys match the feature.
QgsRuleBasedRenderer::Rule * clone() const
clone this rule, return new instance
bool willRenderFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
only tell whether a feature will be rendered without actually rendering it
void removeChildAt(int i)
delete child rule
void setActive(bool state)
Sets if this rule is active.
Rule(QgsSymbol *symbol, int maximumScale=0, int minimumScale=0, const QString &filterExp=QString(), const QString &label=QString(), const QString &description=QString(), bool elseRule=false)
Constructor takes ownership of the symbol.
bool isFilterOK(const QgsFeature &f, QgsRenderContext *context=nullptr) const
Check if a given feature shall be rendered by this rule.
QgsSymbolList symbols(const QgsRenderContext &context=QgsRenderContext()) const
bool isScaleOK(double scale) const
Check if this rule applies for a given scale.
static QgsRuleBasedRenderer::Rule * createFromSld(QDomElement &element, Qgis::GeometryType geomType)
Create a rule from the SLD provided in element and for the specified geometry type.
void setNormZLevels(const QMap< int, int > &zLevelsToNormLevels)
assign normalized z-levels [0..N-1] for this rule's symbol for quick access during rendering
QDomElement save(QDomDocument &doc, QgsSymbolMap &symbolMap) const
void appendChild(QgsRuleBasedRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
QgsRuleBasedRenderer::Rule * takeChildAt(int i)
take child rule out, set parent as nullptr
QSet< int > collectZLevels()
Gets all used z-levels from this rule and children.
double minimumScale() const
Returns the minimum map scale (i.e.
void stopRender(QgsRenderContext &context)
Stop a rendering process.
bool hasActiveChildren() const
Returns true if the rule has any active children.
QgsRuleBasedRenderer::Rule::RenderResult renderFeature(QgsRuleBasedRenderer::FeatureToRender &featToRender, QgsRenderContext &context, QgsRuleBasedRenderer::RenderQueue &renderQueue)
Render a given feature, will recursively call subclasses and only render if the constraints apply.
QgsLegendSymbolList legendSymbolItems(int currentLevel=-1) const
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the attributes used to evaluate the expression of this rule.
void setFilterExpression(const QString &filterExp)
Set the expression used to check if a given feature shall be rendered with this rule.
QString dump(int indent=0) const
Dump for debug purpose.
void setRuleKey(const QString &key)
Override the assigned rule key (should be used just internally by rule-based renderer)
bool startRender(QgsRenderContext &context, const QgsFields &fields, QString &filter)
prepare the rule for rendering and its children (build active children array)
static QgsRuleBasedRenderer::Rule * create(QDomElement &ruleElem, QgsSymbolMap &symbolMap, bool reuseId=true)
Create a rule from an XML definition.
QString filterExpression() const
A filter that will check if this rule applies.
bool active() const
Returns if this rule is active.
void toSld(QDomDocument &doc, QDomElement &element, QVariantMap props) const
Saves the symbol layer as SLD.
Rule based renderer.
static void refineRuleCategories(QgsRuleBasedRenderer::Rule *initialRule, QgsCategorizedSymbolRenderer *r)
take a rule and create a list of new rules based on the categories from categorized symbol renderer
static void convertToDataDefinedSymbology(QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField=QString())
helper function to convert the size scale and rotation fields present in some other renderers to data...
bool legendSymbolItemChecked(const QString &key) override
items of symbology items in legend is checked
void startRender(QgsRenderContext &context, const QgsFields &fields) override
Must be called when a new render cycle is started.
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) override
Stores renderer properties to an XML element.
void setLegendSymbolItem(const QString &key, QgsSymbol *symbol) override
Sets the symbol to be used for a legend symbol item.
bool canSkipRender() override
Returns true if the renderer can be entirely skipped, i.e.
void checkLegendSymbolItem(const QString &key, bool state=true) override
item in symbology was checked
QgsSymbol * symbolForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns symbol for current feature. Should not be used individually: there could be more symbols for ...
QList< QgsRuleBasedRenderer::RenderLevel > RenderQueue
Rendering queue: a list of rendering levels.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns legend keys matching a specified feature.
static void refineRuleRanges(QgsRuleBasedRenderer::Rule *initialRule, QgsGraduatedSymbolRenderer *r)
take a rule and create a list of new rules based on the ranges from graduated symbol renderer
QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns list of symbols used for rendering the feature.
QString dump() const override
Returns debug information about this renderer.
QgsSymbolList originalSymbolsForFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Equivalent of originalSymbolsForFeature() call extended to support renderers that may use more symbol...
static QgsRuleBasedRenderer * convertFromRenderer(const QgsFeatureRenderer *renderer, QgsVectorLayer *layer=nullptr)
Creates a new QgsRuleBasedRenderer from an existing renderer.
bool legendSymbolItemsCheckable() const override
items of symbology items in legend should be checkable
QSet< QString > usedAttributes(const QgsRenderContext &context) const override
Returns a list of attributes required by this renderer.
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...
bool willRenderFeature(const QgsFeature &feature, QgsRenderContext &context) const override
Returns whether the renderer will render a feature or not.
static void refineRuleScales(QgsRuleBasedRenderer::Rule *initialRule, QList< int > scales)
take a rule and create a list of new rules with intervals of scales given by the passed scale denomin...
QList< QgsRuleBasedRenderer::Rule * > RuleList
void stopRender(QgsRenderContext &context) override
Must be called when a render cycle has finished, to allow the renderer to clean up.
Rule * mRootRule
the root node with hierarchical list of rules
bool accept(QgsStyleEntityVisitorInterface *visitor) const override
Accepts the specified symbology visitor, causing it to visit all symbols associated with the renderer...
bool filterNeedsGeometry() const override
Returns true if this renderer requires the geometry to apply the filter.
QgsRuleBasedRenderer * clone() const override
Create a deep copy of this renderer.
static QgsFeatureRenderer * create(QDomElement &element, const QgsReadWriteContext &context)
Creates a new rule-based renderer instance from XML.
bool renderFeature(const QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false) override SIP_THROW(QgsCsException)
Render a feature using this renderer in the given context.
QgsLegendSymbolList legendSymbolItems() const override
Returns a list of symbology items for the legend.
static QgsFeatureRenderer * createFromSld(QDomElement &element, Qgis::GeometryType geomType)
Creates a new rule based renderer from an SLD XML element.
QList< FeatureToRender > mCurrentFeatures
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...
QgsRuleBasedRenderer(QgsRuleBasedRenderer::Rule *root)
Constructs the renderer from given tree of rules (takes ownership)
QgsSymbolList symbols(QgsRenderContext &context) const override
Returns list of symbols used by the renderer.
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
QgsSymbol * symbol() const
Returns the symbol which will be rendered for every feature.
An interface for classes which can visit style entity (e.g.
@ SymbolRule
Rule based symbology or label child rule.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
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
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 bool createSymbolLayerListFromSld(QDomElement &element, Qgis::GeometryType geomType, QList< QgsSymbolLayer * > &layers)
Creates a symbol layer list from a DOM element.
static void mergeScaleDependencies(double mScaleMinDenom, double mScaleMaxDenom, QVariantMap &props)
Merges the local scale limits, if any, with the ones already in the map, if any.
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.
@ PropertyAngle
Symbol angle.
@ PropertySize
Symbol size.
@ PropertyStrokeWidth
Stroke width.
virtual QString layerType() const =0
Returns a string that represents this layer type.
int renderingPass() const
Specifies the rendering pass in which this symbol layer should be rendered.
virtual void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
virtual QgsSymbol * subSymbol()
Returns the symbol's sub symbol, if present.
Abstract base class for all rendered symbols.
Definition: qgssymbol.h:93
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
Definition: qgssymbol.cpp:756
virtual QgsSymbol * clone() const =0
Returns a deep copy of this symbol.
int symbolLayerCount() const
Returns the total number of symbol layers contained in the symbol.
Definition: qgssymbol.h:215
Qgis::SymbolType type() const
Returns the symbol's type.
Definition: qgssymbol.h:152
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.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition: qgis.h:3713
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:3509
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
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition: qgssymbol.h:29
Feature for rendering by a QgsRuleBasedRenderer.
A QgsRuleBasedRenderer rendering job, consisting of a feature to be rendered with a particular symbol...
Render level: a list of jobs to be drawn at particular level for a QgsRuleBasedRenderer.
Contains information relating to a node (i.e.
Contains information relating to the style entity currently being visited.