QGIS API Documentation 3.99.0-Master (2fe06baccd8)
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 <QUuid>
41
42QgsRuleBasedRenderer::Rule::Rule( QgsSymbol *symbol, int scaleMinDenom, int scaleMaxDenom, const QString &filterExp, const QString &label, const QString &description, bool elseRule )
43 : mSymbol( symbol )
44 , mMaximumScale( scaleMinDenom )
45 , mMinimumScale( scaleMaxDenom )
46 , mFilterExp( filterExp )
47 , mLabel( label )
48 , mDescription( description )
49 , mElseRule( elseRule )
50{
51 if ( mElseRule )
52 mFilterExp = QStringLiteral( "ELSE" );
53
54 mRuleKey = QUuid::createUuid().toString();
55 initFilter();
56}
57
59{
60 qDeleteAll( mChildren );
61 // do NOT delete parent
62}
63
65{
66 if ( mFilterExp.trimmed().compare( QLatin1String( "ELSE" ), Qt::CaseInsensitive ) == 0 )
67 {
68 mElseRule = true;
69 mFilter.reset();
70 }
71 else if ( mFilterExp.trimmed().isEmpty() )
72 {
73 mElseRule = false;
74 mFilter.reset();
75 }
76 else
77 {
78 mElseRule = false;
79 mFilter = std::make_unique< QgsExpression >( mFilterExp );
80 }
81}
82
84{
85 mChildren.append( rule );
86 rule->mParent = this;
87 updateElseRules();
88}
89
91{
92 mChildren.insert( i, rule );
93 rule->mParent = this;
94 updateElseRules();
95}
96
98{
99 mChildren.removeAll( rule );
100 delete rule;
101 updateElseRules();
102}
103
105{
106 delete mChildren.takeAt( i );
107 updateElseRules();
108}
109
111{
112 mChildren.removeAll( rule );
113 rule->mParent = nullptr;
114 updateElseRules();
115 return rule;
116}
117
119{
120 Rule *rule = mChildren.takeAt( i );
121 rule->mParent = nullptr;
122 updateElseRules();
123 return rule;
124}
125
127{
128 // we could use a hash / map for search if this will be slow...
129
130 if ( key == mRuleKey )
131 return this;
132
133 const auto constMChildren = mChildren;
134 for ( Rule *rule : constMChildren )
135 {
136 Rule *r = rule->findRuleByKey( key );
137 if ( r )
138 return r;
139 }
140 return nullptr;
141}
142
143void QgsRuleBasedRenderer::Rule::updateElseRules()
144{
145 mElseRules.clear();
146 const auto constMChildren = mChildren;
147 for ( Rule *rule : constMChildren )
148 {
149 if ( rule->isElse() )
150 mElseRules << rule;
151 }
152}
153
155{
156 mFilterExp = QStringLiteral( "ELSE" );
157 mElseRule = iselse;
158 mFilter.reset();
159}
160
162{
163 // NOTE: if visitEnter returns false it means "don't visit the rule", not "abort all further visitations"
165 return true;
166
167 if ( mSymbol )
168 {
169 QgsStyleSymbolEntity entity( mSymbol.get() );
170 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
171 return false;
172 }
173
174 if ( !mChildren.empty() )
175 {
176 for ( const Rule *rule : mChildren )
177 {
178
179 if ( !rule->accept( visitor ) )
180 return false;
181 }
182 }
183
185 return false;
186
187 return true;
188}
189
190QString QgsRuleBasedRenderer::Rule::dump( int indent ) const
191{
192 QString off;
193 off.fill( QChar( ' ' ), indent );
194 QString symbolDump = ( mSymbol ? mSymbol->dump() : QStringLiteral( "[]" ) );
195 QString msg = off + QStringLiteral( "RULE %1 - scale [%2,%3] - filter %4 - symbol %5\n" )
196 .arg( mLabel ).arg( mMaximumScale ).arg( mMinimumScale )
197 .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( QStringLiteral( "rule" ) );
327
328 if ( mSymbol )
329 {
330 int symbolIndex = symbolMap.size();
331 symbolMap[QString::number( symbolIndex )] = mSymbol.get();
332 ruleElem.setAttribute( QStringLiteral( "symbol" ), symbolIndex );
333 }
334 if ( !mFilterExp.isEmpty() )
335 ruleElem.setAttribute( QStringLiteral( "filter" ), mFilterExp );
336 if ( mMaximumScale != 0 )
337 ruleElem.setAttribute( QStringLiteral( "scalemindenom" ), mMaximumScale );
338 if ( mMinimumScale != 0 )
339 ruleElem.setAttribute( QStringLiteral( "scalemaxdenom" ), mMinimumScale );
340 if ( !mLabel.isEmpty() )
341 ruleElem.setAttribute( QStringLiteral( "label" ), mLabel );
342 if ( !mDescription.isEmpty() )
343 ruleElem.setAttribute( QStringLiteral( "description" ), mDescription );
344 if ( !mIsActive )
345 ruleElem.setAttribute( QStringLiteral( "checkstate" ), 0 );
346 ruleElem.setAttribute( QStringLiteral( "key" ), 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( QStringLiteral( "filter" ), QString() ).toString();
375 if ( !filter.isEmpty() )
376 filter += QLatin1String( " AND " );
377 filter += mFilterExp;
378 props[ QStringLiteral( "filter" )] = filter;
379 }
380
381 QgsSymbolLayerUtils::mergeScaleDependencies( mMaximumScale, mMinimumScale, props );
382 exportContext.setExtraProperties( props );
383
384 if ( mSymbol )
385 {
386 QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
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( QStringLiteral( "se:Name" ) );
391 nameElem.appendChild( doc.createTextNode( mLabel ) );
392 ruleElem.appendChild( nameElem );
393
394 if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
395 {
396 QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
397 if ( !mLabel.isEmpty() )
398 {
399 QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
400 titleElem.appendChild( doc.createTextNode( mLabel ) );
401 descrElem.appendChild( titleElem );
402 }
403 if ( !mDescription.isEmpty() )
404 {
405 QDomElement abstractElem = doc.createElement( QStringLiteral( "se:Abstract" ) );
406 abstractElem.appendChild( doc.createTextNode( mDescription ) );
407 descrElem.appendChild( abstractElem );
408 }
409 ruleElem.appendChild( descrElem );
410 }
411
412 if ( !props.value( QStringLiteral( "filter" ), QString() ).toString().isEmpty() )
413 {
414 QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, props.value( QStringLiteral( "filter" ), 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( QStringLiteral( "TRUE" ) ) )
480 {
481 sf = QStringLiteral( "TRUE" );
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 {
496 if ( subf.count( ) == 1 )
497 {
498 return subf.at( 0 );
499 }
500 else if ( subf.count( ) == 2 )
501 {
502 return subf.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
503 }
504 else
505 {
506 int midpos = static_cast<int>( subf.length() / 2 );
507 return QStringLiteral( "(%1) OR (%2)" ).arg( bt( subf.mid( 0, midpos ) ), bt( subf.mid( midpos ) ) );
508 }
509 };
510 sf = bt( subfilters );
511 }
512 else
513 {
514 sf = subfilters.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
515 }
516 }
517 }
518
519 // Now join the subfilters with their parent (this) based on if
520 // * The parent is an else rule
521 // * The existence of parent filter and subfilters
522
523 // No filter expression: ELSE rule or catchall rule
524 if ( !mFilter )
525 {
526 if ( mSymbol || sf.isEmpty() )
527 filter = QStringLiteral( "TRUE" );
528 else
529 filter = sf;
530 }
531 else if ( mSymbol )
532 filter = mFilterExp;
533 else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
534 filter = QStringLiteral( "(%1) AND (%2)" ).arg( mFilterExp, sf );
535 else if ( !mFilterExp.trimmed().isEmpty() )
536 filter = mFilterExp;
537 else if ( sf.isEmpty() )
538 filter = QStringLiteral( "TRUE" );
539 else
540 filter = sf;
541
542 filter = filter.trimmed();
543
544 return true;
545}
546
548{
549 return !mActiveChildren.empty();
550}
551
553{
554 QSet<int> symbolZLevelsSet;
555
556 // process this rule
557 if ( mSymbol )
558 {
559 // find out which Z-levels are used
560 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
561 {
562 symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
563 }
564 }
565
566 // process children
567 QList<Rule *>::iterator it;
568 for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
569 {
570 Rule *rule = *it;
571 symbolZLevelsSet.unite( rule->collectZLevels() );
572 }
573 return symbolZLevelsSet;
574}
575
576void QgsRuleBasedRenderer::Rule::setNormZLevels( const QMap<int, int> &zLevelsToNormLevels )
577{
578 if ( mSymbol )
579 {
580 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
581 {
582 int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
583 mSymbolNormZLevels.insert( normLevel );
584 }
585 }
586
587 // prepare list of normalized levels for each rule
588 const auto constMActiveChildren = mActiveChildren;
589 for ( Rule *rule : constMActiveChildren )
590 {
591 rule->setNormZLevels( zLevelsToNormLevels );
592 }
593}
594
595
597{
598 if ( !isFilterOK( featToRender.feat, &context ) )
599 return Filtered;
600
601 bool rendered = false;
602
603 // create job for this feature and this symbol, add to list of jobs
604 if ( mSymbol && mIsActive )
605 {
606 // add job to the queue: each symbol's zLevel must be added
607 const auto constMSymbolNormZLevels = mSymbolNormZLevels;
608 for ( int normZLevel : constMSymbolNormZLevels )
609 {
610 //QgsDebugMsgLevel(QString("add job at level %1").arg(normZLevel),2);
611 renderQueue[normZLevel].jobs.append( new RenderJob( featToRender, mSymbol.get() ) );
612 rendered = true;
613 }
614 }
615
616 bool matchedAChild = false;
617
618 // process children
619 const auto constMChildren = mChildren;
620 for ( Rule *rule : constMChildren )
621 {
622 // Don't process else rules yet
623 if ( !rule->isElse() )
624 {
625 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
626 // consider inactive items as "matched" so the else rule will ignore them
627 matchedAChild |= ( res == Rendered || res == Inactive );
628 rendered |= ( res == Rendered );
629 }
630 }
631
632 // If none of the rules passed then we jump into the else rules and process them.
633 if ( !matchedAChild )
634 {
635 const auto constMElseRules = mElseRules;
636 for ( Rule *rule : constMElseRules )
637 {
638 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
639 matchedAChild |= ( res == Rendered || res == Inactive );
640 rendered |= res == Rendered;
641 }
642 }
643 if ( !mIsActive || ( mSymbol && !rendered ) || ( matchedAChild && !rendered ) )
644 return Inactive;
645 else if ( rendered )
646 return Rendered;
647 else
648 return Filtered;
649}
650
652{
653 if ( !isFilterOK( feature, context ) )
654 return false;
655
656 if ( mSymbol )
657 return true;
658
659 const auto constMActiveChildren = mActiveChildren;
660 for ( Rule *rule : constMActiveChildren )
661 {
662 if ( rule->isElse() )
663 {
664 if ( rule->children().isEmpty() )
665 {
666 RuleList lst = rulesForFeature( feature, context, false );
667 lst.removeOne( rule );
668
669 if ( lst.empty() )
670 {
671 return true;
672 }
673 }
674 else
675 {
676 return rule->willRenderFeature( feature, context );
677 }
678 }
679 else if ( rule->willRenderFeature( feature, context ) )
680 {
681 return true;
682 }
683 }
684 return false;
685}
686
688{
689 QgsSymbolList lst;
690 if ( !isFilterOK( feature, context ) )
691 return lst;
692 if ( mSymbol )
693 lst.append( mSymbol.get() );
694
695 const auto constMActiveChildren = mActiveChildren;
696 for ( Rule *rule : constMActiveChildren )
697 {
698 lst += rule->symbolsForFeature( feature, context );
699 }
700 return lst;
701}
702
704{
705 QSet< QString> res;
706 if ( !isFilterOK( feature, context ) )
707 return res;
708
709 res.insert( mRuleKey );
710
711 // first determine if any non else rules match at this level
712 bool matchedNonElseRule = false;
713 for ( Rule *rule : std::as_const( mActiveChildren ) )
714 {
715 if ( rule->isElse() )
716 {
717 continue;
718 }
719 if ( rule->willRenderFeature( feature, context ) )
720 {
721 res.unite( rule->legendKeysForFeature( feature, context ) );
722 matchedNonElseRule = true;
723 }
724 }
725
726 // second chance -- allow else rules to take effect if valid
727 if ( !matchedNonElseRule )
728 {
729 for ( Rule *rule : std::as_const( mActiveChildren ) )
730 {
731 if ( rule->isElse() )
732 {
733 if ( rule->children().isEmpty() )
734 {
735 RuleList lst = rulesForFeature( feature, context, false );
736 lst.removeOne( rule );
737
738 if ( lst.empty() )
739 {
740 res.unite( rule->legendKeysForFeature( feature, context ) );
741 }
742 }
743 else
744 {
745 res.unite( rule->legendKeysForFeature( feature, context ) );
746 }
747 }
748 }
749 }
750 return res;
751}
752
754{
755 RuleList lst;
756 if ( ! isFilterOK( feature, context ) || ( context && ! isScaleOK( context->rendererScale() ) ) )
757 return lst;
758
759 if ( mSymbol )
760 lst.append( this );
761
762 RuleList listChildren = children();
763 if ( onlyActive )
764 listChildren = mActiveChildren;
765
766 for ( Rule *rule : std::as_const( listChildren ) )
767 {
768 lst += rule->rulesForFeature( feature, context, onlyActive );
769 }
770 return lst;
771}
772
774{
775 if ( mSymbol )
776 mSymbol->stopRender( context );
777
778 const auto constMActiveChildren = mActiveChildren;
779 for ( Rule *rule : constMActiveChildren )
780 {
781 rule->stopRender( context );
782 }
783
784 mActiveChildren.clear();
785 mSymbolNormZLevels.clear();
786}
787
788QgsRuleBasedRenderer::Rule *QgsRuleBasedRenderer::Rule::create( QDomElement &ruleElem, QgsSymbolMap &symbolMap, bool reuseId )
789{
790 QString symbolIdx = ruleElem.attribute( QStringLiteral( "symbol" ) );
791 QgsSymbol *symbol = nullptr;
792 if ( !symbolIdx.isEmpty() )
793 {
794 if ( symbolMap.contains( symbolIdx ) )
795 {
796 symbol = symbolMap.take( symbolIdx );
797 }
798 else
799 {
800 QgsDebugError( "symbol for rule " + symbolIdx + " not found!" );
801 }
802 }
803
804 QString filterExp = ruleElem.attribute( QStringLiteral( "filter" ) );
805 QString label = ruleElem.attribute( QStringLiteral( "label" ) );
806 QString description = ruleElem.attribute( QStringLiteral( "description" ) );
807 int scaleMinDenom = ruleElem.attribute( QStringLiteral( "scalemindenom" ), QStringLiteral( "0" ) ).toInt();
808 int scaleMaxDenom = ruleElem.attribute( QStringLiteral( "scalemaxdenom" ), QStringLiteral( "0" ) ).toInt();
809 QString ruleKey;
810 if ( reuseId )
811 ruleKey = ruleElem.attribute( QStringLiteral( "key" ) );
812 else
813 ruleKey = QUuid::createUuid().toString();
814 Rule *rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
815
816 if ( !ruleKey.isEmpty() )
817 rule->mRuleKey = ruleKey;
818
819 rule->setActive( ruleElem.attribute( QStringLiteral( "checkstate" ), QStringLiteral( "1" ) ).toInt() );
820
821 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
822 while ( !childRuleElem.isNull() )
823 {
824 Rule *childRule = create( childRuleElem, symbolMap );
825 if ( childRule )
826 {
827 rule->appendChild( childRule );
828 }
829 else
830 {
831 QgsDebugError( QStringLiteral( "failed to init a child rule!" ) );
832 }
833 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
834 }
835
836 return rule;
837}
838
840{
841 RuleList l;
842 for ( QgsRuleBasedRenderer::Rule *c : mChildren )
843 {
844 l += c;
845 l += c->descendants();
846 }
847 return l;
848}
849
851{
852 if ( ruleElem.localName() != QLatin1String( "Rule" ) )
853 {
854 QgsDebugError( QStringLiteral( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
855 return nullptr;
856 }
857
858 QString label, description, filterExp;
859 int scaleMinDenom = 0, scaleMaxDenom = 0;
860 QgsSymbolLayerList layers;
861
862 // retrieve the Rule element child nodes
863 QDomElement childElem = ruleElem.firstChildElement();
864 while ( !childElem.isNull() )
865 {
866 if ( childElem.localName() == QLatin1String( "Name" ) )
867 {
868 // <se:Name> tag contains the rule identifier,
869 // so prefer title tag for the label property value
870 if ( label.isEmpty() )
871 label = childElem.firstChild().nodeValue();
872 }
873 else if ( childElem.localName() == QLatin1String( "Description" ) )
874 {
875 // <se:Description> can contains a title and an abstract
876 QDomElement titleElem = childElem.firstChildElement( QStringLiteral( "Title" ) );
877 if ( !titleElem.isNull() )
878 {
879 label = titleElem.firstChild().nodeValue();
880 }
881
882 QDomElement abstractElem = childElem.firstChildElement( QStringLiteral( "Abstract" ) );
883 if ( !abstractElem.isNull() )
884 {
885 description = abstractElem.firstChild().nodeValue();
886 }
887 }
888 else if ( childElem.localName() == QLatin1String( "Abstract" ) )
889 {
890 // <sld:Abstract> (v1.0)
891 description = childElem.firstChild().nodeValue();
892 }
893 else if ( childElem.localName() == QLatin1String( "Title" ) )
894 {
895 // <sld:Title> (v1.0)
896 label = childElem.firstChild().nodeValue();
897 }
898 else if ( childElem.localName() == QLatin1String( "Filter" ) )
899 {
901 if ( filter )
902 {
903 if ( filter->hasParserError() )
904 {
905 QgsDebugError( "parser error: " + filter->parserErrorString() );
906 }
907 else
908 {
909 filterExp = filter->expression();
910 }
911 delete filter;
912 }
913 }
914 else if ( childElem.localName() == QLatin1String( "ElseFilter" ) )
915 {
916 filterExp = QLatin1String( "ELSE" );
917
918 }
919 else if ( childElem.localName() == QLatin1String( "MinScaleDenominator" ) )
920 {
921 bool ok;
922 int v = childElem.firstChild().nodeValue().toInt( &ok );
923 if ( ok )
924 scaleMinDenom = v;
925 }
926 else if ( childElem.localName() == QLatin1String( "MaxScaleDenominator" ) )
927 {
928 bool ok;
929 int v = childElem.firstChild().nodeValue().toInt( &ok );
930 if ( ok )
931 scaleMaxDenom = v;
932 }
933 else if ( childElem.localName().endsWith( QLatin1String( "Symbolizer" ) ) )
934 {
935 // create symbol layers for this symbolizer
936 QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
937 }
938
939 childElem = childElem.nextSiblingElement();
940 }
941
942 // now create the symbol
943 QgsSymbol *symbol = nullptr;
944 if ( !layers.isEmpty() )
945 {
946 switch ( geomType )
947 {
949 symbol = new QgsLineSymbol( layers );
950 break;
951
953 symbol = new QgsFillSymbol( layers );
954 break;
955
957 symbol = new QgsMarkerSymbol( layers );
958 break;
959
960 default:
961 QgsDebugError( QStringLiteral( "invalid geometry type: found %1" ).arg( qgsEnumValueToKey( geomType ) ) );
962 return nullptr;
963 }
964 }
965
966 // and then create and return the new rule
967 return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
968}
969
970
972
974 : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
975 , mRootRule( root )
976{
977}
978
980 : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
981{
982 mRootRule = new Rule( nullptr ); // root has no symbol, no filter etc - just a container
983 mRootRule->appendChild( new Rule( defaultSymbol ) );
984}
985
990
991
993{
994 // not used at all
995 return nullptr;
996}
997
999{
1001
1002 std::function< void( Rule *rule ) > exploreRule;
1003 exploreRule = [&res, &exploreRule]( Rule * rule )
1004 {
1005 if ( !rule )
1006 return;
1007
1008 if ( QgsSymbol *symbol = rule->symbol() )
1009 {
1010 if ( symbol->flags().testFlag( Qgis::SymbolFlag::AffectsLabeling ) )
1012 }
1013
1014 for ( Rule *child : rule->children() )
1015 {
1016 exploreRule( child );
1017 }
1018 };
1019 exploreRule( mRootRule );
1020
1021 return res;
1022}
1023
1025 QgsRenderContext &context,
1026 int layer,
1027 bool selected,
1028 bool drawVertexMarker )
1029{
1030 Q_UNUSED( layer )
1031
1032 int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
1033 mCurrentFeatures.append( FeatureToRender( feature, flags ) );
1034
1035 // check each active rule
1036 return mRootRule->renderFeature( mCurrentFeatures.last(), context, mRenderQueue ) == Rule::Rendered;
1037}
1038
1039
1041{
1042 QgsFeatureRenderer::startRender( context, fields );
1043
1044 // prepare active children
1045 mRootRule->startRender( context, fields, mFilter );
1046
1047 QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
1048 QList<int> symbolZLevels( symbolZLevelsSet.begin(), symbolZLevelsSet.end() );
1049 std::sort( symbolZLevels.begin(), symbolZLevels.end() );
1050
1051 // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
1052 // and prepare rendering queue
1053 QMap<int, int> zLevelsToNormLevels;
1054 int maxNormLevel = -1;
1055 const auto constSymbolZLevels = symbolZLevels;
1056 for ( int zLevel : constSymbolZLevels )
1057 {
1058 zLevelsToNormLevels[zLevel] = ++maxNormLevel;
1059 mRenderQueue.append( RenderLevel( zLevel ) );
1060 QgsDebugMsgLevel( QStringLiteral( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ), 4 );
1061 }
1062
1063 mRootRule->setNormZLevels( zLevelsToNormLevels );
1064}
1065
1067{
1068 return !mRootRule->hasActiveChildren();
1069}
1070
1072{
1074
1075 //
1076 // do the actual rendering
1077 //
1078
1079 // go through all levels
1080 if ( !context.renderingStopped() )
1081 {
1082 const auto constMRenderQueue = mRenderQueue;
1083 for ( const RenderLevel &level : constMRenderQueue )
1084 {
1085 //QgsDebugMsgLevel(QString("level %1").arg(level.zIndex), 2);
1086 // go through all jobs at the level
1087 for ( const RenderJob *job : std::as_const( level.jobs ) )
1088 {
1089 context.expressionContext().setFeature( job->ftr.feat );
1090 //QgsDebugMsgLevel(QString("job fid %1").arg(job->f->id()), 2);
1091 // render feature - but only with symbol layers with specified zIndex
1092 QgsSymbol *s = job->symbol;
1093 int count = s->symbolLayerCount();
1094 for ( int i = 0; i < count; i++ )
1095 {
1096 // TODO: better solution for this
1097 // renderFeatureWithSymbol asks which symbol layer to draw
1098 // but there are multiple transforms going on!
1099 if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
1100 {
1101 int flags = job->ftr.flags;
1102 renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
1103 }
1104 }
1105 }
1106 }
1107 }
1108
1109 // clean current features
1110 mCurrentFeatures.clear();
1111
1112 // clean render queue
1113 mRenderQueue.clear();
1114
1115 // clean up rules from temporary stuff
1116 mRootRule->stopRender( context );
1117}
1118
1120{
1121 return mFilter;
1122}
1123
1124QSet<QString> QgsRuleBasedRenderer::usedAttributes( const QgsRenderContext &context ) const
1125{
1126 return mRootRule->usedAttributes( context );
1127}
1128
1130{
1131 return mRootRule->needsGeometry();
1132}
1133
1135{
1136 QgsRuleBasedRenderer::Rule *clonedRoot = mRootRule->clone();
1137
1138 // normally with clone() the individual rules get new keys (UUID), but here we want to keep
1139 // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
1140 clonedRoot->setRuleKey( mRootRule->ruleKey() );
1141 RuleList origDescendants = mRootRule->descendants();
1142 RuleList clonedDescendants = clonedRoot->descendants();
1143 Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
1144 for ( int i = 0; i < origDescendants.count(); ++i )
1145 clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
1146
1147 QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( clonedRoot );
1148
1149 copyRendererData( r );
1150 return r;
1151}
1152
1153void QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1154{
1155 QgsSldExportContext context;
1156 context.setExtraProperties( props );
1157 toSld( doc, element, context );
1158}
1159
1160bool QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1161{
1162 return mRootRule->toSld( doc, element, context );
1163}
1164
1165// TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
1167{
1168 return mRootRule->symbols( context );
1169}
1170
1171QDomElement QgsRuleBasedRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
1172{
1173 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1174 rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "RuleRenderer" ) );
1175
1177
1178 QDomElement rulesElem = mRootRule->save( doc, symbols );
1179 rulesElem.setTagName( QStringLiteral( "rules" ) ); // instead of just "rule"
1180 rendererElem.appendChild( rulesElem );
1181
1182 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
1183 rendererElem.appendChild( symbolsElem );
1184
1185 saveRendererData( doc, rendererElem, context );
1186
1187 return rendererElem;
1188}
1189
1191{
1192 return true;
1193}
1194
1196{
1197 Rule *rule = mRootRule->findRuleByKey( key );
1198 return rule ? rule->active() : true;
1199}
1200
1201void QgsRuleBasedRenderer::checkLegendSymbolItem( const QString &key, bool state )
1202{
1203 Rule *rule = mRootRule->findRuleByKey( key );
1204 if ( rule )
1205 rule->setActive( state );
1206}
1207
1208QString QgsRuleBasedRenderer::legendKeyToExpression( const QString &key, QgsVectorLayer *, bool &ok ) const
1209{
1210 ok = false;
1211 Rule *rule = mRootRule->findRuleByKey( key );
1212 if ( !rule )
1213 return QString();
1214
1215 std::function<QString( Rule *rule )> ruleToExpression;
1216 ruleToExpression = [&ruleToExpression]( Rule * rule ) -> QString
1217 {
1218 if ( rule->isElse() && rule->parent() )
1219 {
1220 // gather the expressions for all other rules on this level and invert them
1221
1222 QStringList otherRules;
1223 const QList<QgsRuleBasedRenderer::Rule *> siblings = rule->parent()->children();
1224 for ( Rule *sibling : siblings )
1225 {
1226 if ( sibling == rule || sibling->isElse() )
1227 continue;
1228
1229 const QString siblingExpression = ruleToExpression( sibling );
1230 if ( siblingExpression.isEmpty() )
1231 return QStringLiteral( "FALSE" ); // nothing will match this rule
1232
1233 otherRules.append( siblingExpression );
1234 }
1235
1236 if ( otherRules.empty() )
1237 return QStringLiteral( "TRUE" ); // all features will match the else rule
1238 else
1239 return (
1240 otherRules.size() > 1
1241 ? QStringLiteral( "NOT ((%1))" ).arg( otherRules.join( QLatin1String( ") OR (" ) ) )
1242 : QStringLiteral( "NOT (%1)" ).arg( otherRules.at( 0 ) )
1243 );
1244 }
1245 else
1246 {
1247 QStringList ruleParts;
1248 if ( !rule->filterExpression().isEmpty() )
1249 ruleParts.append( rule->filterExpression() );
1250
1251 if ( !qgsDoubleNear( rule->minimumScale(), 0.0 ) )
1252 ruleParts.append( QStringLiteral( "@map_scale <= %1" ).arg( rule->minimumScale() ) );
1253
1254 if ( !qgsDoubleNear( rule->maximumScale(), 0.0 ) )
1255 ruleParts.append( QStringLiteral( "@map_scale >= %1" ).arg( rule->maximumScale() ) );
1256
1257 if ( !ruleParts.empty() )
1258 {
1259 return (
1260 ruleParts.size() > 1
1261 ? QStringLiteral( "(%1)" ).arg( ruleParts.join( QLatin1String( ") AND (" ) ) )
1262 : ruleParts.at( 0 )
1263 );
1264 }
1265 else
1266 {
1267 return QString();
1268 }
1269 }
1270 };
1271
1272 QStringList parts;
1273 while ( rule )
1274 {
1275 const QString ruleFilter = ruleToExpression( rule );
1276 if ( !ruleFilter.isEmpty() )
1277 parts.append( ruleFilter );
1278
1279 rule = rule->parent();
1280 }
1281
1282 ok = true;
1283 return parts.empty() ? QStringLiteral( "TRUE" )
1284 : ( parts.size() > 1
1285 ? QStringLiteral( "(%1)" ).arg( parts.join( QLatin1String( ") AND (" ) ) )
1286 : parts.at( 0 ) );
1287}
1288
1289void QgsRuleBasedRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
1290{
1291 Rule *rule = mRootRule->findRuleByKey( key );
1292 if ( rule )
1293 rule->setSymbol( symbol );
1294 else
1295 delete symbol;
1296}
1297
1299{
1300 return mRootRule->legendSymbolItems();
1301}
1302
1303
1305{
1306 // load symbols
1307 QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
1308 if ( symbolsElem.isNull() )
1309 return nullptr;
1310
1311 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
1312
1313 QDomElement rulesElem = element.firstChildElement( QStringLiteral( "rules" ) );
1314
1315 Rule *root = Rule::create( rulesElem, symbolMap );
1316 if ( !root )
1317 return nullptr;
1318
1320
1321 // delete symbols if there are any more
1323
1324 return r;
1325}
1326
1328{
1329 // retrieve child rules
1330 Rule *root = nullptr;
1331
1332 QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
1333 while ( !ruleElem.isNull() )
1334 {
1335 Rule *child = Rule::createFromSld( ruleElem, geomType );
1336 if ( child )
1337 {
1338 // create the root rule if not done before
1339 if ( !root )
1340 root = new Rule( nullptr );
1341
1342 root->appendChild( child );
1343 }
1344
1345 ruleElem = ruleElem.nextSiblingElement( QStringLiteral( "Rule" ) );
1346 }
1347
1348 if ( !root )
1349 {
1350 // no valid rules was found
1351 return nullptr;
1352 }
1353
1354 // create and return the new renderer
1355 return new QgsRuleBasedRenderer( root );
1356}
1357
1360
1362{
1363 QString attr = r->classAttribute();
1364 // categorizedAttr could be either an attribute name or an expression.
1365 // the only way to differentiate is to test it as an expression...
1366 QgsExpression testExpr( attr );
1367 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1368 {
1369 //not an expression, so need to quote column name
1370 attr = QgsExpression::quotedColumnRef( attr );
1371 }
1372
1373 const auto constCategories = r->categories();
1374 for ( const QgsRendererCategory &cat : constCategories )
1375 {
1376 QString value;
1377 // not quoting numbers saves a type cast
1378 if ( QgsVariantUtils::isNull( cat.value() ) )
1379 value = "NULL";
1380 else if ( cat.value().userType() == QMetaType::Type::Int )
1381 value = cat.value().toString();
1382 else if ( cat.value().userType() == QMetaType::Type::Double )
1383 // we loose precision here - so we may miss some categories :-(
1384 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1385 value = QString::number( cat.value().toDouble(), 'f', 4 );
1386 else
1387 value = QgsExpression::quotedString( cat.value().toString() );
1388 const QString filter = QStringLiteral( "%1 %2 %3" ).arg( attr, QgsVariantUtils::isNull( cat.value() ) ? QStringLiteral( "IS" ) : QStringLiteral( "=" ), value );
1389 const QString label = !cat.label().isEmpty() ? cat.label() :
1390 cat.value().isValid() ? value : QString();
1391 initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1392 }
1393}
1394
1396{
1397 QString attr = r->classAttribute();
1398 // categorizedAttr could be either an attribute name or an expression.
1399 // the only way to differentiate is to test it as an expression...
1400 QgsExpression testExpr( attr );
1401 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1402 {
1403 //not an expression, so need to quote column name
1404 attr = QgsExpression::quotedColumnRef( attr );
1405 }
1406 else if ( !testExpr.isField() )
1407 {
1408 //otherwise wrap expression in brackets
1409 attr = QStringLiteral( "(%1)" ).arg( attr );
1410 }
1411
1412 bool firstRange = true;
1413 const auto constRanges = r->ranges();
1414 for ( const QgsRendererRange &rng : constRanges )
1415 {
1416 // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1417 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1418 QString filter = QStringLiteral( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
1419 QString::number( rng.lowerValue(), 'f', 4 ),
1420 QString::number( rng.upperValue(), 'f', 4 ) );
1421 firstRange = false;
1422 QString label = rng.label().isEmpty() ? filter : rng.label();
1423 initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1424 }
1425}
1426
1428{
1429 std::sort( scales.begin(), scales.end() ); // make sure the scales are in ascending order
1430 double oldScale = initialRule->maximumScale();
1431 double maxDenom = initialRule->minimumScale();
1432 QgsSymbol *symbol = initialRule->symbol();
1433 const auto constScales = scales;
1434 for ( int scale : constScales )
1435 {
1436 if ( initialRule->maximumScale() >= scale )
1437 continue; // jump over the first scales out of the interval
1438 if ( maxDenom != 0 && maxDenom <= scale )
1439 break; // ignore the latter scales out of the interval
1440 initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1441 oldScale = scale;
1442 }
1443 // last rule
1444 initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1445}
1446
1448{
1449 QString msg( QStringLiteral( "Rule-based renderer:\n" ) );
1450 msg += mRootRule->dump();
1451 return msg;
1452}
1453
1455{
1456 return mRootRule->willRenderFeature( feature, &context );
1457}
1458
1460{
1461 return mRootRule->symbolsForFeature( feature, &context );
1462}
1463
1465{
1466 return mRootRule->symbolsForFeature( feature, &context );
1467}
1468
1469QSet< QString > QgsRuleBasedRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
1470{
1471 return mRootRule->legendKeysForFeature( feature, &context );
1472}
1473
1475{
1476 return mRootRule->accept( visitor );
1477}
1478
1480{
1481 std::unique_ptr< QgsRuleBasedRenderer > r;
1482 if ( renderer->type() == QLatin1String( "RuleRenderer" ) )
1483 {
1484 r.reset( dynamic_cast<QgsRuleBasedRenderer *>( renderer->clone() ) );
1485 }
1486 else if ( renderer->type() == QLatin1String( "singleSymbol" ) )
1487 {
1488 const QgsSingleSymbolRenderer *singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRenderer *>( renderer );
1489 if ( !singleSymbolRenderer )
1490 return nullptr;
1491
1492 std::unique_ptr< QgsSymbol > origSymbol( singleSymbolRenderer->symbol()->clone() );
1493 r = std::make_unique< QgsRuleBasedRenderer >( origSymbol.release() );
1494 }
1495 else if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
1496 {
1497 const QgsCategorizedSymbolRenderer *categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRenderer *>( renderer );
1498 if ( !categorizedRenderer )
1499 return nullptr;
1500
1501 QString attr = categorizedRenderer->classAttribute();
1502 // categorizedAttr could be either an attribute name or an expression.
1503 bool isField = false;
1504 if ( layer )
1505 {
1506 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1507 }
1508 else
1509 {
1510 QgsExpression testExpr( attr );
1511 isField = testExpr.hasParserError() || testExpr.isField();
1512 }
1513 if ( isField && !attr.contains( '\"' ) )
1514 {
1515 //not an expression, so need to quote column name
1516 attr = QgsExpression::quotedColumnRef( attr );
1517 }
1518
1519 auto rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1520
1521 QString expression;
1522 QString value;
1523 QgsRendererCategory category;
1524 for ( const QgsRendererCategory &category : categorizedRenderer->categories() )
1525 {
1526 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1527
1528 rule->setLabel( category.label() );
1529
1530 //We first define the rule corresponding to the category
1531 if ( category.value().userType() == QMetaType::Type::QVariantList )
1532 {
1533 QStringList values;
1534 const QVariantList list = category.value().toList();
1535 for ( const QVariant &v : list )
1536 {
1537 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1538 if ( QVariant( v ).convert( QMetaType::Type::Double ) )
1539 {
1540 values << v.toString();
1541 }
1542 else
1543 {
1544 values << QgsExpression::quotedString( v.toString() );
1545 }
1546 }
1547
1548 if ( values.empty() )
1549 {
1550 expression = QStringLiteral( "ELSE" );
1551 }
1552 else
1553 {
1554 expression = QStringLiteral( "%1 IN (%2)" ).arg( attr, values.join( ',' ) );
1555 }
1556 }
1557 else
1558 {
1559 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1560 if ( category.value().convert( QMetaType::Type::Double ) )
1561 {
1562 value = category.value().toString();
1563 }
1564 else
1565 {
1566 value = QgsExpression::quotedString( category.value().toString() );
1567 }
1568
1569 //An empty category is equivalent to the ELSE keyword
1570 if ( value == QLatin1String( "''" ) )
1571 {
1572 expression = QStringLiteral( "ELSE" );
1573 }
1574 else
1575 {
1576 expression = QStringLiteral( "%1 = %2" ).arg( attr, value );
1577 }
1578 }
1579 rule->setFilterExpression( expression );
1580
1581 //Then we construct an equivalent symbol.
1582 //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1583 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1584
1585 std::unique_ptr< QgsSymbol > origSymbol( category.symbol()->clone() );
1586 rule->setSymbol( origSymbol.release() );
1587
1588 rootrule->appendChild( rule.release() );
1589 }
1590
1591 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1592 }
1593 else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1594 {
1595 const QgsGraduatedSymbolRenderer *graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1596 if ( !graduatedRenderer )
1597 return nullptr;
1598
1599 QString attr = graduatedRenderer->classAttribute();
1600 // categorizedAttr could be either an attribute name or an expression.
1601 // the only way to differentiate is to test it as an expression...
1602 bool isField = false;
1603 if ( layer )
1604 {
1605 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1606 }
1607 else
1608 {
1609 QgsExpression testExpr( attr );
1610 isField = testExpr.hasParserError() || testExpr.isField();
1611 }
1612 if ( isField && !attr.contains( '\"' ) )
1613 {
1614 //not an expression, so need to quote column name
1615 attr = QgsExpression::quotedColumnRef( attr );
1616 }
1617 else if ( !isField )
1618 {
1619 //otherwise wrap expression in brackets
1620 attr = QStringLiteral( "(%1)" ).arg( attr );
1621 }
1622
1623 auto rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1624
1625 QString expression;
1626 const QgsRangeList ranges = graduatedRenderer->ranges();
1627 const bool isInverted = ranges.size() > 1 ?
1628 ranges.at( 0 ).upperValue() > ranges.at( 1 ).upperValue() :
1629 false;
1630 QgsRendererRange range;
1631 for ( int i = 0; i < ranges.size(); ++i )
1632 {
1633 range = ranges.value( i );
1634 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1635
1636 rule->setLabel( range.label() );
1637 const QString upperValue = QString::number( range.upperValue(), 'f', 16 );
1638 const QString lowerValue = QString::number( range.lowerValue(), 'f', 16 );
1639 // Lower and upper boundaries have to be open-ended
1640 if ( i == 0 )
1641 {
1642 expression = !isInverted ?
1643 QStringLiteral( "%1 <= %2" ).arg( attr, upperValue ) :
1644 QStringLiteral( "%1 > %2" ).arg( attr, lowerValue );
1645 }
1646 else if ( i == ranges.size() - 1 )
1647 {
1648 expression = !isInverted ?
1649 QStringLiteral( "%1 > %2" ).arg( attr, lowerValue ) :
1650 QStringLiteral( "%1 <= %2" ).arg( attr, upperValue );
1651 }
1652 else
1653 {
1654 expression = attr + " > " + lowerValue + " AND " + \
1655 attr + " <= " + upperValue;
1656 }
1657 rule->setFilterExpression( expression );
1658
1659 //Then we construct an equivalent symbol.
1660 //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1661 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1662
1663 std::unique_ptr< QgsSymbol > symbol( range.symbol()->clone() );
1664 rule->setSymbol( symbol.release() );
1665
1666 rootrule->appendChild( rule.release() );
1667 }
1668
1669 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1670 }
1671 else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1672 {
1673 if ( const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer ) )
1674 return convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1675 }
1676 else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1677 {
1678 if ( const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer ) )
1679 r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1680 }
1681 else if ( renderer->type() == QLatin1String( "mergedFeatureRenderer" ) )
1682 {
1683 if ( const QgsMergedFeatureRenderer *mergedRenderer = dynamic_cast<const QgsMergedFeatureRenderer *>( renderer ) )
1684 r.reset( convertFromRenderer( mergedRenderer->embeddedRenderer() ) );
1685 }
1686 else if ( renderer->type() == QLatin1String( "embeddedSymbol" ) && layer )
1687 {
1688 const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
1689
1690 auto rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1691
1694 req.setNoAttributes();
1695 QgsFeatureIterator it = layer->getFeatures( req );
1696 QgsFeature feature;
1697 while ( it.nextFeature( feature ) && rootrule->children().size() < 500 )
1698 {
1699 if ( feature.embeddedSymbol() )
1700 {
1701 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1702 rule->setFilterExpression( QStringLiteral( "$id=%1" ).arg( feature.id() ) );
1703 rule->setLabel( QString::number( feature.id() ) );
1704 rule->setSymbol( feature.embeddedSymbol()->clone() );
1705 rootrule->appendChild( rule.release() );
1706 }
1707 }
1708
1709 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1710 rule->setFilterExpression( QStringLiteral( "ELSE" ) );
1711 rule->setLabel( QObject::tr( "All other features" ) );
1712 rule->setSymbol( embeddedRenderer->defaultSymbol()->clone() );
1713 rootrule->appendChild( rule.release() );
1714
1715 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1716 }
1717
1718 if ( r )
1719 {
1720 renderer->copyRendererData( r.get() );
1721 }
1722
1723 return r.release();
1724}
1725
1726void QgsRuleBasedRenderer::convertToDataDefinedSymbology( QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField )
1727{
1728 QString sizeExpression;
1729 switch ( symbol->type() )
1730 {
1732 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1733 {
1734 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( symbol->symbolLayer( j ) );
1735 if ( ! sizeScaleField.isEmpty() )
1736 {
1737 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1739 }
1740 if ( ! rotationField.isEmpty() )
1741 {
1743 }
1744 }
1745 break;
1747 if ( ! sizeScaleField.isEmpty() )
1748 {
1749 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1750 {
1751 if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "SimpleLine" ) )
1752 {
1753 QgsLineSymbolLayer *lsl = static_cast<QgsLineSymbolLayer *>( symbol->symbolLayer( j ) );
1754 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
1756 }
1757 if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "MarkerLine" ) )
1758 {
1759 QgsSymbol *marker = symbol->symbolLayer( j )->subSymbol();
1760 for ( int k = 0; k < marker->symbolLayerCount(); ++k )
1761 {
1762 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( marker->symbolLayer( k ) );
1763 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1765 }
1766 }
1767 }
1768 }
1769 break;
1770 default:
1771 break;
1772 }
1773}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
Definition qgis.h:2196
@ EmbeddedSymbols
Retrieve any embedded feature symbology.
Definition qgis.h:2200
QFlags< FeatureRendererFlag > FeatureRendererFlags
Flags controlling behavior of vector feature renderers.
Definition qgis.h:838
@ AffectsLabeling
If present, indicates that the renderer will participate in the map labeling problem.
Definition qgis.h:829
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:358
@ Point
Points.
Definition qgis.h:359
@ Line
Lines.
Definition qgis.h:360
@ Polygon
Polygons.
Definition qgis.h:361
@ Marker
Marker symbol.
Definition qgis.h:611
@ Line
Line symbol.
Definition qgis.h:612
@ AffectsLabeling
If present, indicates that the symbol will participate in the map labeling problem.
Definition qgis.h:849
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:58
QgsFeatureId id
Definition qgsfeature.h:66
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...
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.
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
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.
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.
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...
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.
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:1397
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:231
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:353
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:294
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:6798
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6607
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:61
#define QgsDebugError(str)
Definition qgslogger.h:57
#define RENDERER_TAG_NAME
Definition qgsrenderer.h:55
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition qgsrenderer.h:50
QList< QgsSymbol * > QgsSymbolList
Definition qgsrenderer.h:49
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.