QGIS API Documentation 3.99.0-Master (26c88405ac0)
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 : mParent( nullptr )
44 , mSymbol( symbol )
45 , mMaximumScale( scaleMinDenom )
46 , mMinimumScale( scaleMaxDenom )
47 , mFilterExp( filterExp )
48 , mLabel( label )
49 , mDescription( description )
50 , mElseRule( elseRule )
51{
52 if ( mElseRule )
53 mFilterExp = QStringLiteral( "ELSE" );
54
55 mRuleKey = QUuid::createUuid().toString();
56 initFilter();
57}
58
60{
61 qDeleteAll( mChildren );
62 // do NOT delete parent
63}
64
66{
67 if ( mFilterExp.trimmed().compare( QLatin1String( "ELSE" ), Qt::CaseInsensitive ) == 0 )
68 {
69 mElseRule = true;
70 mFilter.reset();
71 }
72 else if ( mFilterExp.trimmed().isEmpty() )
73 {
74 mElseRule = false;
75 mFilter.reset();
76 }
77 else
78 {
79 mElseRule = false;
80 mFilter = std::make_unique< QgsExpression >( mFilterExp );
81 }
82}
83
85{
86 mChildren.append( rule );
87 rule->mParent = this;
88 updateElseRules();
89}
90
92{
93 mChildren.insert( i, rule );
94 rule->mParent = this;
95 updateElseRules();
96}
97
99{
100 mChildren.removeAll( rule );
101 delete rule;
102 updateElseRules();
103}
104
106{
107 delete mChildren.takeAt( i );
108 updateElseRules();
109}
110
112{
113 mChildren.removeAll( rule );
114 rule->mParent = nullptr;
115 updateElseRules();
116 return rule;
117}
118
120{
121 Rule *rule = mChildren.takeAt( i );
122 rule->mParent = nullptr;
123 updateElseRules();
124 return rule;
125}
126
128{
129 // we could use a hash / map for search if this will be slow...
130
131 if ( key == mRuleKey )
132 return this;
133
134 const auto constMChildren = mChildren;
135 for ( Rule *rule : constMChildren )
136 {
137 Rule *r = rule->findRuleByKey( key );
138 if ( r )
139 return r;
140 }
141 return nullptr;
142}
143
144void QgsRuleBasedRenderer::Rule::updateElseRules()
145{
146 mElseRules.clear();
147 const auto constMChildren = mChildren;
148 for ( Rule *rule : constMChildren )
149 {
150 if ( rule->isElse() )
151 mElseRules << rule;
152 }
153}
154
156{
157 mFilterExp = QStringLiteral( "ELSE" );
158 mElseRule = iselse;
159 mFilter.reset();
160}
161
163{
164 // NOTE: if visitEnter returns false it means "don't visit the rule", not "abort all further visitations"
166 return true;
167
168 if ( mSymbol )
169 {
170 QgsStyleSymbolEntity entity( mSymbol.get() );
171 if ( !visitor->visit( QgsStyleEntityVisitorInterface::StyleLeaf( &entity ) ) )
172 return false;
173 }
174
175 if ( !mChildren.empty() )
176 {
177 for ( const Rule *rule : mChildren )
178 {
179
180 if ( !rule->accept( visitor ) )
181 return false;
182 }
183 }
184
186 return false;
187
188 return true;
189}
190
191QString QgsRuleBasedRenderer::Rule::dump( int indent ) const
192{
193 QString off;
194 off.fill( QChar( ' ' ), indent );
195 QString symbolDump = ( mSymbol ? mSymbol->dump() : QStringLiteral( "[]" ) );
196 QString msg = off + QStringLiteral( "RULE %1 - scale [%2,%3] - filter %4 - symbol %5\n" )
197 .arg( mLabel ).arg( mMaximumScale ).arg( mMinimumScale )
198 .arg( mFilterExp, symbolDump );
199
200 QStringList lst;
201 const auto constMChildren = mChildren;
202 for ( Rule *rule : constMChildren )
203 {
204 lst.append( rule->dump( indent + 2 ) );
205 }
206 msg += lst.join( QLatin1Char( '\n' ) );
207 return msg;
208}
209
211{
212 // attributes needed by this rule
213 QSet<QString> attrs;
214 if ( mFilter )
215 attrs.unite( mFilter->referencedColumns() );
216 if ( mSymbol )
217 attrs.unite( mSymbol->usedAttributes( context ) );
218
219 // attributes needed by child rules
220 const auto constMChildren = mChildren;
221 for ( Rule *rule : constMChildren )
222 {
223 attrs.unite( rule->usedAttributes( context ) );
224 }
225 return attrs;
226}
227
229{
230 if ( mFilter && mFilter->needsGeometry() )
231 return true;
232
233 const auto constMChildren = mChildren;
234 for ( Rule *rule : constMChildren )
235 {
236 if ( rule->needsGeometry() )
237 return true;
238 }
239
240 return false;
241}
242
244{
245 QgsSymbolList lst;
246 if ( mSymbol )
247 lst.append( mSymbol.get() );
248
249 const auto constMChildren = mChildren;
250 for ( Rule *rule : constMChildren )
251 {
252 lst += rule->symbols( context );
253 }
254 return lst;
255}
256
258{
259 mSymbol.reset( sym );
260}
261
263{
264 mFilterExp = filterExp;
265 initFilter();
266}
267
269{
271 if ( currentLevel != -1 ) // root rule should not be shown
272 {
273 lst << QgsLegendSymbolItem( mSymbol.get(), mLabel, mRuleKey, true, mMaximumScale, mMinimumScale, currentLevel, mParent ? mParent->mRuleKey : QString() );
274 }
275
276 for ( RuleList::const_iterator it = mChildren.constBegin(); it != mChildren.constEnd(); ++it )
277 {
278 Rule *rule = *it;
279 lst << rule->legendSymbolItems( currentLevel + 1 );
280 }
281 return lst;
282}
283
284
286{
287 if ( ! mFilter || mElseRule || ! context )
288 return true;
289
290 context->expressionContext().setFeature( f );
291 QVariant res = mFilter->evaluate( &context->expressionContext() );
292 return res.toBool();
293}
294
296{
297 if ( qgsDoubleNear( scale, 0.0 ) ) // so that we can count features in classes without scale context
298 return true;
299 if ( qgsDoubleNear( mMaximumScale, 0.0 ) && qgsDoubleNear( mMinimumScale, 0.0 ) )
300 return true;
301
302 // maxScale is inclusive ( < --> no render )
303 if ( !qgsDoubleNear( mMaximumScale, 0.0 ) && QgsScaleUtils::lessThanMaximumScale( scale, mMaximumScale ) )
304 return false;
305
306 // minScale is exclusive ( >= --> no render )
307 if ( !qgsDoubleNear( mMinimumScale, 0.0 ) && QgsScaleUtils::equalToOrGreaterThanMinimumScale( scale, mMinimumScale ) )
308 return false;
309
310 return true;
311}
312
314{
315 QgsSymbol *sym = mSymbol ? mSymbol->clone() : nullptr;
316 Rule *newrule = new Rule( sym, mMaximumScale, mMinimumScale, mFilterExp, mLabel, mDescription );
317 newrule->setActive( mIsActive );
318 // clone children
319 const auto constMChildren = mChildren;
320 for ( Rule *rule : constMChildren )
321 newrule->appendChild( rule->clone() );
322 return newrule;
323}
324
325QDomElement QgsRuleBasedRenderer::Rule::save( QDomDocument &doc, QgsSymbolMap &symbolMap ) const
326{
327 QDomElement ruleElem = doc.createElement( QStringLiteral( "rule" ) );
328
329 if ( mSymbol )
330 {
331 int symbolIndex = symbolMap.size();
332 symbolMap[QString::number( symbolIndex )] = mSymbol.get();
333 ruleElem.setAttribute( QStringLiteral( "symbol" ), symbolIndex );
334 }
335 if ( !mFilterExp.isEmpty() )
336 ruleElem.setAttribute( QStringLiteral( "filter" ), mFilterExp );
337 if ( mMaximumScale != 0 )
338 ruleElem.setAttribute( QStringLiteral( "scalemindenom" ), mMaximumScale );
339 if ( mMinimumScale != 0 )
340 ruleElem.setAttribute( QStringLiteral( "scalemaxdenom" ), mMinimumScale );
341 if ( !mLabel.isEmpty() )
342 ruleElem.setAttribute( QStringLiteral( "label" ), mLabel );
343 if ( !mDescription.isEmpty() )
344 ruleElem.setAttribute( QStringLiteral( "description" ), mDescription );
345 if ( !mIsActive )
346 ruleElem.setAttribute( QStringLiteral( "checkstate" ), 0 );
347 ruleElem.setAttribute( QStringLiteral( "key" ), mRuleKey );
348
349 const auto constMChildren = mChildren;
350 for ( Rule *rule : constMChildren )
351 {
352 ruleElem.appendChild( rule->save( doc, symbolMap ) );
353 }
354 return ruleElem;
355}
356
357void QgsRuleBasedRenderer::Rule::toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const
358{
359 QgsSldExportContext context;
360 context.setExtraProperties( props );
361 toSld( doc, element, context );
362}
363
364bool QgsRuleBasedRenderer::Rule::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &exportContext ) const
365{
366 // do not convert this rule if there are no symbols
367 QgsRenderContext context;
368 if ( symbols( context ).isEmpty() )
369 return false;
370
371 const QVariantMap oldProps = exportContext.extraProperties();
372 QVariantMap props = oldProps;
373 if ( !mFilterExp.isEmpty() )
374 {
375 QString filter = props.value( QStringLiteral( "filter" ), QString() ).toString();
376 if ( !filter.isEmpty() )
377 filter += QLatin1String( " AND " );
378 filter += mFilterExp;
379 props[ QStringLiteral( "filter" )] = filter;
380 }
381
382 QgsSymbolLayerUtils::mergeScaleDependencies( mMaximumScale, mMinimumScale, props );
383 exportContext.setExtraProperties( props );
384
385 if ( mSymbol )
386 {
387 QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
388
389 //XXX: <se:Name> is the rule identifier, but our the Rule objects
390 // have no properties could be used as identifier. Use the label.
391 QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
392 nameElem.appendChild( doc.createTextNode( mLabel ) );
393 ruleElem.appendChild( nameElem );
394
395 if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
396 {
397 QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
398 if ( !mLabel.isEmpty() )
399 {
400 QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
401 titleElem.appendChild( doc.createTextNode( mLabel ) );
402 descrElem.appendChild( titleElem );
403 }
404 if ( !mDescription.isEmpty() )
405 {
406 QDomElement abstractElem = doc.createElement( QStringLiteral( "se:Abstract" ) );
407 abstractElem.appendChild( doc.createTextNode( mDescription ) );
408 descrElem.appendChild( abstractElem );
409 }
410 ruleElem.appendChild( descrElem );
411 }
412
413 if ( !props.value( QStringLiteral( "filter" ), QString() ).toString().isEmpty() )
414 {
415 QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, props.value( QStringLiteral( "filter" ), QString() ).toString(), exportContext );
416 }
417
418 QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
419 exportContext.setExtraProperties( props );
420
421 mSymbol->toSld( doc, ruleElem, exportContext );
422
423 // Only create rules if symbol could be converted to SLD, and is not an "empty" symbol. Otherwise we do not generate a rule, as
424 // SLD spec requires a Symbolizer element to be present
426 {
427 element.appendChild( ruleElem );
428 }
429 }
430
431 // loop into children rule list
432 bool result = true;
433 for ( Rule *rule : std::as_const( mChildren ) )
434 {
435 if ( !rule->toSld( doc, element, exportContext ) )
436 result = false;
437 }
438 exportContext.setExtraProperties( oldProps );
439 return result;
440}
441
443{
444 mActiveChildren.clear();
445
446 if ( ! mIsActive )
447 return false;
448
449 // filter out rules which are not compatible with this scale
450 if ( !isScaleOK( context.rendererScale() ) )
451 return false;
452
453 // init this rule
454 if ( mFilter )
455 mFilter->prepare( &context.expressionContext() );
456 if ( mSymbol )
457 mSymbol->startRender( context, fields );
458
459 // init children
460 // build temporary list of active rules (usable with this scale)
461 QStringList subfilters;
462 const auto constMChildren = mChildren;
463 for ( Rule *rule : constMChildren )
464 {
465 QString subfilter;
466 if ( rule->startRender( context, fields, subfilter ) )
467 {
468 // only add those which are active with current scale
469 mActiveChildren.append( rule );
470 subfilters.append( subfilter );
471 }
472 }
473
474 // subfilters (on the same level) are joined with OR
475 // Finally they are joined with their parent (this) with AND
476 QString sf;
477 // If there are subfilters present (and it's not a single empty one), group them and join them with OR
478 if ( subfilters.length() > 1 || !subfilters.value( 0 ).isEmpty() )
479 {
480 if ( subfilters.contains( QStringLiteral( "TRUE" ) ) )
481 {
482 sf = QStringLiteral( "TRUE" );
483 }
484 else
485 {
486 // test for a common case -- all subfilters can be combined into a single "field in (...)" expression
487 if ( QgsExpression::attemptReduceToInClause( subfilters, sf ) )
488 {
489 // success! we can use a simple "field IN (...)" list!
490 }
491 // If we have more than 50 rules (to stay on the safe side) make a binary tree or SQLITE will fail,
492 // see: https://github.com/qgis/QGIS/issues/27269
493 else if ( subfilters.count() > 50 )
494 {
495 std::function<QString( const QStringList & )>bt = [ &bt ]( const QStringList & subf )
496 {
497 if ( subf.count( ) == 1 )
498 {
499 return subf.at( 0 );
500 }
501 else if ( subf.count( ) == 2 )
502 {
503 return subf.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
504 }
505 else
506 {
507 int midpos = static_cast<int>( subf.length() / 2 );
508 return QStringLiteral( "(%1) OR (%2)" ).arg( bt( subf.mid( 0, midpos ) ), bt( subf.mid( midpos ) ) );
509 }
510 };
511 sf = bt( subfilters );
512 }
513 else
514 {
515 sf = subfilters.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
516 }
517 }
518 }
519
520 // Now join the subfilters with their parent (this) based on if
521 // * The parent is an else rule
522 // * The existence of parent filter and subfilters
523
524 // No filter expression: ELSE rule or catchall rule
525 if ( !mFilter )
526 {
527 if ( mSymbol || sf.isEmpty() )
528 filter = QStringLiteral( "TRUE" );
529 else
530 filter = sf;
531 }
532 else if ( mSymbol )
533 filter = mFilterExp;
534 else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
535 filter = QStringLiteral( "(%1) AND (%2)" ).arg( mFilterExp, sf );
536 else if ( !mFilterExp.trimmed().isEmpty() )
537 filter = mFilterExp;
538 else if ( sf.isEmpty() )
539 filter = QStringLiteral( "TRUE" );
540 else
541 filter = sf;
542
543 filter = filter.trimmed();
544
545 return true;
546}
547
549{
550 return !mActiveChildren.empty();
551}
552
554{
555 QSet<int> symbolZLevelsSet;
556
557 // process this rule
558 if ( mSymbol )
559 {
560 // find out which Z-levels are used
561 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
562 {
563 symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
564 }
565 }
566
567 // process children
568 QList<Rule *>::iterator it;
569 for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
570 {
571 Rule *rule = *it;
572 symbolZLevelsSet.unite( rule->collectZLevels() );
573 }
574 return symbolZLevelsSet;
575}
576
577void QgsRuleBasedRenderer::Rule::setNormZLevels( const QMap<int, int> &zLevelsToNormLevels )
578{
579 if ( mSymbol )
580 {
581 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
582 {
583 int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
584 mSymbolNormZLevels.insert( normLevel );
585 }
586 }
587
588 // prepare list of normalized levels for each rule
589 const auto constMActiveChildren = mActiveChildren;
590 for ( Rule *rule : constMActiveChildren )
591 {
592 rule->setNormZLevels( zLevelsToNormLevels );
593 }
594}
595
596
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 )
790{
791 QString symbolIdx = ruleElem.attribute( QStringLiteral( "symbol" ) );
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( QStringLiteral( "filter" ) );
806 QString label = ruleElem.attribute( QStringLiteral( "label" ) );
807 QString description = ruleElem.attribute( QStringLiteral( "description" ) );
808 int scaleMinDenom = ruleElem.attribute( QStringLiteral( "scalemindenom" ), QStringLiteral( "0" ) ).toInt();
809 int scaleMaxDenom = ruleElem.attribute( QStringLiteral( "scalemaxdenom" ), QStringLiteral( "0" ) ).toInt();
810 QString ruleKey;
811 if ( reuseId )
812 ruleKey = ruleElem.attribute( QStringLiteral( "key" ) );
813 else
814 ruleKey = QUuid::createUuid().toString();
815 Rule *rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
816
817 if ( !ruleKey.isEmpty() )
818 rule->mRuleKey = ruleKey;
819
820 rule->setActive( ruleElem.attribute( QStringLiteral( "checkstate" ), QStringLiteral( "1" ) ).toInt() );
821
822 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
823 while ( !childRuleElem.isNull() )
824 {
825 Rule *childRule = create( childRuleElem, symbolMap );
826 if ( childRule )
827 {
828 rule->appendChild( childRule );
829 }
830 else
831 {
832 QgsDebugError( QStringLiteral( "failed to init a child rule!" ) );
833 }
834 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
835 }
836
837 return rule;
838}
839
841{
842 RuleList l;
843 for ( QgsRuleBasedRenderer::Rule *c : mChildren )
844 {
845 l += c;
846 l += c->descendants();
847 }
848 return l;
849}
850
852{
853 if ( ruleElem.localName() != QLatin1String( "Rule" ) )
854 {
855 QgsDebugError( QStringLiteral( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
856 return nullptr;
857 }
858
859 QString label, description, filterExp;
860 int scaleMinDenom = 0, scaleMaxDenom = 0;
861 QgsSymbolLayerList layers;
862
863 // retrieve the Rule element child nodes
864 QDomElement childElem = ruleElem.firstChildElement();
865 while ( !childElem.isNull() )
866 {
867 if ( childElem.localName() == QLatin1String( "Name" ) )
868 {
869 // <se:Name> tag contains the rule identifier,
870 // so prefer title tag for the label property value
871 if ( label.isEmpty() )
872 label = childElem.firstChild().nodeValue();
873 }
874 else if ( childElem.localName() == QLatin1String( "Description" ) )
875 {
876 // <se:Description> can contains a title and an abstract
877 QDomElement titleElem = childElem.firstChildElement( QStringLiteral( "Title" ) );
878 if ( !titleElem.isNull() )
879 {
880 label = titleElem.firstChild().nodeValue();
881 }
882
883 QDomElement abstractElem = childElem.firstChildElement( QStringLiteral( "Abstract" ) );
884 if ( !abstractElem.isNull() )
885 {
886 description = abstractElem.firstChild().nodeValue();
887 }
888 }
889 else if ( childElem.localName() == QLatin1String( "Abstract" ) )
890 {
891 // <sld:Abstract> (v1.0)
892 description = childElem.firstChild().nodeValue();
893 }
894 else if ( childElem.localName() == QLatin1String( "Title" ) )
895 {
896 // <sld:Title> (v1.0)
897 label = childElem.firstChild().nodeValue();
898 }
899 else if ( childElem.localName() == QLatin1String( "Filter" ) )
900 {
902 if ( filter )
903 {
904 if ( filter->hasParserError() )
905 {
906 QgsDebugError( "parser error: " + filter->parserErrorString() );
907 }
908 else
909 {
910 filterExp = filter->expression();
911 }
912 delete filter;
913 }
914 }
915 else if ( childElem.localName() == QLatin1String( "ElseFilter" ) )
916 {
917 filterExp = QLatin1String( "ELSE" );
918
919 }
920 else if ( childElem.localName() == QLatin1String( "MinScaleDenominator" ) )
921 {
922 bool ok;
923 int v = childElem.firstChild().nodeValue().toInt( &ok );
924 if ( ok )
925 scaleMinDenom = v;
926 }
927 else if ( childElem.localName() == QLatin1String( "MaxScaleDenominator" ) )
928 {
929 bool ok;
930 int v = childElem.firstChild().nodeValue().toInt( &ok );
931 if ( ok )
932 scaleMaxDenom = v;
933 }
934 else if ( childElem.localName().endsWith( QLatin1String( "Symbolizer" ) ) )
935 {
936 // create symbol layers for this symbolizer
937 QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
938 }
939
940 childElem = childElem.nextSiblingElement();
941 }
942
943 // now create the symbol
944 QgsSymbol *symbol = nullptr;
945 if ( !layers.isEmpty() )
946 {
947 switch ( geomType )
948 {
950 symbol = new QgsLineSymbol( layers );
951 break;
952
954 symbol = new QgsFillSymbol( layers );
955 break;
956
958 symbol = new QgsMarkerSymbol( layers );
959 break;
960
961 default:
962 QgsDebugError( QStringLiteral( "invalid geometry type: found %1" ).arg( qgsEnumValueToKey( geomType ) ) );
963 return nullptr;
964 }
965 }
966
967 // and then create and return the new rule
968 return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
969}
970
971
973
975 : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
976 , mRootRule( root )
977{
978}
979
981 : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
982{
983 mRootRule = new Rule( nullptr ); // root has no symbol, no filter etc - just a container
984 mRootRule->appendChild( new Rule( defaultSymbol ) );
985}
986
991
992
994{
995 // not used at all
996 return nullptr;
997}
998
1000{
1002
1003 std::function< void( Rule *rule ) > exploreRule;
1004 exploreRule = [&res, &exploreRule]( Rule * rule )
1005 {
1006 if ( !rule )
1007 return;
1008
1009 if ( QgsSymbol *symbol = rule->symbol() )
1010 {
1011 if ( symbol->flags().testFlag( Qgis::SymbolFlag::AffectsLabeling ) )
1013 }
1014
1015 for ( Rule *child : rule->children() )
1016 {
1017 exploreRule( child );
1018 }
1019 };
1020 exploreRule( mRootRule );
1021
1022 return res;
1023}
1024
1026 QgsRenderContext &context,
1027 int layer,
1028 bool selected,
1029 bool drawVertexMarker )
1030{
1031 Q_UNUSED( layer )
1032
1033 int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
1034 mCurrentFeatures.append( FeatureToRender( feature, flags ) );
1035
1036 // check each active rule
1037 return mRootRule->renderFeature( mCurrentFeatures.last(), context, mRenderQueue ) == Rule::Rendered;
1038}
1039
1040
1042{
1043 QgsFeatureRenderer::startRender( context, fields );
1044
1045 // prepare active children
1046 mRootRule->startRender( context, fields, mFilter );
1047
1048 QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
1049 QList<int> symbolZLevels( symbolZLevelsSet.begin(), symbolZLevelsSet.end() );
1050 std::sort( symbolZLevels.begin(), symbolZLevels.end() );
1051
1052 // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
1053 // and prepare rendering queue
1054 QMap<int, int> zLevelsToNormLevels;
1055 int maxNormLevel = -1;
1056 const auto constSymbolZLevels = symbolZLevels;
1057 for ( int zLevel : constSymbolZLevels )
1058 {
1059 zLevelsToNormLevels[zLevel] = ++maxNormLevel;
1060 mRenderQueue.append( RenderLevel( zLevel ) );
1061 QgsDebugMsgLevel( QStringLiteral( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ), 4 );
1062 }
1063
1064 mRootRule->setNormZLevels( zLevelsToNormLevels );
1065}
1066
1068{
1069 return !mRootRule->hasActiveChildren();
1070}
1071
1073{
1075
1076 //
1077 // do the actual rendering
1078 //
1079
1080 // go through all levels
1081 if ( !context.renderingStopped() )
1082 {
1083 const auto constMRenderQueue = mRenderQueue;
1084 for ( const RenderLevel &level : constMRenderQueue )
1085 {
1086 //QgsDebugMsgLevel(QString("level %1").arg(level.zIndex), 2);
1087 // go through all jobs at the level
1088 for ( const RenderJob *job : std::as_const( level.jobs ) )
1089 {
1090 context.expressionContext().setFeature( job->ftr.feat );
1091 //QgsDebugMsgLevel(QString("job fid %1").arg(job->f->id()), 2);
1092 // render feature - but only with symbol layers with specified zIndex
1093 QgsSymbol *s = job->symbol;
1094 int count = s->symbolLayerCount();
1095 for ( int i = 0; i < count; i++ )
1096 {
1097 // TODO: better solution for this
1098 // renderFeatureWithSymbol asks which symbol layer to draw
1099 // but there are multiple transforms going on!
1100 if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
1101 {
1102 int flags = job->ftr.flags;
1103 renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
1104 }
1105 }
1106 }
1107 }
1108 }
1109
1110 // clean current features
1111 mCurrentFeatures.clear();
1112
1113 // clean render queue
1114 mRenderQueue.clear();
1115
1116 // clean up rules from temporary stuff
1117 mRootRule->stopRender( context );
1118}
1119
1121{
1122 return mFilter;
1123}
1124
1125QSet<QString> QgsRuleBasedRenderer::usedAttributes( const QgsRenderContext &context ) const
1126{
1127 return mRootRule->usedAttributes( context );
1128}
1129
1131{
1132 return mRootRule->needsGeometry();
1133}
1134
1136{
1137 QgsRuleBasedRenderer::Rule *clonedRoot = mRootRule->clone();
1138
1139 // normally with clone() the individual rules get new keys (UUID), but here we want to keep
1140 // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
1141 clonedRoot->setRuleKey( mRootRule->ruleKey() );
1142 RuleList origDescendants = mRootRule->descendants();
1143 RuleList clonedDescendants = clonedRoot->descendants();
1144 Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
1145 for ( int i = 0; i < origDescendants.count(); ++i )
1146 clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
1147
1148 QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( clonedRoot );
1149
1150 copyRendererData( r );
1151 return r;
1152}
1153
1154void QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1155{
1156 QgsSldExportContext context;
1157 context.setExtraProperties( props );
1158 toSld( doc, element, context );
1159}
1160
1161bool QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, QgsSldExportContext &context ) const
1162{
1163 return mRootRule->toSld( doc, element, context );
1164}
1165
1166// TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
1168{
1169 return mRootRule->symbols( context );
1170}
1171
1172QDomElement QgsRuleBasedRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
1173{
1174 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1175 rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "RuleRenderer" ) );
1176
1178
1179 QDomElement rulesElem = mRootRule->save( doc, symbols );
1180 rulesElem.setTagName( QStringLiteral( "rules" ) ); // instead of just "rule"
1181 rendererElem.appendChild( rulesElem );
1182
1183 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
1184 rendererElem.appendChild( symbolsElem );
1185
1186 saveRendererData( doc, rendererElem, context );
1187
1188 return rendererElem;
1189}
1190
1192{
1193 return true;
1194}
1195
1197{
1198 Rule *rule = mRootRule->findRuleByKey( key );
1199 return rule ? rule->active() : true;
1200}
1201
1202void QgsRuleBasedRenderer::checkLegendSymbolItem( const QString &key, bool state )
1203{
1204 Rule *rule = mRootRule->findRuleByKey( key );
1205 if ( rule )
1206 rule->setActive( state );
1207}
1208
1209QString QgsRuleBasedRenderer::legendKeyToExpression( const QString &key, QgsVectorLayer *, bool &ok ) const
1210{
1211 ok = false;
1212 Rule *rule = mRootRule->findRuleByKey( key );
1213 if ( !rule )
1214 return QString();
1215
1216 std::function<QString( Rule *rule )> ruleToExpression;
1217 ruleToExpression = [&ruleToExpression]( Rule * rule ) -> QString
1218 {
1219 if ( rule->isElse() && rule->parent() )
1220 {
1221 // gather the expressions for all other rules on this level and invert them
1222
1223 QStringList otherRules;
1224 const QList<QgsRuleBasedRenderer::Rule *> siblings = rule->parent()->children();
1225 for ( Rule *sibling : siblings )
1226 {
1227 if ( sibling == rule || sibling->isElse() )
1228 continue;
1229
1230 const QString siblingExpression = ruleToExpression( sibling );
1231 if ( siblingExpression.isEmpty() )
1232 return QStringLiteral( "FALSE" ); // nothing will match this rule
1233
1234 otherRules.append( siblingExpression );
1235 }
1236
1237 if ( otherRules.empty() )
1238 return QStringLiteral( "TRUE" ); // all features will match the else rule
1239 else
1240 return (
1241 otherRules.size() > 1
1242 ? QStringLiteral( "NOT ((%1))" ).arg( otherRules.join( QLatin1String( ") OR (" ) ) )
1243 : QStringLiteral( "NOT (%1)" ).arg( otherRules.at( 0 ) )
1244 );
1245 }
1246 else
1247 {
1248 QStringList ruleParts;
1249 if ( !rule->filterExpression().isEmpty() )
1250 ruleParts.append( rule->filterExpression() );
1251
1252 if ( !qgsDoubleNear( rule->minimumScale(), 0.0 ) )
1253 ruleParts.append( QStringLiteral( "@map_scale <= %1" ).arg( rule->minimumScale() ) );
1254
1255 if ( !qgsDoubleNear( rule->maximumScale(), 0.0 ) )
1256 ruleParts.append( QStringLiteral( "@map_scale >= %1" ).arg( rule->maximumScale() ) );
1257
1258 if ( !ruleParts.empty() )
1259 {
1260 return (
1261 ruleParts.size() > 1
1262 ? QStringLiteral( "(%1)" ).arg( ruleParts.join( QLatin1String( ") AND (" ) ) )
1263 : ruleParts.at( 0 )
1264 );
1265 }
1266 else
1267 {
1268 return QString();
1269 }
1270 }
1271 };
1272
1273 QStringList parts;
1274 while ( rule )
1275 {
1276 const QString ruleFilter = ruleToExpression( rule );
1277 if ( !ruleFilter.isEmpty() )
1278 parts.append( ruleFilter );
1279
1280 rule = rule->parent();
1281 }
1282
1283 ok = true;
1284 return parts.empty() ? QStringLiteral( "TRUE" )
1285 : ( parts.size() > 1
1286 ? QStringLiteral( "(%1)" ).arg( parts.join( QLatin1String( ") AND (" ) ) )
1287 : parts.at( 0 ) );
1288}
1289
1290void QgsRuleBasedRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
1291{
1292 Rule *rule = mRootRule->findRuleByKey( key );
1293 if ( rule )
1294 rule->setSymbol( symbol );
1295 else
1296 delete symbol;
1297}
1298
1300{
1301 return mRootRule->legendSymbolItems();
1302}
1303
1304
1306{
1307 // load symbols
1308 QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
1309 if ( symbolsElem.isNull() )
1310 return nullptr;
1311
1312 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
1313
1314 QDomElement rulesElem = element.firstChildElement( QStringLiteral( "rules" ) );
1315
1316 Rule *root = Rule::create( rulesElem, symbolMap );
1317 if ( !root )
1318 return nullptr;
1319
1321
1322 // delete symbols if there are any more
1324
1325 return r;
1326}
1327
1329{
1330 // retrieve child rules
1331 Rule *root = nullptr;
1332
1333 QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
1334 while ( !ruleElem.isNull() )
1335 {
1336 Rule *child = Rule::createFromSld( ruleElem, geomType );
1337 if ( child )
1338 {
1339 // create the root rule if not done before
1340 if ( !root )
1341 root = new Rule( nullptr );
1342
1343 root->appendChild( child );
1344 }
1345
1346 ruleElem = ruleElem.nextSiblingElement( QStringLiteral( "Rule" ) );
1347 }
1348
1349 if ( !root )
1350 {
1351 // no valid rules was found
1352 return nullptr;
1353 }
1354
1355 // create and return the new renderer
1356 return new QgsRuleBasedRenderer( root );
1357}
1358
1361
1363{
1364 QString attr = r->classAttribute();
1365 // categorizedAttr could be either an attribute name or an expression.
1366 // the only way to differentiate is to test it as an expression...
1367 QgsExpression testExpr( attr );
1368 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1369 {
1370 //not an expression, so need to quote column name
1371 attr = QgsExpression::quotedColumnRef( attr );
1372 }
1373
1374 const auto constCategories = r->categories();
1375 for ( const QgsRendererCategory &cat : constCategories )
1376 {
1377 QString value;
1378 // not quoting numbers saves a type cast
1379 if ( QgsVariantUtils::isNull( cat.value() ) )
1380 value = "NULL";
1381 else if ( cat.value().userType() == QMetaType::Type::Int )
1382 value = cat.value().toString();
1383 else if ( cat.value().userType() == QMetaType::Type::Double )
1384 // we loose precision here - so we may miss some categories :-(
1385 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1386 value = QString::number( cat.value().toDouble(), 'f', 4 );
1387 else
1388 value = QgsExpression::quotedString( cat.value().toString() );
1389 const QString filter = QStringLiteral( "%1 %2 %3" ).arg( attr, QgsVariantUtils::isNull( cat.value() ) ? QStringLiteral( "IS" ) : QStringLiteral( "=" ), value );
1390 const QString label = !cat.label().isEmpty() ? cat.label() :
1391 cat.value().isValid() ? value : QString();
1392 initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1393 }
1394}
1395
1397{
1398 QString attr = r->classAttribute();
1399 // categorizedAttr could be either an attribute name or an expression.
1400 // the only way to differentiate is to test it as an expression...
1401 QgsExpression testExpr( attr );
1402 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1403 {
1404 //not an expression, so need to quote column name
1405 attr = QgsExpression::quotedColumnRef( attr );
1406 }
1407 else if ( !testExpr.isField() )
1408 {
1409 //otherwise wrap expression in brackets
1410 attr = QStringLiteral( "(%1)" ).arg( attr );
1411 }
1412
1413 bool firstRange = true;
1414 const auto constRanges = r->ranges();
1415 for ( const QgsRendererRange &rng : constRanges )
1416 {
1417 // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1418 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1419 QString filter = QStringLiteral( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
1420 QString::number( rng.lowerValue(), 'f', 4 ),
1421 QString::number( rng.upperValue(), 'f', 4 ) );
1422 firstRange = false;
1423 QString label = rng.label().isEmpty() ? filter : rng.label();
1424 initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1425 }
1426}
1427
1429{
1430 std::sort( scales.begin(), scales.end() ); // make sure the scales are in ascending order
1431 double oldScale = initialRule->maximumScale();
1432 double maxDenom = initialRule->minimumScale();
1433 QgsSymbol *symbol = initialRule->symbol();
1434 const auto constScales = scales;
1435 for ( int scale : constScales )
1436 {
1437 if ( initialRule->maximumScale() >= scale )
1438 continue; // jump over the first scales out of the interval
1439 if ( maxDenom != 0 && maxDenom <= scale )
1440 break; // ignore the latter scales out of the interval
1441 initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1442 oldScale = scale;
1443 }
1444 // last rule
1445 initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1446}
1447
1449{
1450 QString msg( QStringLiteral( "Rule-based renderer:\n" ) );
1451 msg += mRootRule->dump();
1452 return msg;
1453}
1454
1456{
1457 return mRootRule->willRenderFeature( feature, &context );
1458}
1459
1461{
1462 return mRootRule->symbolsForFeature( feature, &context );
1463}
1464
1466{
1467 return mRootRule->symbolsForFeature( feature, &context );
1468}
1469
1470QSet< QString > QgsRuleBasedRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
1471{
1472 return mRootRule->legendKeysForFeature( feature, &context );
1473}
1474
1476{
1477 return mRootRule->accept( visitor );
1478}
1479
1481{
1482 std::unique_ptr< QgsRuleBasedRenderer > r;
1483 if ( renderer->type() == QLatin1String( "RuleRenderer" ) )
1484 {
1485 r.reset( dynamic_cast<QgsRuleBasedRenderer *>( renderer->clone() ) );
1486 }
1487 else if ( renderer->type() == QLatin1String( "singleSymbol" ) )
1488 {
1489 const QgsSingleSymbolRenderer *singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRenderer *>( renderer );
1490 if ( !singleSymbolRenderer )
1491 return nullptr;
1492
1493 std::unique_ptr< QgsSymbol > origSymbol( singleSymbolRenderer->symbol()->clone() );
1494 r = std::make_unique< QgsRuleBasedRenderer >( origSymbol.release() );
1495 }
1496 else if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
1497 {
1498 const QgsCategorizedSymbolRenderer *categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRenderer *>( renderer );
1499 if ( !categorizedRenderer )
1500 return nullptr;
1501
1502 QString attr = categorizedRenderer->classAttribute();
1503 // categorizedAttr could be either an attribute name or an expression.
1504 bool isField = false;
1505 if ( layer )
1506 {
1507 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1508 }
1509 else
1510 {
1511 QgsExpression testExpr( attr );
1512 isField = testExpr.hasParserError() || testExpr.isField();
1513 }
1514 if ( isField && !attr.contains( '\"' ) )
1515 {
1516 //not an expression, so need to quote column name
1517 attr = QgsExpression::quotedColumnRef( attr );
1518 }
1519
1520 auto rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1521
1522 QString expression;
1523 QString value;
1524 QgsRendererCategory category;
1525 for ( const QgsRendererCategory &category : categorizedRenderer->categories() )
1526 {
1527 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1528
1529 rule->setLabel( category.label() );
1530
1531 //We first define the rule corresponding to the category
1532 if ( category.value().userType() == QMetaType::Type::QVariantList )
1533 {
1534 QStringList values;
1535 const QVariantList list = category.value().toList();
1536 for ( const QVariant &v : list )
1537 {
1538 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1539 if ( QVariant( v ).convert( QMetaType::Type::Double ) )
1540 {
1541 values << v.toString();
1542 }
1543 else
1544 {
1545 values << QgsExpression::quotedString( v.toString() );
1546 }
1547 }
1548
1549 if ( values.empty() )
1550 {
1551 expression = QStringLiteral( "ELSE" );
1552 }
1553 else
1554 {
1555 expression = QStringLiteral( "%1 IN (%2)" ).arg( attr, values.join( ',' ) );
1556 }
1557 }
1558 else
1559 {
1560 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1561 if ( category.value().convert( QMetaType::Type::Double ) )
1562 {
1563 value = category.value().toString();
1564 }
1565 else
1566 {
1567 value = QgsExpression::quotedString( category.value().toString() );
1568 }
1569
1570 //An empty category is equivalent to the ELSE keyword
1571 if ( value == QLatin1String( "''" ) )
1572 {
1573 expression = QStringLiteral( "ELSE" );
1574 }
1575 else
1576 {
1577 expression = QStringLiteral( "%1 = %2" ).arg( attr, value );
1578 }
1579 }
1580 rule->setFilterExpression( expression );
1581
1582 //Then we construct an equivalent symbol.
1583 //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1584 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1585
1586 std::unique_ptr< QgsSymbol > origSymbol( category.symbol()->clone() );
1587 rule->setSymbol( origSymbol.release() );
1588
1589 rootrule->appendChild( rule.release() );
1590 }
1591
1592 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1593 }
1594 else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1595 {
1596 const QgsGraduatedSymbolRenderer *graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1597 if ( !graduatedRenderer )
1598 return nullptr;
1599
1600 QString attr = graduatedRenderer->classAttribute();
1601 // categorizedAttr could be either an attribute name or an expression.
1602 // the only way to differentiate is to test it as an expression...
1603 bool isField = false;
1604 if ( layer )
1605 {
1606 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1607 }
1608 else
1609 {
1610 QgsExpression testExpr( attr );
1611 isField = testExpr.hasParserError() || testExpr.isField();
1612 }
1613 if ( isField && !attr.contains( '\"' ) )
1614 {
1615 //not an expression, so need to quote column name
1616 attr = QgsExpression::quotedColumnRef( attr );
1617 }
1618 else if ( !isField )
1619 {
1620 //otherwise wrap expression in brackets
1621 attr = QStringLiteral( "(%1)" ).arg( attr );
1622 }
1623
1624 auto rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1625
1626 QString expression;
1627 const QgsRangeList ranges = graduatedRenderer->ranges();
1628 const bool isInverted = ranges.size() > 1 ?
1629 ranges.at( 0 ).upperValue() > ranges.at( 1 ).upperValue() :
1630 false;
1631 QgsRendererRange range;
1632 for ( int i = 0; i < ranges.size(); ++i )
1633 {
1634 range = ranges.value( i );
1635 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1636
1637 rule->setLabel( range.label() );
1638 const QString upperValue = QString::number( range.upperValue(), 'f', 16 );
1639 const QString lowerValue = QString::number( range.lowerValue(), 'f', 16 );
1640 // Lower and upper boundaries have to be open-ended
1641 if ( i == 0 )
1642 {
1643 expression = !isInverted ?
1644 QStringLiteral( "%1 <= %2" ).arg( attr, upperValue ) :
1645 QStringLiteral( "%1 > %2" ).arg( attr, lowerValue );
1646 }
1647 else if ( i == ranges.size() - 1 )
1648 {
1649 expression = !isInverted ?
1650 QStringLiteral( "%1 > %2" ).arg( attr, lowerValue ) :
1651 QStringLiteral( "%1 <= %2" ).arg( attr, upperValue );
1652 }
1653 else
1654 {
1655 expression = attr + " > " + lowerValue + " AND " + \
1656 attr + " <= " + upperValue;
1657 }
1658 rule->setFilterExpression( expression );
1659
1660 //Then we construct an equivalent symbol.
1661 //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1662 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1663
1664 std::unique_ptr< QgsSymbol > symbol( range.symbol()->clone() );
1665 rule->setSymbol( symbol.release() );
1666
1667 rootrule->appendChild( rule.release() );
1668 }
1669
1670 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1671 }
1672 else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1673 {
1674 if ( const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer ) )
1675 return convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1676 }
1677 else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1678 {
1679 if ( const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer ) )
1680 r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1681 }
1682 else if ( renderer->type() == QLatin1String( "mergedFeatureRenderer" ) )
1683 {
1684 if ( const QgsMergedFeatureRenderer *mergedRenderer = dynamic_cast<const QgsMergedFeatureRenderer *>( renderer ) )
1685 r.reset( convertFromRenderer( mergedRenderer->embeddedRenderer() ) );
1686 }
1687 else if ( renderer->type() == QLatin1String( "embeddedSymbol" ) && layer )
1688 {
1689 const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
1690
1691 auto rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1692
1695 req.setNoAttributes();
1696 QgsFeatureIterator it = layer->getFeatures( req );
1697 QgsFeature feature;
1698 while ( it.nextFeature( feature ) && rootrule->children().size() < 500 )
1699 {
1700 if ( feature.embeddedSymbol() )
1701 {
1702 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1703 rule->setFilterExpression( QStringLiteral( "$id=%1" ).arg( feature.id() ) );
1704 rule->setLabel( QString::number( feature.id() ) );
1705 rule->setSymbol( feature.embeddedSymbol()->clone() );
1706 rootrule->appendChild( rule.release() );
1707 }
1708 }
1709
1710 auto rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1711 rule->setFilterExpression( QStringLiteral( "ELSE" ) );
1712 rule->setLabel( QObject::tr( "All other features" ) );
1713 rule->setSymbol( embeddedRenderer->defaultSymbol()->clone() );
1714 rootrule->appendChild( rule.release() );
1715
1716 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1717 }
1718
1719 if ( r )
1720 {
1721 renderer->copyRendererData( r.get() );
1722 }
1723
1724 return r.release();
1725}
1726
1727void QgsRuleBasedRenderer::convertToDataDefinedSymbology( QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField )
1728{
1729 QString sizeExpression;
1730 switch ( symbol->type() )
1731 {
1733 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1734 {
1735 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( symbol->symbolLayer( j ) );
1736 if ( ! sizeScaleField.isEmpty() )
1737 {
1738 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1740 }
1741 if ( ! rotationField.isEmpty() )
1742 {
1744 }
1745 }
1746 break;
1748 if ( ! sizeScaleField.isEmpty() )
1749 {
1750 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1751 {
1752 if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "SimpleLine" ) )
1753 {
1754 QgsLineSymbolLayer *lsl = static_cast<QgsLineSymbolLayer *>( symbol->symbolLayer( j ) );
1755 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
1757 }
1758 if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "MarkerLine" ) )
1759 {
1760 QgsSymbol *marker = symbol->symbolLayer( j )->subSymbol();
1761 for ( int k = 0; k < marker->symbolLayerCount(); ++k )
1762 {
1763 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( marker->symbolLayer( k ) );
1764 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1766 }
1767 }
1768 }
1769 }
1770 break;
1771 default:
1772 break;
1773 }
1774}
@ 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.