QGIS API Documentation 4.3.0-Master (0978c174f8e)
Loading...
Searching...
No Matches
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
19#include "qgsexpression.h"
20#include "qgsfillsymbol.h"
22#include "qgslinesymbol.h"
23#include "qgslogger.h"
24#include "qgsmarkersymbol.h"
25#include "qgsogcutils.h"
27#include "qgsproperty.h"
28#include "qgsrendercontext.h"
29#include "qgsscaleutils.h"
31#include "qgssldexportcontext.h"
33#include "qgssymbollayer.h"
34#include "qgssymbollayerutils.h"
35#include "qgsvectorlayer.h"
36
37#include <QDomDocument>
38#include <QDomElement>
39#include <QSet>
40#include <QString>
41#include <QUuid>
42
43using namespace Qt::StringLiterals;
44
45QgsRuleBasedRenderer::Rule::Rule( QgsSymbol *symbol, int scaleMinDenom, int scaleMaxDenom, const QString &filterExp, const QString &label, const QString &description, bool elseRule )
46 : mSymbol( symbol )
47 , mMaximumScale( scaleMinDenom )
48 , mMinimumScale( scaleMaxDenom )
49 , mFilterExp( filterExp )
50 , mLabel( label )
51 , mDescription( description )
52 , mElseRule( elseRule )
53{
54 if ( mElseRule )
55 mFilterExp = u"ELSE"_s;
56
57 mRuleKey = QUuid::createUuid().toString();
58 initFilter();
59}
60
62{
63 qDeleteAll( mChildren );
64 // do NOT delete parent
65}
66
68{
69 if ( mFilterExp.trimmed().compare( "ELSE"_L1, Qt::CaseInsensitive ) == 0 )
70 {
71 mElseRule = true;
72 mFilter.reset();
73 }
74 else if ( mFilterExp.trimmed().isEmpty() )
75 {
76 mElseRule = false;
77 mFilter.reset();
78 }
79 else
80 {
81 mElseRule = false;
82 mFilter = std::make_unique< QgsExpression >( mFilterExp );
83 }
84}
85
87{
88 mChildren.append( rule );
89 rule->mParent = this;
90 updateElseRules();
91}
92
94{
95 mChildren.insert( i, rule );
96 rule->mParent = this;
97 updateElseRules();
98}
99
101{
102 mChildren.removeAll( rule );
103 delete rule;
104 updateElseRules();
105}
106
108{
109 delete mChildren.takeAt( i );
110 updateElseRules();
111}
112
114{
115 mChildren.removeAll( rule );
116 rule->mParent = nullptr;
117 updateElseRules();
118 return rule;
119}
120
122{
123 Rule *rule = mChildren.takeAt( i );
124 rule->mParent = nullptr;
125 updateElseRules();
126 return rule;
127}
128
130{
131 // we could use a hash / map for search if this will be slow...
132
133 if ( key == mRuleKey )
134 return this;
135
136 const auto constMChildren = mChildren;
137 for ( Rule *rule : constMChildren )
138 {
139 Rule *r = rule->findRuleByKey( key );
140 if ( r )
141 return r;
142 }
143 return nullptr;
144}
145
146void QgsRuleBasedRenderer::Rule::updateElseRules()
147{
148 mElseRules.clear();
149 const auto constMChildren = mChildren;
150 for ( Rule *rule : constMChildren )
151 {
152 if ( rule->isElse() )
153 mElseRules << rule;
154 }
155}
156
158{
159 mFilterExp = u"ELSE"_s;
160 mElseRule = iselse;
161 mFilter.reset();
162}
163
165{
166 // NOTE: if visitEnter returns false it means "don't visit the rule", not "abort all further visitations"
168 return true;
169
170 if ( mSymbol )
171 {
172 QgsStyleSymbolEntity entity( mSymbol.get() );
173 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
174 return false;
175 }
176
177 if ( !mChildren.empty() )
178 {
179 for ( const Rule *rule : mChildren )
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() : u"[]"_s );
197 QString msg = off + u"RULE %1 - scale [%2,%3] - filter %4 - symbol %5\n"_s.arg( mLabel ).arg( mMaximumScale ).arg( mMinimumScale ).arg( mFilterExp, symbolDump );
198
199 QStringList lst;
200 const auto constMChildren = mChildren;
201 for ( Rule *rule : constMChildren )
202 {
203 lst.append( rule->dump( indent + 2 ) );
204 }
205 msg += lst.join( QLatin1Char( '\n' ) );
206 return msg;
207}
208
210{
211 // attributes needed by this rule
212 QSet<QString> attrs;
213 if ( mFilter )
214 attrs.unite( mFilter->referencedColumns() );
215 if ( mSymbol )
216 attrs.unite( mSymbol->usedAttributes( context ) );
217
218 // attributes needed by child rules
219 const auto constMChildren = mChildren;
220 for ( Rule *rule : constMChildren )
221 {
222 attrs.unite( rule->usedAttributes( context ) );
223 }
224 return attrs;
225}
226
228{
229 if ( mFilter && mFilter->needsGeometry() )
230 return true;
231
232 const auto constMChildren = mChildren;
233 for ( Rule *rule : constMChildren )
234 {
235 if ( rule->needsGeometry() )
236 return true;
237 }
238
239 return false;
240}
241
243{
244 QgsSymbolList lst;
245 if ( mSymbol )
246 lst.append( mSymbol.get() );
247
248 const auto constMChildren = mChildren;
249 for ( Rule *rule : constMChildren )
250 {
251 lst += rule->symbols( context );
252 }
253 return lst;
254}
255
257{
258 mSymbol.reset( sym );
259}
260
262{
263 mFilterExp = filterExp;
264 initFilter();
265}
266
268{
270 if ( currentLevel != -1 ) // root rule should not be shown
271 {
272 lst << QgsLegendSymbolItem( mSymbol.get(), mLabel, mRuleKey, true, mMaximumScale, mMinimumScale, currentLevel, mParent ? mParent->mRuleKey : QString() );
273 }
274
275 for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
276 {
277 Rule *rule = *it;
278 lst << rule->legendSymbolItems( currentLevel + 1 );
279 }
280 return lst;
281}
282
283
285{
286 if ( !mFilter || mElseRule || !context )
287 return true;
288
289 context->expressionContext().setFeature( f );
290 QVariant res = mFilter->evaluate( &context->expressionContext() );
291 return res.toBool();
292}
293
295{
296 if ( qgsDoubleNear( scale, 0.0 ) ) // so that we can count features in classes without scale context
297 return true;
298 if ( qgsDoubleNear( mMaximumScale, 0.0 ) && qgsDoubleNear( mMinimumScale, 0.0 ) )
299 return true;
300
301 // maxScale is inclusive ( < --> no render )
302 if ( !qgsDoubleNear( mMaximumScale, 0.0 ) && QgsScaleUtils::lessThanMaximumScale( scale, mMaximumScale ) )
303 return false;
304
305 // minScale is exclusive ( >= --> no render )
306 if ( !qgsDoubleNear( mMinimumScale, 0.0 ) && QgsScaleUtils::equalToOrGreaterThanMinimumScale( scale, mMinimumScale ) )
307 return false;
308
309 return true;
310}
311
313{
314 QgsSymbol *sym = mSymbol ? mSymbol->clone() : nullptr;
315 Rule *newrule = new Rule( sym, mMaximumScale, mMinimumScale, mFilterExp, mLabel, mDescription );
316 newrule->setActive( mIsActive );
317 // clone children
318 const auto constMChildren = mChildren;
319 for ( Rule *rule : constMChildren )
320 newrule->appendChild( rule->clone() );
321 return newrule;
322}
323
324QDomElement QgsRuleBasedRenderer::Rule::save( QDomDocument &doc, QgsSymbolMap &symbolMap ) const
325{
326 QDomElement ruleElem = doc.createElement( u"rule"_s );
327
328 if ( mSymbol )
329 {
330 int symbolIndex = symbolMap.size();
331 symbolMap[QString::number( symbolIndex )] = mSymbol.get();
332 ruleElem.setAttribute( u"symbol"_s, symbolIndex );
333 }
334 if ( !mFilterExp.isEmpty() )
335 ruleElem.setAttribute( u"filter"_s, mFilterExp );
336 if ( mMaximumScale != 0 )
337 ruleElem.setAttribute( u"scalemindenom"_s, mMaximumScale );
338 if ( mMinimumScale != 0 )
339 ruleElem.setAttribute( u"scalemaxdenom"_s, mMinimumScale );
340 if ( !mLabel.isEmpty() )
341 ruleElem.setAttribute( u"label"_s, mLabel );
342 if ( !mDescription.isEmpty() )
343 ruleElem.setAttribute( u"description"_s, mDescription );
344 if ( !mIsActive )
345 ruleElem.setAttribute( u"checkstate"_s, 0 );
346 ruleElem.setAttribute( u"key"_s, mRuleKey );
347
348 const auto constMChildren = mChildren;
349 for ( Rule *rule : constMChildren )
350 {
351 ruleElem.appendChild( rule->save( doc, symbolMap ) );
352 }
353 return ruleElem;
354}
355
356void QgsRuleBasedRenderer::Rule::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
357{
358 QgsSldExportContext context;
359 context.setExtraProperties( props );
360 toSld( doc, element, context );
361}
362
363bool QgsRuleBasedRenderer::Rule::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &exportContext ) const
364{
365 // do not convert this rule if there are no symbols
366 QgsRenderContext context;
367 if ( symbols( context ).isEmpty() )
368 return false;
369
370 const QVariantMap oldProps = exportContext.extraProperties();
371 QVariantMap props = oldProps;
372 if ( !mFilterExp.isEmpty() )
373 {
374 QString filter = props.value( u"filter"_s, QString() ).toString();
375 if ( !filter.isEmpty() )
376 filter += " AND "_L1;
377 filter += mFilterExp;
378 props[u"filter"_s] = filter;
379 }
380
381 QgsSymbolLayerUtils::mergeScaleDependencies( mMaximumScale, mMinimumScale, props );
382 exportContext.setExtraProperties( props );
383
384 if ( mSymbol )
385 {
386 QDomElement ruleElem = doc.createElement( u"se:Rule"_s );
387
388 //XXX: <se:Name> is the rule identifier, but our the Rule objects
389 // have no properties could be used as identifier. Use the label.
390 QDomElement nameElem = doc.createElement( u"se:Name"_s );
391 nameElem.appendChild( doc.createTextNode( mLabel ) );
392 ruleElem.appendChild( nameElem );
393
394 if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
395 {
396 QDomElement descrElem = doc.createElement( u"se:Description"_s );
397 if ( !mLabel.isEmpty() )
398 {
399 QDomElement titleElem = doc.createElement( u"se:Title"_s );
400 titleElem.appendChild( doc.createTextNode( mLabel ) );
401 descrElem.appendChild( titleElem );
402 }
403 if ( !mDescription.isEmpty() )
404 {
405 QDomElement abstractElem = doc.createElement( u"se:Abstract"_s );
406 abstractElem.appendChild( doc.createTextNode( mDescription ) );
407 descrElem.appendChild( abstractElem );
408 }
409 ruleElem.appendChild( descrElem );
410 }
411
412 if ( !props.value( u"filter"_s, QString() ).toString().isEmpty() )
413 {
414 QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, props.value( u"filter"_s, QString() ).toString(), exportContext );
415 }
416
417 QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
418 exportContext.setExtraProperties( props );
419
420 mSymbol->toSld( doc, ruleElem, exportContext );
421
422 // Only create rules if symbol could be converted to SLD, and is not an "empty" symbol. Otherwise we do not generate a rule, as
423 // SLD spec requires a Symbolizer element to be present
425 {
426 element.appendChild( ruleElem );
427 }
428 }
429
430 // loop into children rule list
431 bool result = true;
432 for ( Rule *rule : std::as_const( mChildren ) )
433 {
434 if ( !rule->toSld( doc, element, exportContext ) )
435 result = false;
436 }
437 exportContext.setExtraProperties( oldProps );
438 return result;
439}
440
442{
443 mActiveChildren.clear();
444
445 if ( !mIsActive )
446 return false;
447
448 // filter out rules which are not compatible with this scale
449 if ( !isScaleOK( context.rendererScale() ) )
450 return false;
451
452 // init this rule
453 if ( mFilter )
454 mFilter->prepare( &context.expressionContext() );
455 if ( mSymbol )
456 mSymbol->startRender( context, fields );
457
458 // init children
459 // build temporary list of active rules (usable with this scale)
460 QStringList subfilters;
461 const auto constMChildren = mChildren;
462 for ( Rule *rule : constMChildren )
463 {
464 QString subfilter;
465 if ( rule->startRender( context, fields, subfilter ) )
466 {
467 // only add those which are active with current scale
468 mActiveChildren.append( rule );
469 subfilters.append( subfilter );
470 }
471 }
472
473 // subfilters (on the same level) are joined with OR
474 // Finally they are joined with their parent (this) with AND
475 QString sf;
476 // If there are subfilters present (and it's not a single empty one), group them and join them with OR
477 if ( subfilters.length() > 1 || !subfilters.value( 0 ).isEmpty() )
478 {
479 if ( subfilters.contains( u"TRUE"_s ) )
480 {
481 sf = u"TRUE"_s;
482 }
483 else
484 {
485 // test for a common case -- all subfilters can be combined into a single "field in (...)" expression
486 if ( QgsExpression::attemptReduceToInClause( subfilters, sf ) )
487 {
488 // success! we can use a simple "field IN (...)" list!
489 }
490 // If we have more than 50 rules (to stay on the safe side) make a binary tree or SQLITE will fail,
491 // see: https://github.com/qgis/QGIS/issues/27269
492 else if ( subfilters.count() > 50 )
493 {
494 std::function<QString( const QStringList & )> bt = [&bt]( const QStringList &subf ) {
495 if ( subf.count() == 1 )
496 {
497 return subf.at( 0 );
498 }
499 else if ( subf.count() == 2 )
500 {
501 return subf.join( ") OR ("_L1 ).prepend( '(' ).append( ')' );
502 }
503 else
504 {
505 int midpos = static_cast<int>( subf.length() / 2 );
506 return u"(%1) OR (%2)"_s.arg( bt( subf.mid( 0, midpos ) ), bt( subf.mid( midpos ) ) );
507 }
508 };
509 sf = bt( subfilters );
510 }
511 else
512 {
513 sf = subfilters.join( ") OR ("_L1 ).prepend( '(' ).append( ')' );
514 }
515 }
516 }
517
518 // Now join the subfilters with their parent (this) based on if
519 // * The parent is an else rule
520 // * The existence of parent filter and subfilters
521
522 // No filter expression: ELSE rule or catchall rule
523 if ( !mFilter )
524 {
525 if ( mSymbol || sf.isEmpty() )
526 filter = u"TRUE"_s;
527 else
528 filter = sf;
529 }
530 else if ( mSymbol )
531 filter = mFilterExp;
532 else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
533 filter = u"(%1) AND (%2)"_s.arg( mFilterExp, sf );
534 else if ( !mFilterExp.trimmed().isEmpty() )
535 filter = mFilterExp;
536 else if ( sf.isEmpty() )
537 filter = u"TRUE"_s;
538 else
539 filter = sf;
540
541 filter = filter.trimmed();
542
543 return true;
544}
545
547{
548 return !mActiveChildren.empty();
549}
550
552{
553 QSet<int> symbolZLevelsSet;
554
555 // process this rule
556 if ( mSymbol )
557 {
558 // find out which Z-levels are used
559 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
560 {
561 symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
562 }
563 }
564
565 // process children
566 QList<Rule *>::iterator it;
567 for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
568 {
569 Rule *rule = *it;
570 symbolZLevelsSet.unite( rule->collectZLevels() );
571 }
572 return symbolZLevelsSet;
573}
574
575void QgsRuleBasedRenderer::Rule::setNormZLevels( const QMap<int, int> &zLevelsToNormLevels )
576{
577 if ( mSymbol )
578 {
579 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
580 {
581 int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
582 mSymbolNormZLevels.insert( normLevel );
583 }
584 }
585
586 // prepare list of normalized levels for each rule
587 const auto constMActiveChildren = mActiveChildren;
588 for ( Rule *rule : constMActiveChildren )
589 {
590 rule->setNormZLevels( zLevelsToNormLevels );
591 }
592}
593
594
597)
598{
599 if ( !isFilterOK( featToRender.feat, &context ) )
600 return Filtered;
601
602 bool rendered = false;
603
604 // create job for this feature and this symbol, add to list of jobs
605 if ( mSymbol && mIsActive )
606 {
607 // add job to the queue: each symbol's zLevel must be added
608 const auto constMSymbolNormZLevels = mSymbolNormZLevels;
609 for ( int normZLevel : constMSymbolNormZLevels )
610 {
611 //QgsDebugMsgLevel(QString("add job at level %1").arg(normZLevel),2);
612 renderQueue[normZLevel].jobs.append( new RenderJob( featToRender, mSymbol.get() ) );
613 rendered = true;
614 }
615 }
616
617 bool matchedAChild = false;
618
619 // process children
620 const auto constMChildren = mChildren;
621 for ( Rule *rule : constMChildren )
622 {
623 // Don't process else rules yet
624 if ( !rule->isElse() )
625 {
626 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
627 // consider inactive items as "matched" so the else rule will ignore them
628 matchedAChild |= ( res == Rendered || res == Inactive );
629 rendered |= ( res == Rendered );
630 }
631 }
632
633 // If none of the rules passed then we jump into the else rules and process them.
634 if ( !matchedAChild )
635 {
636 const auto constMElseRules = mElseRules;
637 for ( Rule *rule : constMElseRules )
638 {
639 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
640 matchedAChild |= ( res == Rendered || res == Inactive );
641 rendered |= res == Rendered;
642 }
643 }
644 if ( !mIsActive || ( mSymbol && !rendered ) || ( matchedAChild && !rendered ) )
645 return Inactive;
646 else if ( rendered )
647 return Rendered;
648 else
649 return Filtered;
650}
651
653{
654 if ( !isFilterOK( feature, context ) )
655 return false;
656
657 if ( mSymbol )
658 return true;
659
660 const auto constMActiveChildren = mActiveChildren;
661 for ( Rule *rule : constMActiveChildren )
662 {
663 if ( rule->isElse() )
664 {
665 if ( rule->children().isEmpty() )
666 {
667 RuleList lst = rulesForFeature( feature, context, false );
668 lst.removeOne( rule );
669
670 if ( lst.empty() )
671 {
672 return true;
673 }
674 }
675 else
676 {
677 return rule->willRenderFeature( feature, context );
678 }
679 }
680 else if ( rule->willRenderFeature( feature, context ) )
681 {
682 return true;
683 }
684 }
685 return false;
686}
687
689{
690 QgsSymbolList lst;
691 if ( !isFilterOK( feature, context ) )
692 return lst;
693 if ( mSymbol )
694 lst.append( mSymbol.get() );
695
696 const auto constMActiveChildren = mActiveChildren;
697 for ( Rule *rule : constMActiveChildren )
698 {
699 lst += rule->symbolsForFeature( feature, context );
700 }
701 return lst;
702}
703
705{
706 QSet< QString> res;
707 if ( !isFilterOK( feature, context ) )
708 return res;
709
710 res.insert( mRuleKey );
711
712 // first determine if any non else rules match at this level
713 bool matchedNonElseRule = false;
714 for ( Rule *rule : std::as_const( mActiveChildren ) )
715 {
716 if ( rule->isElse() )
717 {
718 continue;
719 }
720 if ( rule->willRenderFeature( feature, context ) )
721 {
722 res.unite( rule->legendKeysForFeature( feature, context ) );
723 matchedNonElseRule = true;
724 }
725 }
726
727 // second chance -- allow else rules to take effect if valid
728 if ( !matchedNonElseRule )
729 {
730 for ( Rule *rule : std::as_const( mActiveChildren ) )
731 {
732 if ( rule->isElse() )
733 {
734 if ( rule->children().isEmpty() )
735 {
736 RuleList lst = rulesForFeature( feature, context, false );
737 lst.removeOne( rule );
738
739 if ( lst.empty() )
740 {
741 res.unite( rule->legendKeysForFeature( feature, context ) );
742 }
743 }
744 else
745 {
746 res.unite( rule->legendKeysForFeature( feature, context ) );
747 }
748 }
749 }
750 }
751 return res;
752}
753
755{
756 RuleList lst;
757 if ( !isFilterOK( feature, context ) || ( context && !isScaleOK( context->rendererScale() ) ) )
758 return lst;
759
760 if ( mSymbol )
761 lst.append( this );
762
763 RuleList listChildren = children();
764 if ( onlyActive )
765 listChildren = mActiveChildren;
766
767 for ( Rule *rule : std::as_const( listChildren ) )
768 {
769 lst += rule->rulesForFeature( feature, context, onlyActive );
770 }
771 return lst;
772}
773
775{
776 if ( mSymbol )
777 mSymbol->stopRender( context );
778
779 const auto constMActiveChildren = mActiveChildren;
780 for ( Rule *rule : constMActiveChildren )
781 {
782 rule->stopRender( context );
783 }
784
785 mActiveChildren.clear();
786 mSymbolNormZLevels.clear();
787}
788
789QgsRuleBasedRenderer::Rule *QgsRuleBasedRenderer::Rule::create( QDomElement &ruleElem, QgsSymbolMap &symbolMap, bool reuseId, const QgsReadWriteContext &context )
790{
791 QString symbolIdx = ruleElem.attribute( u"symbol"_s );
792 QgsSymbol *symbol = nullptr;
793 if ( !symbolIdx.isEmpty() )
794 {
795 if ( symbolMap.contains( symbolIdx ) )
796 {
797 symbol = symbolMap.take( symbolIdx );
798 }
799 else
800 {
801 QgsDebugError( "symbol for rule " + symbolIdx + " not found!" );
802 }
803 }
804
805 QString filterExp = ruleElem.attribute( u"filter"_s );
806 QString label = context.projectTranslator()->translate( u"project:layers:%1:legendsymbollabels"_s.arg( context.currentLayerId() ), ruleElem.attribute( u"label"_s ) );
807 QgsDebugMsgLevel( "context" + u"project:layers:%1:legendsymbollabels"_s.arg( context.currentLayerId() ) + " source " + ruleElem.attribute( u"label"_s ), 3 );
808 QString description = ruleElem.attribute( u"description"_s );
809 int scaleMinDenom = ruleElem.attribute( u"scalemindenom"_s, u"0"_s ).toInt();
810 int scaleMaxDenom = ruleElem.attribute( u"scalemaxdenom"_s, u"0"_s ).toInt();
811
812 QString ruleKey;
813 if ( reuseId )
814 ruleKey = ruleElem.attribute( u"key"_s );
815 else
816 ruleKey = QUuid::createUuid().toString();
817 Rule *rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
818
819 if ( !ruleKey.isEmpty() )
820 rule->mRuleKey = ruleKey;
821
822 rule->setActive( ruleElem.attribute( u"checkstate"_s, u"1"_s ).toInt() );
823
824 QDomElement childRuleElem = ruleElem.firstChildElement( u"rule"_s );
825 while ( !childRuleElem.isNull() )
826 {
827 Rule *childRule = create( childRuleElem, symbolMap, true, context );
828 if ( childRule )
829 {
830 rule->appendChild( childRule );
831 }
832 else
833 {
834 QgsDebugError( u"failed to init a child rule!"_s );
835 }
836 childRuleElem = childRuleElem.nextSiblingElement( u"rule"_s );
837 }
838
839 return rule;
840}
841
843{
844 RuleList l;
845 for ( QgsRuleBasedRenderer::Rule *c : mChildren )
846 {
847 l += c;
848 l += c->descendants();
849 }
850 return l;
851}
852
854{
855 if ( ruleElem.localName() != "Rule"_L1 )
856 {
857 QgsDebugError( u"invalid element: Rule element expected, %1 found!"_s.arg( ruleElem.tagName() ) );
858 return nullptr;
859 }
860
861 QString label, description, filterExp;
862 int scaleMinDenom = 0, scaleMaxDenom = 0;
863 QgsSymbolLayerList layers;
864
865 // retrieve the Rule element child nodes
866 QDomElement childElem = ruleElem.firstChildElement();
867 while ( !childElem.isNull() )
868 {
869 if ( childElem.localName() == "Name"_L1 )
870 {
871 // <se:Name> tag contains the rule identifier,
872 // so prefer title tag for the label property value
873 if ( label.isEmpty() )
874 label = childElem.firstChild().nodeValue();
875 }
876 else if ( childElem.localName() == "Description"_L1 )
877 {
878 // <se:Description> can contains a title and an abstract
879 QDomElement titleElem = childElem.firstChildElement( u"Title"_s );
880 if ( !titleElem.isNull() )
881 {
882 label = titleElem.firstChild().nodeValue();
883 }
884
885 QDomElement abstractElem = childElem.firstChildElement( u"Abstract"_s );
886 if ( !abstractElem.isNull() )
887 {
888 description = abstractElem.firstChild().nodeValue();
889 }
890 }
891 else if ( childElem.localName() == "Abstract"_L1 )
892 {
893 // <sld:Abstract> (v1.0)
894 description = childElem.firstChild().nodeValue();
895 }
896 else if ( childElem.localName() == "Title"_L1 )
897 {
898 // <sld:Title> (v1.0)
899 label = childElem.firstChild().nodeValue();
900 }
901 else if ( childElem.localName() == "Filter"_L1 )
902 {
904 if ( filter )
905 {
906 if ( filter->hasParserError() )
907 {
908 QgsDebugError( "parser error: " + filter->parserErrorString() );
909 }
910 else
911 {
912 filterExp = filter->expression();
913 }
914 delete filter;
915 }
916 }
917 else if ( childElem.localName() == "ElseFilter"_L1 )
918 {
919 filterExp = "ELSE"_L1;
920 }
921 else if ( childElem.localName() == "MinScaleDenominator"_L1 )
922 {
923 bool ok;
924 int v = childElem.firstChild().nodeValue().toInt( &ok );
925 if ( ok )
926 scaleMinDenom = v;
927 }
928 else if ( childElem.localName() == "MaxScaleDenominator"_L1 )
929 {
930 bool ok;
931 int v = childElem.firstChild().nodeValue().toInt( &ok );
932 if ( ok )
933 scaleMaxDenom = v;
934 }
935 else if ( childElem.localName().endsWith( "Symbolizer"_L1 ) )
936 {
937 // create symbol layers for this symbolizer
938 QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
939 }
940
941 childElem = childElem.nextSiblingElement();
942 }
943
944 // now create the symbol
945 QgsSymbol *symbol = nullptr;
946 if ( !layers.isEmpty() )
947 {
948 switch ( geomType )
949 {
951 symbol = new QgsLineSymbol( layers );
952 break;
953
955 symbol = new QgsFillSymbol( layers );
956 break;
957
959 symbol = new QgsMarkerSymbol( layers );
960 break;
961
962 default:
963 QgsDebugError( u"invalid geometry type: found %1"_s.arg( qgsEnumValueToKey( geomType ) ) );
964 return nullptr;
965 }
966 }
967
968 // and then create and return the new rule
969 return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
970}
971
972
974
979
981 : QgsFeatureRenderer( u"RuleRenderer"_s )
982{
983 mRootRule = std::make_unique<Rule>( nullptr ); // root has no symbol, no filter etc - just a container
984 mRootRule->appendChild( new Rule( defaultSymbol ) );
985}
986
989
990
992{
993 // not used at all
994 return nullptr;
995}
996
998{
1000
1001 std::function< void( Rule * rule ) > exploreRule;
1002 exploreRule = [&res, &exploreRule]( Rule *rule ) {
1003 if ( !rule )
1004 return;
1005
1006 if ( QgsSymbol *symbol = rule->symbol() )
1007 {
1008 if ( symbol->flags().testFlag( Qgis::SymbolFlag::AffectsLabeling ) )
1010 }
1011
1012 for ( Rule *child : rule->children() )
1013 {
1014 exploreRule( child );
1015 }
1016 };
1017 exploreRule( mRootRule.get() );
1018
1019 return res;
1020}
1021
1022bool QgsRuleBasedRenderer::renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker )
1023{
1024 Q_UNUSED( layer )
1025
1026 int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
1027 mCurrentFeatures.append( FeatureToRender( feature, flags ) );
1028
1029 // check each active rule
1030 return mRootRule->renderFeature( mCurrentFeatures.last(), context, mRenderQueue ) == Rule::Rendered;
1031}
1032
1033
1035{
1036 QgsFeatureRenderer::startRender( context, fields );
1037
1038 // prepare active children
1039 mRootRule->startRender( context, fields, mFilter );
1040
1041 QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
1042 QList<int> symbolZLevels( symbolZLevelsSet.begin(), symbolZLevelsSet.end() );
1043 std::sort( symbolZLevels.begin(), symbolZLevels.end() );
1044
1045 // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
1046 // and prepare rendering queue
1047 QMap<int, int> zLevelsToNormLevels;
1048 int maxNormLevel = -1;
1049 const auto constSymbolZLevels = symbolZLevels;
1050 for ( int zLevel : constSymbolZLevels )
1051 {
1052 zLevelsToNormLevels[zLevel] = ++maxNormLevel;
1053 mRenderQueue.append( RenderLevel( zLevel ) );
1054 QgsDebugMsgLevel( u"zLevel %1 -> %2"_s.arg( zLevel ).arg( maxNormLevel ), 4 );
1055 }
1056
1057 mRootRule->setNormZLevels( zLevelsToNormLevels );
1058}
1059
1061{
1062 return !mRootRule->hasActiveChildren();
1063}
1064
1066{
1068
1069 //
1070 // do the actual rendering
1071 //
1072
1073 // go through all levels
1074 if ( !context.renderingStopped() )
1075 {
1076 const auto constMRenderQueue = mRenderQueue;
1077 for ( const RenderLevel &level : constMRenderQueue )
1078 {
1079 //QgsDebugMsgLevel(QString("level %1").arg(level.zIndex), 2);
1080 // go through all jobs at the level
1081 for ( const RenderJob *job : std::as_const( level.jobs ) )
1082 {
1083 context.expressionContext().setFeature( job->ftr.feat );
1084 //QgsDebugMsgLevel(QString("job fid %1").arg(job->f->id()), 2);
1085 // render feature - but only with symbol layers with specified zIndex
1086 QgsSymbol *s = job->symbol;
1087 int count = s->symbolLayerCount();
1088 for ( int i = 0; i < count; i++ )
1089 {
1090 // TODO: better solution for this
1091 // renderFeatureWithSymbol asks which symbol layer to draw
1092 // but there are multiple transforms going on!
1093 if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
1094 {
1095 int flags = job->ftr.flags;
1096 renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
1097 }
1098 }
1099 }
1100 }
1101 }
1102
1103 // clean current features
1104 mCurrentFeatures.clear();
1105
1106 // clean render queue
1107 mRenderQueue.clear();
1108
1109 // clean up rules from temporary stuff
1110 mRootRule->stopRender( context );
1111}
1112
1114{
1115 return mFilter;
1116}
1117
1118QSet<QString> QgsRuleBasedRenderer::usedAttributes( const QgsRenderContext &context ) const
1119{
1120 return mRootRule->usedAttributes( context );
1121}
1122
1124{
1125 return mRootRule->needsGeometry();
1126}
1127
1129{
1130 QgsRuleBasedRenderer::Rule *clonedRoot = mRootRule->clone();
1131
1132 // normally with clone() the individual rules get new keys (UUID), but here we want to keep
1133 // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
1134 clonedRoot->setRuleKey( mRootRule->ruleKey() );
1135 RuleList origDescendants = mRootRule->descendants();
1136 RuleList clonedDescendants = clonedRoot->descendants();
1137 Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
1138 for ( int i = 0; i < origDescendants.count(); ++i )
1139 clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
1140
1141 QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( clonedRoot );
1142
1143 copyRendererData( r );
1144 return r;
1145}
1146
1147void QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1148{
1149 QgsSldExportContext context;
1150 context.setExtraProperties( props );
1151 toSld( doc, element, context );
1152}
1153
1154bool QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1155{
1156 return mRootRule->toSld( doc, element, context );
1157}
1158
1159// TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
1161{
1162 return mRootRule->symbols( context );
1163}
1164
1165QDomElement QgsRuleBasedRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
1166{
1167 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1168 rendererElem.setAttribute( u"type"_s, u"RuleRenderer"_s );
1169
1171
1172 QDomElement rulesElem = mRootRule->save( doc, symbols );
1173 rulesElem.setTagName( u"rules"_s ); // instead of just "rule"
1174 rendererElem.appendChild( rulesElem );
1175
1176 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, u"symbols"_s, doc, context );
1177 rendererElem.appendChild( symbolsElem );
1178
1179 saveRendererData( doc, rendererElem, context );
1180
1181 return rendererElem;
1182}
1183
1185{
1186 return true;
1187}
1188
1190{
1191 Rule *rule = mRootRule->findRuleByKey( key );
1192 return rule ? rule->active() : true;
1193}
1194
1195void QgsRuleBasedRenderer::checkLegendSymbolItem( const QString &key, bool state )
1196{
1197 Rule *rule = mRootRule->findRuleByKey( key );
1198 if ( rule )
1199 rule->setActive( state );
1200}
1201
1202QString QgsRuleBasedRenderer::legendKeyToExpression( const QString &key, QgsVectorLayer *, bool &ok ) const
1203{
1204 ok = false;
1205 Rule *rule = mRootRule->findRuleByKey( key );
1206 if ( !rule )
1207 return QString();
1208
1209 std::function<QString( Rule * rule )> ruleToExpression;
1210 ruleToExpression = [&ruleToExpression]( Rule *rule ) -> QString {
1211 if ( rule->isElse() && rule->parent() )
1212 {
1213 // gather the expressions for all other rules on this level and invert them
1214
1215 QStringList otherRules;
1216 const QList<QgsRuleBasedRenderer::Rule *> siblings = rule->parent()->children();
1217 for ( Rule *sibling : siblings )
1218 {
1219 if ( sibling == rule || sibling->isElse() )
1220 continue;
1221
1222 const QString siblingExpression = ruleToExpression( sibling );
1223 if ( siblingExpression.isEmpty() )
1224 return u"FALSE"_s; // nothing will match this rule
1225
1226 otherRules.append( siblingExpression );
1227 }
1228
1229 if ( otherRules.empty() )
1230 return u"TRUE"_s; // all features will match the else rule
1231 else
1232 return ( otherRules.size() > 1 ? u"NOT ((%1))"_s.arg( otherRules.join( ") OR ("_L1 ) ) : u"NOT (%1)"_s.arg( otherRules.at( 0 ) ) );
1233 }
1234 else
1235 {
1236 QStringList ruleParts;
1237 if ( !rule->filterExpression().isEmpty() )
1238 ruleParts.append( rule->filterExpression() );
1239
1240 if ( !qgsDoubleNear( rule->minimumScale(), 0.0 ) )
1241 ruleParts.append( u"@map_scale <= %1"_s.arg( rule->minimumScale() ) );
1242
1243 if ( !qgsDoubleNear( rule->maximumScale(), 0.0 ) )
1244 ruleParts.append( u"@map_scale >= %1"_s.arg( rule->maximumScale() ) );
1245
1246 if ( !ruleParts.empty() )
1247 {
1248 return ( ruleParts.size() > 1 ? u"(%1)"_s.arg( ruleParts.join( ") AND ("_L1 ) ) : ruleParts.at( 0 ) );
1249 }
1250 else
1251 {
1252 return QString();
1253 }
1254 }
1255 };
1256
1257 QStringList parts;
1258 while ( rule )
1259 {
1260 const QString ruleFilter = ruleToExpression( rule );
1261 if ( !ruleFilter.isEmpty() )
1262 parts.append( ruleFilter );
1263
1264 rule = rule->parent();
1265 }
1266
1267 ok = true;
1268 return parts.empty() ? u"TRUE"_s : ( parts.size() > 1 ? u"(%1)"_s.arg( parts.join( ") AND ("_L1 ) ) : parts.at( 0 ) );
1269}
1270
1271void QgsRuleBasedRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
1272{
1273 Rule *rule = mRootRule->findRuleByKey( key );
1274 if ( rule )
1275 rule->setSymbol( symbol );
1276 else
1277 delete symbol;
1278}
1279
1280void QgsRuleBasedRenderer::setLegendSymbolItemLabel( const QString &key, const QString &label )
1281{
1282 Rule *rule = mRootRule->findRuleByKey( key );
1283 if ( rule )
1284 rule->setLabel( label );
1285}
1286
1288{
1289 return mRootRule->legendSymbolItems();
1290}
1291
1292
1294{
1295 // load symbols
1296 QDomElement symbolsElem = element.firstChildElement( u"symbols"_s );
1297 if ( symbolsElem.isNull() )
1298 return nullptr;
1299
1300 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
1301
1302 QDomElement rulesElem = element.firstChildElement( u"rules"_s );
1303
1304 Rule *root = Rule::create( rulesElem, symbolMap, true, context );
1305 if ( !root )
1306 return nullptr;
1307
1309
1310 // delete symbols if there are any more
1312
1313 return r;
1314}
1315
1317{
1318 // retrieve child rules
1319 Rule *root = nullptr;
1320
1321 QDomElement ruleElem = element.firstChildElement( u"Rule"_s );
1322 while ( !ruleElem.isNull() )
1323 {
1324 Rule *child = Rule::createFromSld( ruleElem, geomType );
1325 if ( child )
1326 {
1327 // create the root rule if not done before
1328 if ( !root )
1329 root = new Rule( nullptr );
1330
1331 root->appendChild( child );
1332 }
1333
1334 ruleElem = ruleElem.nextSiblingElement( u"Rule"_s );
1335 }
1336
1337 if ( !root )
1338 {
1339 // no valid rules was found
1340 return nullptr;
1341 }
1342
1343 // create and return the new renderer
1344 return new QgsRuleBasedRenderer( root );
1345}
1346
1349
1351{
1352 QString attr = r->classAttribute();
1353 // categorizedAttr could be either an attribute name or an expression.
1354 // the only way to differentiate is to test it as an expression...
1355 QgsExpression testExpr( attr );
1356 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1357 {
1358 //not an expression, so need to quote column name
1359 attr = QgsExpression::quotedColumnRef( attr );
1360 }
1361
1362 const auto constCategories = r->categories();
1363 for ( const QgsRendererCategory &cat : constCategories )
1364 {
1365 QString value;
1366 // not quoting numbers saves a type cast
1367 if ( QgsVariantUtils::isNull( cat.value() ) )
1368 value = "NULL";
1369 else if ( cat.value().userType() == QMetaType::Type::Int )
1370 value = cat.value().toString();
1371 else if ( cat.value().userType() == QMetaType::Type::Double )
1372 // we loose precision here - so we may miss some categories :-(
1373 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1374 value = QString::number( cat.value().toDouble(), 'f', 4 );
1375 else
1376 value = QgsExpression::quotedString( cat.value().toString() );
1377 const QString filter = u"%1 %2 %3"_s.arg( attr, QgsVariantUtils::isNull( cat.value() ) ? u"IS"_s : u"="_s, value );
1378 const QString label = !cat.label().isEmpty() ? cat.label() : cat.value().isValid() ? value : QString();
1379 initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1380 }
1381}
1382
1384{
1385 QString attr = r->classAttribute();
1386 // categorizedAttr could be either an attribute name or an expression.
1387 // the only way to differentiate is to test it as an expression...
1388 QgsExpression testExpr( attr );
1389 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1390 {
1391 //not an expression, so need to quote column name
1392 attr = QgsExpression::quotedColumnRef( attr );
1393 }
1394 else if ( !testExpr.isField() )
1395 {
1396 //otherwise wrap expression in brackets
1397 attr = u"(%1)"_s.arg( attr );
1398 }
1399
1400 bool firstRange = true;
1401 const auto constRanges = r->ranges();
1402 for ( const QgsRendererRange &rng : constRanges )
1403 {
1404 // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1405 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1406 QString filter = u"%1 %2 %3 AND %1 <= %4"_s.arg( attr, firstRange ? u">="_s : u">"_s, QString::number( rng.lowerValue(), 'f', 4 ), QString::number( rng.upperValue(), 'f', 4 ) );
1407 firstRange = false;
1408 QString label = rng.label().isEmpty() ? filter : rng.label();
1409 initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1410 }
1411}
1412
1414{
1415 std::sort( scales.begin(), scales.end() ); // make sure the scales are in ascending order
1416 double oldScale = initialRule->maximumScale();
1417 double maxDenom = initialRule->minimumScale();
1418 QgsSymbol *symbol = initialRule->symbol();
1419 const auto constScales = scales;
1420 for ( int scale : constScales )
1421 {
1422 if ( initialRule->maximumScale() >= scale )
1423 continue; // jump over the first scales out of the interval
1424 if ( maxDenom != 0 && maxDenom <= scale )
1425 break; // ignore the latter scales out of the interval
1426 initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), u"%1 - %2"_s.arg( oldScale ).arg( scale ) ) );
1427 oldScale = scale;
1428 }
1429 // last rule
1430 initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), u"%1 - %2"_s.arg( oldScale ).arg( maxDenom ) ) );
1431}
1432
1434{
1435 QString msg( u"Rule-based renderer:\n"_s );
1436 msg += mRootRule->dump();
1437 return msg;
1438}
1439
1441{
1442 return mRootRule->willRenderFeature( feature, &context );
1443}
1444
1446{
1447 return mRootRule->symbolsForFeature( feature, &context );
1448}
1449
1451{
1452 return mRootRule->symbolsForFeature( feature, &context );
1453}
1454
1455QSet< QString > QgsRuleBasedRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
1456{
1457 return mRootRule->legendKeysForFeature( feature, &context );
1458}
1459
1461{
1462 return mRootRule->accept( visitor );
1463}
1464
1466{
1467 std::unique_ptr< QgsRuleBasedRenderer > r;
1468 if ( renderer->type() == "RuleRenderer"_L1 )
1469 {
1470 r.reset( dynamic_cast<QgsRuleBasedRenderer *>( renderer->clone() ) );
1471 }
1472 else if ( renderer->type() == "singleSymbol"_L1 )
1473 {
1474 const QgsSingleSymbolRenderer *singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRenderer *>( renderer );
1475 if ( !singleSymbolRenderer )
1476 return nullptr;
1477
1478 std::unique_ptr< QgsSymbol > origSymbol( singleSymbolRenderer->symbol()->clone() );
1479 r = std::make_unique< QgsRuleBasedRenderer >( origSymbol.release() );
1480 }
1481 else if ( renderer->type() == "categorizedSymbol"_L1 )
1482 {
1483 const QgsCategorizedSymbolRenderer *categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRenderer *>( renderer );
1484 if ( !categorizedRenderer )
1485 return nullptr;
1486
1487 QString attr = categorizedRenderer->classAttribute();
1488 // categorizedAttr could be either an attribute name or an expression.
1489 bool isField = false;
1490 if ( layer )
1491 {
1492 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1493 }
1494 else
1495 {
1496 QgsExpression testExpr( attr );
1497 isField = testExpr.hasParserError() || testExpr.isField();
1498 }
1499 if ( isField && !attr.contains( '\"' ) )
1500 {
1501 //not an expression, so need to quote column name
1502 attr = QgsExpression::quotedColumnRef( attr );
1503 }
1504
1505 auto rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1506
1507 QString expression;
1508 QString value;
1509 QgsRendererCategory category;
1510 for ( const QgsRendererCategory &category : categorizedRenderer->categories() )
1511 {
1512 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1513
1514 rule->setLabel( category.label() );
1515
1516 //We first define the rule corresponding to the category
1517 if ( category.value().userType() == QMetaType::Type::QVariantList )
1518 {
1519 QStringList values;
1520 const QVariantList list = category.value().toList();
1521 for ( const QVariant &v : list )
1522 {
1523 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1524 if ( QVariant( v ).convert( QMetaType::Type::Double ) )
1525 {
1526 values << v.toString();
1527 }
1528 else
1529 {
1530 values << QgsExpression::quotedString( v.toString() );
1531 }
1532 }
1533
1534 if ( values.empty() )
1535 {
1536 expression = u"ELSE"_s;
1537 }
1538 else
1539 {
1540 expression = u"%1 IN (%2)"_s.arg( attr, values.join( ',' ) );
1541 }
1542 }
1543 else
1544 {
1545 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1546 if ( category.value().convert( QMetaType::Type::Double ) )
1547 {
1548 value = category.value().toString();
1549 }
1550 else
1551 {
1552 value = QgsExpression::quotedString( category.value().toString() );
1553 }
1554
1555 //An empty category is equivalent to the ELSE keyword
1556 if ( value == "''"_L1 )
1557 {
1558 expression = u"ELSE"_s;
1559 }
1560 else
1561 {
1562 expression = u"%1 = %2"_s.arg( attr, value );
1563 }
1564 }
1565 rule->setFilterExpression( expression );
1566
1567 //Then we construct an equivalent symbol.
1568 //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1569 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1570
1571 std::unique_ptr< QgsSymbol > origSymbol( category.symbol()->clone() );
1572 rule->setSymbol( origSymbol.release() );
1573
1574 rootrule->appendChild( rule.release() );
1575 }
1576
1577 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1578 }
1579 else if ( renderer->type() == "graduatedSymbol"_L1 )
1580 {
1581 const QgsGraduatedSymbolRenderer *graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1582 if ( !graduatedRenderer )
1583 return nullptr;
1584
1585 QString attr = graduatedRenderer->classAttribute();
1586 // categorizedAttr could be either an attribute name or an expression.
1587 // the only way to differentiate is to test it as an expression...
1588 bool isField = false;
1589 if ( layer )
1590 {
1591 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1592 }
1593 else
1594 {
1595 QgsExpression testExpr( attr );
1596 isField = testExpr.hasParserError() || testExpr.isField();
1597 }
1598 if ( isField && !attr.contains( '\"' ) )
1599 {
1600 //not an expression, so need to quote column name
1601 attr = QgsExpression::quotedColumnRef( attr );
1602 }
1603 else if ( !isField )
1604 {
1605 //otherwise wrap expression in brackets
1606 attr = u"(%1)"_s.arg( attr );
1607 }
1608
1609 auto rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1610
1611 QString expression;
1612 const QgsRangeList ranges = graduatedRenderer->ranges();
1613 const bool isInverted = ranges.size() > 1 ? ranges.at( 0 ).upperValue() > ranges.at( 1 ).upperValue() : false;
1614 QgsRendererRange range;
1615 for ( int i = 0; i < ranges.size(); ++i )
1616 {
1617 range = ranges.value( i );
1618 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1619
1620 rule->setLabel( range.label() );
1621 const QString upperValue = QString::number( range.upperValue(), 'f', 16 );
1622 const QString lowerValue = QString::number( range.lowerValue(), 'f', 16 );
1623 // Lower and upper boundaries have to be open-ended
1624 if ( i == 0 )
1625 {
1626 expression = !isInverted ? u"%1 <= %2"_s.arg( attr, upperValue ) : u"%1 > %2"_s.arg( attr, lowerValue );
1627 }
1628 else if ( i == ranges.size() - 1 )
1629 {
1630 expression = !isInverted ? u"%1 > %2"_s.arg( attr, lowerValue ) : u"%1 <= %2"_s.arg( attr, upperValue );
1631 }
1632 else
1633 {
1634 expression = attr + " > " + lowerValue + " AND " + attr + " <= " + upperValue;
1635 }
1636 rule->setFilterExpression( expression );
1637
1638 //Then we construct an equivalent symbol.
1639 //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1640 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1641
1642 std::unique_ptr< QgsSymbol > symbol( range.symbol()->clone() );
1643 rule->setSymbol( symbol.release() );
1644
1645 rootrule->appendChild( rule.release() );
1646 }
1647
1648 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1649 }
1650 else if ( renderer->type() == "pointDisplacement"_L1 || renderer->type() == "pointCluster"_L1 )
1651 {
1652 if ( const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer ) )
1653 return convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1654 }
1655 else if ( renderer->type() == "invertedPolygonRenderer"_L1 )
1656 {
1657 if ( const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer ) )
1658 r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1659 }
1660 else if ( renderer->type() == "mergedFeatureRenderer"_L1 )
1661 {
1662 if ( const QgsMergedFeatureRenderer *mergedRenderer = dynamic_cast<const QgsMergedFeatureRenderer *>( renderer ) )
1663 r.reset( convertFromRenderer( mergedRenderer->embeddedRenderer() ) );
1664 }
1665 else if ( renderer->type() == "embeddedSymbol"_L1 && layer )
1666 {
1667 const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
1668
1669 auto rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1670
1673 req.setNoAttributes();
1674 QgsFeatureIterator it = layer->getFeatures( req );
1675 QgsFeature feature;
1676 while ( it.nextFeature( feature ) && rootrule->children().size() < 500 )
1677 {
1678 if ( feature.embeddedSymbol() )
1679 {
1680 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1681 rule->setFilterExpression( u"$id=%1"_s.arg( feature.id() ) );
1682 rule->setLabel( QString::number( feature.id() ) );
1683 rule->setSymbol( feature.embeddedSymbol()->clone() );
1684 rootrule->appendChild( rule.release() );
1685 }
1686 }
1687
1688 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1689 rule->setFilterExpression( u"ELSE"_s );
1690 rule->setLabel( QObject::tr( "All other features" ) );
1691 rule->setSymbol( embeddedRenderer->defaultSymbol()->clone() );
1692 rootrule->appendChild( rule.release() );
1693
1694 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1695 }
1696
1697 if ( r )
1698 {
1699 renderer->copyRendererData( r.get() );
1700 }
1701
1702 return r.release();
1703}
1704
1705void QgsRuleBasedRenderer::convertToDataDefinedSymbology( QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField )
1706{
1707 QString sizeExpression;
1708 switch ( symbol->type() )
1709 {
1711 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1712 {
1713 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( symbol->symbolLayer( j ) );
1714 if ( !sizeScaleField.isEmpty() )
1715 {
1716 sizeExpression = u"%1*(%2)"_s.arg( msl->size() ).arg( sizeScaleField );
1718 }
1719 if ( !rotationField.isEmpty() )
1720 {
1722 }
1723 }
1724 break;
1726 if ( !sizeScaleField.isEmpty() )
1727 {
1728 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1729 {
1730 if ( symbol->symbolLayer( j )->layerType() == "SimpleLine"_L1 )
1731 {
1732 QgsLineSymbolLayer *lsl = static_cast<QgsLineSymbolLayer *>( symbol->symbolLayer( j ) );
1733 sizeExpression = u"%1*(%2)"_s.arg( lsl->width() ).arg( sizeScaleField );
1735 }
1736 if ( symbol->symbolLayer( j )->layerType() == "MarkerLine"_L1 )
1737 {
1738 QgsSymbol *marker = symbol->symbolLayer( j )->subSymbol();
1739 for ( int k = 0; k < marker->symbolLayerCount(); ++k )
1740 {
1741 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( marker->symbolLayer( k ) );
1742 sizeExpression = u"%1*(%2)"_s.arg( msl->size() ).arg( sizeScaleField );
1744 }
1745 }
1746 }
1747 }
1748 break;
1749 default:
1750 break;
1751 }
1752}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2359
@ EmbeddedSymbols
Retrieve any embedded feature symbology.
Definition qgis.h:2364
QFlags< FeatureRendererFlag > FeatureRendererFlags
Flags controlling behavior of vector feature renderers.
Definition qgis.h:900
@ AffectsLabeling
If present, indicates that the renderer will participate in the map labeling problem.
Definition qgis.h:891
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:379
@ Point
Points.
Definition qgis.h:380
@ Line
Lines.
Definition qgis.h:381
@ Polygon
Polygons.
Definition qgis.h:382
@ Marker
Marker symbol.
Definition qgis.h:652
@ Line
Line symbol.
Definition qgis.h:653
@ AffectsLabeling
If present, indicates that the symbol will participate in the map labeling problem.
Definition qgis.h:911
A feature renderer which represents features using a list of renderer categories.
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.
Handles 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)
Fetch next feature and stores in f, returns true on success.
QgsFeatureRenderer(const QString &type)
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.
void saveRendererData(QDomDocument &doc, QDomElement &element, const QgsReadWriteContext &context)
Saves generic renderer data into the specified element.
void renderFeatureWithSymbol(const QgsFeature &feature, QgsSymbol *symbol, QgsRenderContext &context, int layer, bool selected, bool drawVertexMarker)
Render the feature with the symbol using context.
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.
virtual QgsFeatureRenderer * clone() const =0
Create a deep copy of this renderer.
Wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(Qgis::FeatureRequestFlags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setNoAttributes()
Set that no attributes will be fetched.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition qgsfeature.h:60
QgsFeatureId id
Definition qgsfeature.h:63
const QgsSymbol * embeddedSymbol() const
Returns the feature's embedded symbology, or nullptr if the feature has no embedded symbol.
Container of fields for a vector layer.
Definition qgsfields.h:46
A fill symbol type, for rendering Polygon and MultiPolygon geometries.
A vector feature renderer which uses numeric attributes to classify features into different ranges.
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.
A polygon-only feature renderer used to display features inverted.
Stores information about one class/rule of a vector layer renderer in a unified way that can be used ...
Abstract base class for line symbol layers.
virtual double width() const
Returns the estimated width for the line symbol layer.
A line symbol type, for rendering LineString and MultiLineString geometries.
Abstract base class for marker symbol layers.
double size() const
Returns the symbol size.
A marker symbol type, for rendering Point and MultiPoint geometries.
A polygon or line-only feature renderer used to render a set of features merged (or dissolved) into a...
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...
virtual QString translate(const QString &context, const QString &sourceText, const char *disambiguation=nullptr, int n=-1) const =0
Translates a string using the Qt QTranslator mechanism.
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.
A container for the context for various read/write operations on objects.
const QgsProjectTranslator * projectTranslator() const
Returns the project translator.
const QString currentLayerId() const
Returns the currently used layer id as string.
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...
Represents a value range for a QgsGraduatedSymbolRenderer.
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.
Represents an individual rule for a 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
static QgsRuleBasedRenderer::Rule * create(QDomElement &ruleElem, QgsSymbolMap &symbolMap, bool reuseId=true, const QgsReadWriteContext &context=QgsReadWriteContext())
Create a rule from an XML definition.
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.
@ Inactive
The rule is inactive.
@ Filtered
The rule does not apply.
@ 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
Returns a list of the symbols used by this rule and all children of this rule.
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.
QgsExpression * filter() const
A filter that will check if this rule applies.
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.
QString description() const
A human readable description for this rule.
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.
void setLabel(const QString &label)
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)
QString filterExpression() const
A filter that will check if this rule applies.
bool active() const
Returns if this rule is active.
Q_DECL_DEPRECATED void toSld(QDomDocument &doc, QDomElement &element, QVariantMap props) const
Saves the symbol layer as SLD.
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
Returns true if the legend symbology item with the specified key 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
Sets whether the legend symbology item with the specified ley should be 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
Returns true if symbology items in legend are 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...
std::unique_ptr< Rule > mRootRule
the root node with hierarchical list of rules
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.
void setLegendSymbolItemLabel(const QString &key, const QString &label) override
Sets the label for a legend symbol item.
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.
Qgis::FeatureRendererFlags flags() const override
Returns flags associated with the renderer.
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.
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...
bool renderFeature(const QgsFeature &feature, QgsRenderContext &context, int layer=-1, bool selected=false, bool drawVertexMarker=false) override
Render a feature using this renderer in the given context.
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.
Q_DECL_DEPRECATED 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.
static bool equalToOrGreaterThanMinimumScale(const double scale, const double minScale)
Returns whether the scale is equal to or greater than the minScale, taking non-round numbers into acc...
static bool lessThanMaximumScale(const double scale, const double maxScale)
Returns whether the scale is less than the maxScale, taking non-round numbers into account.
A feature renderer which renders all features with the same symbol.
QgsSymbol * symbol() const
Returns the symbol which will be rendered for every feature.
Holds SLD export options and other information related to SLD export of a QGIS layer style.
void setExtraProperties(const QVariantMap &properties)
Sets the open ended set of properties that can drive/inform the SLD encoding.
QVariantMap extraProperties() const
Returns the open ended set of properties that can drive/inform the SLD encoding.
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:1462
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 Q_DECL_DEPRECATED bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
Creates an OGC function element.
static bool hasSldSymbolizer(const QDomElement &element)
Returns true if a DOM element contains an SLD Symbolizer element.
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.
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:227
QgsSymbolLayer * symbolLayer(int layer)
Returns the symbol layer at the specified index.
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:357
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:296
static bool isNull(const QVariant &variant, bool silenceNullWarnings=false)
Returns true if the specified variant should be considered a NULL value.
Represents a vector layer which manages a vector based dataset.
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:7653
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7417
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:80
#define QgsDebugError(str)
Definition qgslogger.h:71
#define RENDERER_TAG_NAME
Definition qgsrenderer.h:57
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition qgsrenderer.h:52
QList< QgsSymbol * > QgsSymbolList
Definition qgsrenderer.h:51
QList< QgsRendererRange > QgsRangeList
QList< QgsSymbolLayer * > QgsSymbolLayerList
Definition qgssymbol.h:30
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.