QGIS API Documentation 3.40.0-Bratislava (b56115d8743)
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#include "qgssymbollayer.h"
18#include "qgsexpression.h"
19#include "qgssymbollayerutils.h"
20#include "qgsrendercontext.h"
21#include "qgsvectorlayer.h"
22#include "qgslogger.h"
23#include "qgsogcutils.h"
26#include "qgsproperty.h"
29#include "qgslinesymbol.h"
30#include "qgsfillsymbol.h"
31#include "qgsmarkersymbol.h"
33#include "qgsscaleutils.h"
34
35#include <QSet>
36
37#include <QDomDocument>
38#include <QDomElement>
39#include <QUuid>
40
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 // do not convert this rule if there are no symbols
360 QgsRenderContext context;
361 if ( symbols( context ).isEmpty() )
362 return;
363
364 if ( !mFilterExp.isEmpty() )
365 {
366 QString filter = props.value( QStringLiteral( "filter" ), QString() ).toString();
367 if ( !filter.isEmpty() )
368 filter += QLatin1String( " AND " );
369 filter += mFilterExp;
370 props[ QStringLiteral( "filter" )] = filter;
371 }
372
373 QgsSymbolLayerUtils::mergeScaleDependencies( mMaximumScale, mMinimumScale, props );
374
375 if ( mSymbol )
376 {
377 QDomElement ruleElem = doc.createElement( QStringLiteral( "se:Rule" ) );
378 element.appendChild( ruleElem );
379
380 //XXX: <se:Name> is the rule identifier, but our the Rule objects
381 // have no properties could be used as identifier. Use the label.
382 QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
383 nameElem.appendChild( doc.createTextNode( mLabel ) );
384 ruleElem.appendChild( nameElem );
385
386 if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
387 {
388 QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
389 if ( !mLabel.isEmpty() )
390 {
391 QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
392 titleElem.appendChild( doc.createTextNode( mLabel ) );
393 descrElem.appendChild( titleElem );
394 }
395 if ( !mDescription.isEmpty() )
396 {
397 QDomElement abstractElem = doc.createElement( QStringLiteral( "se:Abstract" ) );
398 abstractElem.appendChild( doc.createTextNode( mDescription ) );
399 descrElem.appendChild( abstractElem );
400 }
401 ruleElem.appendChild( descrElem );
402 }
403
404 if ( !props.value( QStringLiteral( "filter" ), QString() ).toString().isEmpty() )
405 {
406 QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, props.value( QStringLiteral( "filter" ), QString() ).toString() );
407 }
408
409 QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
410
411 mSymbol->toSld( doc, ruleElem, props );
412 }
413
414 // loop into children rule list
415 const auto constMChildren = mChildren;
416 for ( Rule *rule : constMChildren )
417 {
418 rule->toSld( doc, element, props );
419 }
420}
421
423{
424 mActiveChildren.clear();
425
426 if ( ! mIsActive )
427 return false;
428
429 // filter out rules which are not compatible with this scale
430 if ( !isScaleOK( context.rendererScale() ) )
431 return false;
432
433 // init this rule
434 if ( mFilter )
435 mFilter->prepare( &context.expressionContext() );
436 if ( mSymbol )
437 mSymbol->startRender( context, fields );
438
439 // init children
440 // build temporary list of active rules (usable with this scale)
441 QStringList subfilters;
442 const auto constMChildren = mChildren;
443 for ( Rule *rule : constMChildren )
444 {
445 QString subfilter;
446 if ( rule->startRender( context, fields, subfilter ) )
447 {
448 // only add those which are active with current scale
449 mActiveChildren.append( rule );
450 subfilters.append( subfilter );
451 }
452 }
453
454 // subfilters (on the same level) are joined with OR
455 // Finally they are joined with their parent (this) with AND
456 QString sf;
457 // If there are subfilters present (and it's not a single empty one), group them and join them with OR
458 if ( subfilters.length() > 1 || !subfilters.value( 0 ).isEmpty() )
459 {
460 if ( subfilters.contains( QStringLiteral( "TRUE" ) ) )
461 {
462 sf = QStringLiteral( "TRUE" );
463 }
464 else
465 {
466 // test for a common case -- all subfilters can be combined into a single "field in (...)" expression
467 if ( QgsExpression::attemptReduceToInClause( subfilters, sf ) )
468 {
469 // success! we can use a simple "field IN (...)" list!
470 }
471 // If we have more than 50 rules (to stay on the safe side) make a binary tree or SQLITE will fail,
472 // see: https://github.com/qgis/QGIS/issues/27269
473 else if ( subfilters.count() > 50 )
474 {
475 std::function<QString( const QStringList & )>bt = [ &bt ]( const QStringList & subf )
476 {
477 if ( subf.count( ) == 1 )
478 {
479 return subf.at( 0 );
480 }
481 else if ( subf.count( ) == 2 )
482 {
483 return subf.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
484 }
485 else
486 {
487 int midpos = static_cast<int>( subf.length() / 2 );
488 return QStringLiteral( "(%1) OR (%2)" ).arg( bt( subf.mid( 0, midpos ) ), bt( subf.mid( midpos ) ) );
489 }
490 };
491 sf = bt( subfilters );
492 }
493 else
494 {
495 sf = subfilters.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
496 }
497 }
498 }
499
500 // Now join the subfilters with their parent (this) based on if
501 // * The parent is an else rule
502 // * The existence of parent filter and subfilters
503
504 // No filter expression: ELSE rule or catchall rule
505 if ( !mFilter )
506 {
507 if ( mSymbol || sf.isEmpty() )
508 filter = QStringLiteral( "TRUE" );
509 else
510 filter = sf;
511 }
512 else if ( mSymbol )
513 filter = mFilterExp;
514 else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
515 filter = QStringLiteral( "(%1) AND (%2)" ).arg( mFilterExp, sf );
516 else if ( !mFilterExp.trimmed().isEmpty() )
517 filter = mFilterExp;
518 else if ( sf.isEmpty() )
519 filter = QStringLiteral( "TRUE" );
520 else
521 filter = sf;
522
523 filter = filter.trimmed();
524
525 return true;
526}
527
529{
530 return !mActiveChildren.empty();
531}
532
534{
535 QSet<int> symbolZLevelsSet;
536
537 // process this rule
538 if ( mSymbol )
539 {
540 // find out which Z-levels are used
541 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
542 {
543 symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
544 }
545 }
546
547 // process children
548 QList<Rule *>::iterator it;
549 for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
550 {
551 Rule *rule = *it;
552 symbolZLevelsSet.unite( rule->collectZLevels() );
553 }
554 return symbolZLevelsSet;
555}
556
557void QgsRuleBasedRenderer::Rule::setNormZLevels( const QMap<int, int> &zLevelsToNormLevels )
558{
559 if ( mSymbol )
560 {
561 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
562 {
563 int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
564 mSymbolNormZLevels.insert( normLevel );
565 }
566 }
567
568 // prepare list of normalized levels for each rule
569 const auto constMActiveChildren = mActiveChildren;
570 for ( Rule *rule : constMActiveChildren )
571 {
572 rule->setNormZLevels( zLevelsToNormLevels );
573 }
574}
575
576
578{
579 if ( !isFilterOK( featToRender.feat, &context ) )
580 return Filtered;
581
582 bool rendered = false;
583
584 // create job for this feature and this symbol, add to list of jobs
585 if ( mSymbol && mIsActive )
586 {
587 // add job to the queue: each symbol's zLevel must be added
588 const auto constMSymbolNormZLevels = mSymbolNormZLevels;
589 for ( int normZLevel : constMSymbolNormZLevels )
590 {
591 //QgsDebugMsgLevel(QString("add job at level %1").arg(normZLevel),2);
592 renderQueue[normZLevel].jobs.append( new RenderJob( featToRender, mSymbol.get() ) );
593 rendered = true;
594 }
595 }
596
597 bool matchedAChild = false;
598
599 // process children
600 const auto constMChildren = mChildren;
601 for ( Rule *rule : constMChildren )
602 {
603 // Don't process else rules yet
604 if ( !rule->isElse() )
605 {
606 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
607 // consider inactive items as "matched" so the else rule will ignore them
608 matchedAChild |= ( res == Rendered || res == Inactive );
609 rendered |= ( res == Rendered );
610 }
611 }
612
613 // If none of the rules passed then we jump into the else rules and process them.
614 if ( !matchedAChild )
615 {
616 const auto constMElseRules = mElseRules;
617 for ( Rule *rule : constMElseRules )
618 {
619 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
620 matchedAChild |= ( res == Rendered || res == Inactive );
621 rendered |= res == Rendered;
622 }
623 }
624 if ( !mIsActive || ( mSymbol && !rendered ) || ( matchedAChild && !rendered ) )
625 return Inactive;
626 else if ( rendered )
627 return Rendered;
628 else
629 return Filtered;
630}
631
633{
634 if ( !isFilterOK( feature, context ) )
635 return false;
636
637 if ( mSymbol )
638 return true;
639
640 const auto constMActiveChildren = mActiveChildren;
641 for ( Rule *rule : constMActiveChildren )
642 {
643 if ( rule->isElse() )
644 {
645 if ( rule->children().isEmpty() )
646 {
647 RuleList lst = rulesForFeature( feature, context, false );
648 lst.removeOne( rule );
649
650 if ( lst.empty() )
651 {
652 return true;
653 }
654 }
655 else
656 {
657 return rule->willRenderFeature( feature, context );
658 }
659 }
660 else if ( rule->willRenderFeature( feature, context ) )
661 {
662 return true;
663 }
664 }
665 return false;
666}
667
669{
670 QgsSymbolList lst;
671 if ( !isFilterOK( feature, context ) )
672 return lst;
673 if ( mSymbol )
674 lst.append( mSymbol.get() );
675
676 const auto constMActiveChildren = mActiveChildren;
677 for ( Rule *rule : constMActiveChildren )
678 {
679 lst += rule->symbolsForFeature( feature, context );
680 }
681 return lst;
682}
683
685{
686 QSet< QString> res;
687 if ( !isFilterOK( feature, context ) )
688 return res;
689
690 res.insert( mRuleKey );
691
692 // first determine if any non else rules match at this level
693 bool matchedNonElseRule = false;
694 for ( Rule *rule : std::as_const( mActiveChildren ) )
695 {
696 if ( rule->isElse() )
697 {
698 continue;
699 }
700 if ( rule->willRenderFeature( feature, context ) )
701 {
702 res.unite( rule->legendKeysForFeature( feature, context ) );
703 matchedNonElseRule = true;
704 }
705 }
706
707 // second chance -- allow else rules to take effect if valid
708 if ( !matchedNonElseRule )
709 {
710 for ( Rule *rule : std::as_const( mActiveChildren ) )
711 {
712 if ( rule->isElse() )
713 {
714 if ( rule->children().isEmpty() )
715 {
716 RuleList lst = rulesForFeature( feature, context, false );
717 lst.removeOne( rule );
718
719 if ( lst.empty() )
720 {
721 res.unite( rule->legendKeysForFeature( feature, context ) );
722 }
723 }
724 else
725 {
726 res.unite( rule->legendKeysForFeature( feature, context ) );
727 }
728 }
729 }
730 }
731 return res;
732}
733
735{
736 RuleList lst;
737 if ( ! isFilterOK( feature, context ) || ( context && ! isScaleOK( context->rendererScale() ) ) )
738 return lst;
739
740 if ( mSymbol )
741 lst.append( this );
742
743 RuleList listChildren = children();
744 if ( onlyActive )
745 listChildren = mActiveChildren;
746
747 const auto constListChildren = listChildren;
748 for ( Rule *rule : constListChildren )
749 {
750 lst += rule->rulesForFeature( feature, context, onlyActive );
751 }
752 return lst;
753}
754
756{
757 if ( mSymbol )
758 mSymbol->stopRender( context );
759
760 const auto constMActiveChildren = mActiveChildren;
761 for ( Rule *rule : constMActiveChildren )
762 {
763 rule->stopRender( context );
764 }
765
766 mActiveChildren.clear();
767 mSymbolNormZLevels.clear();
768}
769
770QgsRuleBasedRenderer::Rule *QgsRuleBasedRenderer::Rule::create( QDomElement &ruleElem, QgsSymbolMap &symbolMap, bool reuseId )
771{
772 QString symbolIdx = ruleElem.attribute( QStringLiteral( "symbol" ) );
773 QgsSymbol *symbol = nullptr;
774 if ( !symbolIdx.isEmpty() )
775 {
776 if ( symbolMap.contains( symbolIdx ) )
777 {
778 symbol = symbolMap.take( symbolIdx );
779 }
780 else
781 {
782 QgsDebugError( "symbol for rule " + symbolIdx + " not found!" );
783 }
784 }
785
786 QString filterExp = ruleElem.attribute( QStringLiteral( "filter" ) );
787 QString label = ruleElem.attribute( QStringLiteral( "label" ) );
788 QString description = ruleElem.attribute( QStringLiteral( "description" ) );
789 int scaleMinDenom = ruleElem.attribute( QStringLiteral( "scalemindenom" ), QStringLiteral( "0" ) ).toInt();
790 int scaleMaxDenom = ruleElem.attribute( QStringLiteral( "scalemaxdenom" ), QStringLiteral( "0" ) ).toInt();
791 QString ruleKey;
792 if ( reuseId )
793 ruleKey = ruleElem.attribute( QStringLiteral( "key" ) );
794 else
795 ruleKey = QUuid::createUuid().toString();
796 Rule *rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
797
798 if ( !ruleKey.isEmpty() )
799 rule->mRuleKey = ruleKey;
800
801 rule->setActive( ruleElem.attribute( QStringLiteral( "checkstate" ), QStringLiteral( "1" ) ).toInt() );
802
803 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
804 while ( !childRuleElem.isNull() )
805 {
806 Rule *childRule = create( childRuleElem, symbolMap );
807 if ( childRule )
808 {
809 rule->appendChild( childRule );
810 }
811 else
812 {
813 QgsDebugError( QStringLiteral( "failed to init a child rule!" ) );
814 }
815 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
816 }
817
818 return rule;
819}
820
822{
823 RuleList l;
824 for ( QgsRuleBasedRenderer::Rule *c : mChildren )
825 {
826 l += c;
827 l += c->descendants();
828 }
829 return l;
830}
831
833{
834 if ( ruleElem.localName() != QLatin1String( "Rule" ) )
835 {
836 QgsDebugError( QStringLiteral( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
837 return nullptr;
838 }
839
840 QString label, description, filterExp;
841 int scaleMinDenom = 0, scaleMaxDenom = 0;
842 QgsSymbolLayerList layers;
843
844 // retrieve the Rule element child nodes
845 QDomElement childElem = ruleElem.firstChildElement();
846 while ( !childElem.isNull() )
847 {
848 if ( childElem.localName() == QLatin1String( "Name" ) )
849 {
850 // <se:Name> tag contains the rule identifier,
851 // so prefer title tag for the label property value
852 if ( label.isEmpty() )
853 label = childElem.firstChild().nodeValue();
854 }
855 else if ( childElem.localName() == QLatin1String( "Description" ) )
856 {
857 // <se:Description> can contains a title and an abstract
858 QDomElement titleElem = childElem.firstChildElement( QStringLiteral( "Title" ) );
859 if ( !titleElem.isNull() )
860 {
861 label = titleElem.firstChild().nodeValue();
862 }
863
864 QDomElement abstractElem = childElem.firstChildElement( QStringLiteral( "Abstract" ) );
865 if ( !abstractElem.isNull() )
866 {
867 description = abstractElem.firstChild().nodeValue();
868 }
869 }
870 else if ( childElem.localName() == QLatin1String( "Abstract" ) )
871 {
872 // <sld:Abstract> (v1.0)
873 description = childElem.firstChild().nodeValue();
874 }
875 else if ( childElem.localName() == QLatin1String( "Title" ) )
876 {
877 // <sld:Title> (v1.0)
878 label = childElem.firstChild().nodeValue();
879 }
880 else if ( childElem.localName() == QLatin1String( "Filter" ) )
881 {
883 if ( filter )
884 {
885 if ( filter->hasParserError() )
886 {
887 QgsDebugError( "parser error: " + filter->parserErrorString() );
888 }
889 else
890 {
891 filterExp = filter->expression();
892 }
893 delete filter;
894 }
895 }
896 else if ( childElem.localName() == QLatin1String( "ElseFilter" ) )
897 {
898 filterExp = QLatin1String( "ELSE" );
899
900 }
901 else if ( childElem.localName() == QLatin1String( "MinScaleDenominator" ) )
902 {
903 bool ok;
904 int v = childElem.firstChild().nodeValue().toInt( &ok );
905 if ( ok )
906 scaleMinDenom = v;
907 }
908 else if ( childElem.localName() == QLatin1String( "MaxScaleDenominator" ) )
909 {
910 bool ok;
911 int v = childElem.firstChild().nodeValue().toInt( &ok );
912 if ( ok )
913 scaleMaxDenom = v;
914 }
915 else if ( childElem.localName().endsWith( QLatin1String( "Symbolizer" ) ) )
916 {
917 // create symbol layers for this symbolizer
918 QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
919 }
920
921 childElem = childElem.nextSiblingElement();
922 }
923
924 // now create the symbol
925 QgsSymbol *symbol = nullptr;
926 if ( !layers.isEmpty() )
927 {
928 switch ( geomType )
929 {
931 symbol = new QgsLineSymbol( layers );
932 break;
933
935 symbol = new QgsFillSymbol( layers );
936 break;
937
939 symbol = new QgsMarkerSymbol( layers );
940 break;
941
942 default:
943 QgsDebugError( QStringLiteral( "invalid geometry type: found %1" ).arg( qgsEnumValueToKey( geomType ) ) );
944 return nullptr;
945 }
946 }
947
948 // and then create and return the new rule
949 return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
950}
951
952
954
956 : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
957 , mRootRule( root )
958{
959}
960
962 : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
963{
964 mRootRule = new Rule( nullptr ); // root has no symbol, no filter etc - just a container
965 mRootRule->appendChild( new Rule( defaultSymbol ) );
966}
967
972
973
975{
976 // not used at all
977 return nullptr;
978}
979
981{
983
984 std::function< void( Rule *rule ) > exploreRule;
985 exploreRule = [&res, &exploreRule]( Rule * rule )
986 {
987 if ( !rule )
988 return;
989
990 if ( QgsSymbol *symbol = rule->symbol() )
991 {
992 if ( symbol->flags().testFlag( Qgis::SymbolFlag::AffectsLabeling ) )
994 }
995
996 for ( Rule *child : rule->children() )
997 {
998 exploreRule( child );
999 }
1000 };
1001 exploreRule( mRootRule );
1002
1003 return res;
1004}
1005
1007 QgsRenderContext &context,
1008 int layer,
1009 bool selected,
1010 bool drawVertexMarker )
1011{
1012 Q_UNUSED( layer )
1013
1014 int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
1015 mCurrentFeatures.append( FeatureToRender( feature, flags ) );
1016
1017 // check each active rule
1019}
1020
1021
1023{
1024 QgsFeatureRenderer::startRender( context, fields );
1025
1026 // prepare active children
1027 mRootRule->startRender( context, fields, mFilter );
1028
1029 QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
1030 QList<int> symbolZLevels( symbolZLevelsSet.begin(), symbolZLevelsSet.end() );
1031 std::sort( symbolZLevels.begin(), symbolZLevels.end() );
1032
1033 // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
1034 // and prepare rendering queue
1035 QMap<int, int> zLevelsToNormLevels;
1036 int maxNormLevel = -1;
1037 const auto constSymbolZLevels = symbolZLevels;
1038 for ( int zLevel : constSymbolZLevels )
1039 {
1040 zLevelsToNormLevels[zLevel] = ++maxNormLevel;
1041 mRenderQueue.append( RenderLevel( zLevel ) );
1042 QgsDebugMsgLevel( QStringLiteral( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ), 4 );
1043 }
1044
1045 mRootRule->setNormZLevels( zLevelsToNormLevels );
1046}
1047
1052
1054{
1056
1057 //
1058 // do the actual rendering
1059 //
1060
1061 // go through all levels
1062 if ( !context.renderingStopped() )
1063 {
1064 const auto constMRenderQueue = mRenderQueue;
1065 for ( const RenderLevel &level : constMRenderQueue )
1066 {
1067 //QgsDebugMsgLevel(QString("level %1").arg(level.zIndex), 2);
1068 // go through all jobs at the level
1069 for ( const RenderJob *job : std::as_const( level.jobs ) )
1070 {
1071 context.expressionContext().setFeature( job->ftr.feat );
1072 //QgsDebugMsgLevel(QString("job fid %1").arg(job->f->id()), 2);
1073 // render feature - but only with symbol layers with specified zIndex
1074 QgsSymbol *s = job->symbol;
1075 int count = s->symbolLayerCount();
1076 for ( int i = 0; i < count; i++ )
1077 {
1078 // TODO: better solution for this
1079 // renderFeatureWithSymbol asks which symbol layer to draw
1080 // but there are multiple transforms going on!
1081 if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
1082 {
1083 int flags = job->ftr.flags;
1084 renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
1085 }
1086 }
1087 }
1088 }
1089 }
1090
1091 // clean current features
1092 mCurrentFeatures.clear();
1093
1094 // clean render queue
1095 mRenderQueue.clear();
1096
1097 // clean up rules from temporary stuff
1098 mRootRule->stopRender( context );
1099}
1100
1102{
1103 return mFilter;
1104}
1105
1106QSet<QString> QgsRuleBasedRenderer::usedAttributes( const QgsRenderContext &context ) const
1107{
1108 return mRootRule->usedAttributes( context );
1109}
1110
1115
1117{
1119
1120 // normally with clone() the individual rules get new keys (UUID), but here we want to keep
1121 // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
1122 clonedRoot->setRuleKey( mRootRule->ruleKey() );
1123 RuleList origDescendants = mRootRule->descendants();
1124 RuleList clonedDescendants = clonedRoot->descendants();
1125 Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
1126 for ( int i = 0; i < origDescendants.count(); ++i )
1127 clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
1128
1129 QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( clonedRoot );
1130
1131 copyRendererData( r );
1132 return r;
1133}
1134
1135void QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1136{
1137 mRootRule->toSld( doc, element, props );
1138}
1139
1140// TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
1142{
1143 return mRootRule->symbols( context );
1144}
1145
1146QDomElement QgsRuleBasedRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
1147{
1148 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1149 rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "RuleRenderer" ) );
1150
1152
1153 QDomElement rulesElem = mRootRule->save( doc, symbols );
1154 rulesElem.setTagName( QStringLiteral( "rules" ) ); // instead of just "rule"
1155 rendererElem.appendChild( rulesElem );
1156
1157 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
1158 rendererElem.appendChild( symbolsElem );
1159
1160 saveRendererData( doc, rendererElem, context );
1161
1162 return rendererElem;
1163}
1164
1166{
1167 return true;
1168}
1169
1171{
1172 Rule *rule = mRootRule->findRuleByKey( key );
1173 return rule ? rule->active() : true;
1174}
1175
1176void QgsRuleBasedRenderer::checkLegendSymbolItem( const QString &key, bool state )
1177{
1178 Rule *rule = mRootRule->findRuleByKey( key );
1179 if ( rule )
1180 rule->setActive( state );
1181}
1182
1183QString QgsRuleBasedRenderer::legendKeyToExpression( const QString &key, QgsVectorLayer *, bool &ok ) const
1184{
1185 ok = false;
1186 Rule *rule = mRootRule->findRuleByKey( key );
1187 if ( !rule )
1188 return QString();
1189
1190 std::function<QString( Rule *rule )> ruleToExpression;
1191 ruleToExpression = [&ruleToExpression]( Rule * rule ) -> QString
1192 {
1193 if ( rule->isElse() && rule->parent() )
1194 {
1195 // gather the expressions for all other rules on this level and invert them
1196
1197 QStringList otherRules;
1198 const QList<QgsRuleBasedRenderer::Rule *> siblings = rule->parent()->children();
1199 for ( Rule *sibling : siblings )
1200 {
1201 if ( sibling == rule || sibling->isElse() )
1202 continue;
1203
1204 const QString siblingExpression = ruleToExpression( sibling );
1205 if ( siblingExpression.isEmpty() )
1206 return QStringLiteral( "FALSE" ); // nothing will match this rule
1207
1208 otherRules.append( siblingExpression );
1209 }
1210
1211 if ( otherRules.empty() )
1212 return QStringLiteral( "TRUE" ); // all features will match the else rule
1213 else
1214 return (
1215 otherRules.size() > 1
1216 ? QStringLiteral( "NOT ((%1))" ).arg( otherRules.join( QLatin1String( ") OR (" ) ) )
1217 : QStringLiteral( "NOT (%1)" ).arg( otherRules.at( 0 ) )
1218 );
1219 }
1220 else
1221 {
1222 QStringList ruleParts;
1223 if ( !rule->filterExpression().isEmpty() )
1224 ruleParts.append( rule->filterExpression() );
1225
1226 if ( !qgsDoubleNear( rule->minimumScale(), 0.0 ) )
1227 ruleParts.append( QStringLiteral( "@map_scale <= %1" ).arg( rule->minimumScale() ) );
1228
1229 if ( !qgsDoubleNear( rule->maximumScale(), 0.0 ) )
1230 ruleParts.append( QStringLiteral( "@map_scale >= %1" ).arg( rule->maximumScale() ) );
1231
1232 if ( !ruleParts.empty() )
1233 {
1234 return (
1235 ruleParts.size() > 1
1236 ? QStringLiteral( "(%1)" ).arg( ruleParts.join( QLatin1String( ") AND (" ) ) )
1237 : ruleParts.at( 0 )
1238 );
1239 }
1240 else
1241 {
1242 return QString();
1243 }
1244 }
1245 };
1246
1247 QStringList parts;
1248 while ( rule )
1249 {
1250 const QString ruleFilter = ruleToExpression( rule );
1251 if ( !ruleFilter.isEmpty() )
1252 parts.append( ruleFilter );
1253
1254 rule = rule->parent();
1255 }
1256
1257 ok = true;
1258 return parts.empty() ? QStringLiteral( "TRUE" )
1259 : ( parts.size() > 1
1260 ? QStringLiteral( "(%1)" ).arg( parts.join( QLatin1String( ") AND (" ) ) )
1261 : parts.at( 0 ) );
1262}
1263
1264void QgsRuleBasedRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
1265{
1266 Rule *rule = mRootRule->findRuleByKey( key );
1267 if ( rule )
1268 rule->setSymbol( symbol );
1269 else
1270 delete symbol;
1271}
1272
1277
1278
1280{
1281 // load symbols
1282 QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
1283 if ( symbolsElem.isNull() )
1284 return nullptr;
1285
1286 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
1287
1288 QDomElement rulesElem = element.firstChildElement( QStringLiteral( "rules" ) );
1289
1290 Rule *root = Rule::create( rulesElem, symbolMap );
1291 if ( !root )
1292 return nullptr;
1293
1295
1296 // delete symbols if there are any more
1298
1299 return r;
1300}
1301
1303{
1304 // retrieve child rules
1305 Rule *root = nullptr;
1306
1307 QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
1308 while ( !ruleElem.isNull() )
1309 {
1310 Rule *child = Rule::createFromSld( ruleElem, geomType );
1311 if ( child )
1312 {
1313 // create the root rule if not done before
1314 if ( !root )
1315 root = new Rule( nullptr );
1316
1317 root->appendChild( child );
1318 }
1319
1320 ruleElem = ruleElem.nextSiblingElement( QStringLiteral( "Rule" ) );
1321 }
1322
1323 if ( !root )
1324 {
1325 // no valid rules was found
1326 return nullptr;
1327 }
1328
1329 // create and return the new renderer
1330 return new QgsRuleBasedRenderer( root );
1331}
1332
1335
1337{
1338 QString attr = r->classAttribute();
1339 // categorizedAttr could be either an attribute name or an expression.
1340 // the only way to differentiate is to test it as an expression...
1341 QgsExpression testExpr( attr );
1342 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1343 {
1344 //not an expression, so need to quote column name
1345 attr = QgsExpression::quotedColumnRef( attr );
1346 }
1347
1348 const auto constCategories = r->categories();
1349 for ( const QgsRendererCategory &cat : constCategories )
1350 {
1351 QString value;
1352 // not quoting numbers saves a type cast
1353 if ( QgsVariantUtils::isNull( cat.value() ) )
1354 value = "NULL";
1355 else if ( cat.value().userType() == QMetaType::Type::Int )
1356 value = cat.value().toString();
1357 else if ( cat.value().userType() == QMetaType::Type::Double )
1358 // we loose precision here - so we may miss some categories :-(
1359 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1360 value = QString::number( cat.value().toDouble(), 'f', 4 );
1361 else
1362 value = QgsExpression::quotedString( cat.value().toString() );
1363 const QString filter = QStringLiteral( "%1 %2 %3" ).arg( attr, QgsVariantUtils::isNull( cat.value() ) ? QStringLiteral( "IS" ) : QStringLiteral( "=" ), value );
1364 const QString label = !cat.label().isEmpty() ? cat.label() :
1365 cat.value().isValid() ? value : QString();
1366 initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1367 }
1368}
1369
1371{
1372 QString attr = r->classAttribute();
1373 // categorizedAttr could be either an attribute name or an expression.
1374 // the only way to differentiate is to test it as an expression...
1375 QgsExpression testExpr( attr );
1376 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1377 {
1378 //not an expression, so need to quote column name
1379 attr = QgsExpression::quotedColumnRef( attr );
1380 }
1381 else if ( !testExpr.isField() )
1382 {
1383 //otherwise wrap expression in brackets
1384 attr = QStringLiteral( "(%1)" ).arg( attr );
1385 }
1386
1387 bool firstRange = true;
1388 const auto constRanges = r->ranges();
1389 for ( const QgsRendererRange &rng : constRanges )
1390 {
1391 // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1392 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1393 QString filter = QStringLiteral( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
1394 QString::number( rng.lowerValue(), 'f', 4 ),
1395 QString::number( rng.upperValue(), 'f', 4 ) );
1396 firstRange = false;
1397 QString label = rng.label().isEmpty() ? filter : rng.label();
1398 initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1399 }
1400}
1401
1403{
1404 std::sort( scales.begin(), scales.end() ); // make sure the scales are in ascending order
1405 double oldScale = initialRule->maximumScale();
1406 double maxDenom = initialRule->minimumScale();
1407 QgsSymbol *symbol = initialRule->symbol();
1408 const auto constScales = scales;
1409 for ( int scale : constScales )
1410 {
1411 if ( initialRule->maximumScale() >= scale )
1412 continue; // jump over the first scales out of the interval
1413 if ( maxDenom != 0 && maxDenom <= scale )
1414 break; // ignore the latter scales out of the interval
1415 initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1416 oldScale = scale;
1417 }
1418 // last rule
1419 initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1420}
1421
1423{
1424 QString msg( QStringLiteral( "Rule-based renderer:\n" ) );
1425 msg += mRootRule->dump();
1426 return msg;
1427}
1428
1430{
1431 return mRootRule->willRenderFeature( feature, &context );
1432}
1433
1435{
1436 return mRootRule->symbolsForFeature( feature, &context );
1437}
1438
1440{
1441 return mRootRule->symbolsForFeature( feature, &context );
1442}
1443
1444QSet< QString > QgsRuleBasedRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
1445{
1446 return mRootRule->legendKeysForFeature( feature, &context );
1447}
1448
1450{
1451 return mRootRule->accept( visitor );
1452}
1453
1455{
1456 std::unique_ptr< QgsRuleBasedRenderer > r;
1457 if ( renderer->type() == QLatin1String( "RuleRenderer" ) )
1458 {
1459 r.reset( dynamic_cast<QgsRuleBasedRenderer *>( renderer->clone() ) );
1460 }
1461 else if ( renderer->type() == QLatin1String( "singleSymbol" ) )
1462 {
1463 const QgsSingleSymbolRenderer *singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRenderer *>( renderer );
1464 if ( !singleSymbolRenderer )
1465 return nullptr;
1466
1467 std::unique_ptr< QgsSymbol > origSymbol( singleSymbolRenderer->symbol()->clone() );
1468 r = std::make_unique< QgsRuleBasedRenderer >( origSymbol.release() );
1469 }
1470 else if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
1471 {
1472 const QgsCategorizedSymbolRenderer *categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRenderer *>( renderer );
1473 if ( !categorizedRenderer )
1474 return nullptr;
1475
1476 QString attr = categorizedRenderer->classAttribute();
1477 // categorizedAttr could be either an attribute name or an expression.
1478 bool isField = false;
1479 if ( layer )
1480 {
1481 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1482 }
1483 else
1484 {
1485 QgsExpression testExpr( attr );
1486 isField = testExpr.hasParserError() || testExpr.isField();
1487 }
1488 if ( isField && !attr.contains( '\"' ) )
1489 {
1490 //not an expression, so need to quote column name
1491 attr = QgsExpression::quotedColumnRef( attr );
1492 }
1493
1494 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1495
1496 QString expression;
1497 QString value;
1498 QgsRendererCategory category;
1499 for ( const QgsRendererCategory &category : categorizedRenderer->categories() )
1500 {
1501 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1502
1503 rule->setLabel( category.label() );
1504
1505 //We first define the rule corresponding to the category
1506 if ( category.value().userType() == QMetaType::Type::QVariantList )
1507 {
1508 QStringList values;
1509 const QVariantList list = category.value().toList();
1510 for ( const QVariant &v : list )
1511 {
1512 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1513 if ( QVariant( v ).convert( QMetaType::Type::Double ) )
1514 {
1515 values << v.toString();
1516 }
1517 else
1518 {
1519 values << QgsExpression::quotedString( v.toString() );
1520 }
1521 }
1522
1523 if ( values.empty() )
1524 {
1525 expression = QStringLiteral( "ELSE" );
1526 }
1527 else
1528 {
1529 expression = QStringLiteral( "%1 IN (%2)" ).arg( attr, values.join( ',' ) );
1530 }
1531 }
1532 else
1533 {
1534 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1535 if ( category.value().convert( QMetaType::Type::Double ) )
1536 {
1537 value = category.value().toString();
1538 }
1539 else
1540 {
1541 value = QgsExpression::quotedString( category.value().toString() );
1542 }
1543
1544 //An empty category is equivalent to the ELSE keyword
1545 if ( value == QLatin1String( "''" ) )
1546 {
1547 expression = QStringLiteral( "ELSE" );
1548 }
1549 else
1550 {
1551 expression = QStringLiteral( "%1 = %2" ).arg( attr, value );
1552 }
1553 }
1554 rule->setFilterExpression( expression );
1555
1556 //Then we construct an equivalent symbol.
1557 //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1558 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1559
1560 std::unique_ptr< QgsSymbol > origSymbol( category.symbol()->clone() );
1561 rule->setSymbol( origSymbol.release() );
1562
1563 rootrule->appendChild( rule.release() );
1564 }
1565
1566 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1567 }
1568 else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1569 {
1570 const QgsGraduatedSymbolRenderer *graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1571 if ( !graduatedRenderer )
1572 return nullptr;
1573
1574 QString attr = graduatedRenderer->classAttribute();
1575 // categorizedAttr could be either an attribute name or an expression.
1576 // the only way to differentiate is to test it as an expression...
1577 bool isField = false;
1578 if ( layer )
1579 {
1580 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1581 }
1582 else
1583 {
1584 QgsExpression testExpr( attr );
1585 isField = testExpr.hasParserError() || testExpr.isField();
1586 }
1587 if ( isField && !attr.contains( '\"' ) )
1588 {
1589 //not an expression, so need to quote column name
1590 attr = QgsExpression::quotedColumnRef( attr );
1591 }
1592 else if ( !isField )
1593 {
1594 //otherwise wrap expression in brackets
1595 attr = QStringLiteral( "(%1)" ).arg( attr );
1596 }
1597
1598 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1599
1600 QString expression;
1601 QgsRendererRange range;
1602 for ( int i = 0; i < graduatedRenderer->ranges().size(); ++i )
1603 {
1604 range = graduatedRenderer->ranges().value( i );
1605 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1606 rule->setLabel( range.label() );
1607 if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
1608 {
1609 expression = attr + " >= " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1610 attr + " <= " + QString::number( range.upperValue(), 'f' );
1611 }
1612 else
1613 {
1614 expression = attr + " > " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1615 attr + " <= " + QString::number( range.upperValue(), 'f' );
1616 }
1617 rule->setFilterExpression( expression );
1618
1619 //Then we construct an equivalent symbol.
1620 //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1621 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1622
1623 std::unique_ptr< QgsSymbol > symbol( range.symbol()->clone() );
1624 rule->setSymbol( symbol.release() );
1625
1626 rootrule->appendChild( rule.release() );
1627 }
1628
1629 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1630 }
1631 else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1632 {
1633 if ( const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer ) )
1634 return convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1635 }
1636 else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1637 {
1638 if ( const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer ) )
1639 r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1640 }
1641 else if ( renderer->type() == QLatin1String( "mergedFeatureRenderer" ) )
1642 {
1643 if ( const QgsMergedFeatureRenderer *mergedRenderer = dynamic_cast<const QgsMergedFeatureRenderer *>( renderer ) )
1644 r.reset( convertFromRenderer( mergedRenderer->embeddedRenderer() ) );
1645 }
1646 else if ( renderer->type() == QLatin1String( "embeddedSymbol" ) && layer )
1647 {
1648 const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
1649
1650 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1651
1654 req.setNoAttributes();
1655 QgsFeatureIterator it = layer->getFeatures( req );
1656 QgsFeature feature;
1657 while ( it.nextFeature( feature ) && rootrule->children().size() < 500 )
1658 {
1659 if ( feature.embeddedSymbol() )
1660 {
1661 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1662 rule->setFilterExpression( QStringLiteral( "$id=%1" ).arg( feature.id() ) );
1663 rule->setLabel( QString::number( feature.id() ) );
1664 rule->setSymbol( feature.embeddedSymbol()->clone() );
1665 rootrule->appendChild( rule.release() );
1666 }
1667 }
1668
1669 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1670 rule->setFilterExpression( QStringLiteral( "ELSE" ) );
1671 rule->setLabel( QObject::tr( "All other features" ) );
1672 rule->setSymbol( embeddedRenderer->defaultSymbol()->clone() );
1673 rootrule->appendChild( rule.release() );
1674
1675 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1676 }
1677
1678 if ( r )
1679 {
1680 renderer->copyRendererData( r.get() );
1681 }
1682
1683 return r.release();
1684}
1685
1686void QgsRuleBasedRenderer::convertToDataDefinedSymbology( QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField )
1687{
1688 QString sizeExpression;
1689 switch ( symbol->type() )
1690 {
1692 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1693 {
1694 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( symbol->symbolLayer( j ) );
1695 if ( ! sizeScaleField.isEmpty() )
1696 {
1697 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1699 }
1700 if ( ! rotationField.isEmpty() )
1701 {
1703 }
1704 }
1705 break;
1707 if ( ! sizeScaleField.isEmpty() )
1708 {
1709 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1710 {
1711 if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "SimpleLine" ) )
1712 {
1713 QgsLineSymbolLayer *lsl = static_cast<QgsLineSymbolLayer *>( symbol->symbolLayer( j ) );
1714 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
1716 }
1717 if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "MarkerLine" ) )
1718 {
1719 QgsSymbol *marker = symbol->symbolLayer( j )->subSymbol();
1720 for ( int k = 0; k < marker->symbolLayerCount(); ++k )
1721 {
1722 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( marker->symbolLayer( k ) );
1723 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1725 }
1726 }
1727 }
1728 }
1729 break;
1730 default:
1731 break;
1732 }
1733}
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
@ EmbeddedSymbols
Retrieve any embedded feature symbology.
QFlags< FeatureRendererFlag > FeatureRendererFlags
Flags controlling behavior of vector feature renderers.
Definition qgis.h:772
@ AffectsLabeling
If present, indicates that the renderer will participate in the map labeling problem.
GeometryType
The geometry types are used to group Qgis::WkbType in a coarse way.
Definition qgis.h:337
@ Polygon
Polygons.
@ Marker
Marker symbol.
@ Line
Line symbol.
@ AffectsLabeling
If present, indicates that the symbol will participate in the map labeling problem.
const QgsCategoryList & categories() const
Returns a list of all categories recognized by the renderer.
QString classAttribute() const
Returns the class attribute for the renderer, which is the field name or expression string from the l...
A vector feature renderer which uses embedded feature symbology to render per-feature symbols.
void setFeature(const QgsFeature &feature)
Convenience function for setting a feature for the context.
Class for parsing and evaluation of expressions (formerly called "search strings").
static QString quotedString(QString text)
Returns a quoted version of a string (in single quotes)
bool hasParserError() const
Returns true if an error occurred when parsing the input expression.
bool isField() const
Checks whether an expression consists only of a single field reference.
static QString quotedColumnRef(QString name)
Returns a quoted column reference (in double quotes)
static int expressionToLayerFieldIndex(const QString &expression, const QgsVectorLayer *layer)
Attempts to resolve an expression to a field index from the given layer.
static bool attemptReduceToInClause(const QStringList &expressions, QString &result)
Attempts to reduce a list of expressions to a single "field IN (val1, val2, ... )" type expression.
Wrapper for iterator of features from vector data provider or vector layer.
bool nextFeature(QgsFeature &f)
Fetch next feature and stores in f, returns true on success.
Abstract base class for all 2D vector feature renderers.
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.
This class 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.
QgsInvertedPolygonRenderer is a polygon-only feature renderer used to display features inverted,...
The class stores information about one class/rule of a vector layer renderer in a unified way that ca...
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.
QgsMergedFeatureRenderer is a polygon or line-only feature renderer used to renderer a set of feature...
static QgsExpression * expressionFromOgcFilter(const QDomElement &element, QgsVectorLayer *layer=nullptr)
Parse XML with OGC filter into QGIS expression.
An abstract base class for distance based point renderers (e.g., clusterer and displacement renderers...
static QgsProperty fromExpression(const QString &expression, bool isActive=true)
Returns a new ExpressionBasedProperty created from the specified expression.
static QgsProperty fromField(const QString &fieldName, bool isActive=true)
Returns a new FieldBasedProperty created from the specified field name.
The class is used as a container of context for various read/write operations on other objects.
Contains information about the context of a rendering operation.
double rendererScale() const
Returns the renderer map scale.
QgsExpressionContext & expressionContext()
Gets the expression context.
bool renderingStopped() const
Returns true if the rendering operation has been stopped and any ongoing rendering should be canceled...
Represents an individual category (class) from a QgsCategorizedSymbolRenderer.
QgsSymbol * symbol() const
Returns the symbol which will be used to render this category.
QVariant value() const
Returns the value corresponding to this category.
QString label() const
Returns the label for this category, which is used to represent the category within legends and the l...
QString label() const
Returns the label used for the range.
QgsSymbol * symbol() const
Returns the symbol used for the range.
double upperValue() const
Returns the upper bound of the range.
double lowerValue() const
Returns the lower bound of the range.
This class keeps data about a rules for rule-based renderer.
bool accept(QgsStyleEntityVisitorInterface *visitor) const
Accepts the specified symbology visitor, causing it to visit all child rules associated with the rule...
QgsRuleBasedRenderer::RuleList descendants() const
Returns all children, grand-children, grand-grand-children, grand-gra... you get it.
void setSymbol(QgsSymbol *sym)
Sets a new symbol (or nullptr). Deletes old symbol.
void removeChild(QgsRuleBasedRenderer::Rule *rule)
delete child rule
QgsRuleBasedRenderer::Rule * findRuleByKey(const QString &key)
Try to find a rule given its unique key.
void insertChild(int i, QgsRuleBasedRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
QString ruleKey() const
Unique rule identifier (for identification of rule within renderer)
bool needsGeometry() const
Returns true if this rule or one of its children needs the geometry to be applied.
QgsRuleBasedRenderer::Rule * takeChild(QgsRuleBasedRenderer::Rule *rule)
take child rule out, set parent as nullptr
const QgsRuleBasedRenderer::RuleList & children() const
Returns all children rules of this rule.
RenderResult
The result of rendering a rule.
@ Rendered
Something was rendered.
QgsRuleBasedRenderer::RuleList rulesForFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr, bool onlyActive=true)
Returns the list of rules used to render the feature in a specific context.
double maximumScale() const
Returns the maximum map scale (i.e.
QgsRuleBasedRenderer::Rule * parent()
The parent rule.
void setIsElse(bool iselse)
Sets if this rule is an ELSE rule.
QgsSymbolList symbolsForFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
tell which symbols will be used to render the feature
bool isElse() const
Check if this rule is an ELSE rule.
QSet< QString > legendKeysForFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
Returns which legend keys match the feature.
QgsRuleBasedRenderer::Rule * clone() const
clone this rule, return new instance
bool willRenderFeature(const QgsFeature &feature, QgsRenderContext *context=nullptr)
only tell whether a feature will be rendered without actually rendering it
void removeChildAt(int i)
delete child rule
void setActive(bool state)
Sets if this rule is active.
Rule(QgsSymbol *symbol, int maximumScale=0, int minimumScale=0, const QString &filterExp=QString(), const QString &label=QString(), const QString &description=QString(), bool elseRule=false)
Constructor takes ownership of the symbol.
bool isFilterOK(const QgsFeature &f, QgsRenderContext *context=nullptr) const
Check if a given feature shall be rendered by this rule.
QgsSymbolList symbols(const QgsRenderContext &context=QgsRenderContext()) const
bool isScaleOK(double scale) const
Check if this rule applies for a given scale.
static QgsRuleBasedRenderer::Rule * createFromSld(QDomElement &element, Qgis::GeometryType geomType)
Create a rule from the SLD provided in element and for the specified geometry type.
void setNormZLevels(const QMap< int, int > &zLevelsToNormLevels)
assign normalized z-levels [0..N-1] for this rule's symbol for quick access during rendering
QDomElement save(QDomDocument &doc, QgsSymbolMap &symbolMap) const
void appendChild(QgsRuleBasedRenderer::Rule *rule)
add child rule, take ownership, sets this as parent
QgsRuleBasedRenderer::Rule * takeChildAt(int i)
take child rule out, set parent as nullptr
QSet< int > collectZLevels()
Gets all used z-levels from this rule and children.
double minimumScale() const
Returns the minimum map scale (i.e.
void stopRender(QgsRenderContext &context)
Stop a rendering process.
bool hasActiveChildren() const
Returns true if the rule has any active children.
QgsRuleBasedRenderer::Rule::RenderResult renderFeature(QgsRuleBasedRenderer::FeatureToRender &featToRender, QgsRenderContext &context, QgsRuleBasedRenderer::RenderQueue &renderQueue)
Render a given feature, will recursively call subclasses and only render if the constraints apply.
QgsLegendSymbolList legendSymbolItems(int currentLevel=-1) const
QSet< QString > usedAttributes(const QgsRenderContext &context) const
Returns the attributes used to evaluate the expression of this rule.
void setFilterExpression(const QString &filterExp)
Set the expression used to check if a given feature shall be rendered with this rule.
QString dump(int indent=0) const
Dump for debug purpose.
void setRuleKey(const QString &key)
Override the assigned rule key (should be used just internally by rule-based renderer)
bool startRender(QgsRenderContext &context, const QgsFields &fields, QString &filter)
prepare the rule for rendering and its children (build active children array)
static QgsRuleBasedRenderer::Rule * create(QDomElement &ruleElem, QgsSymbolMap &symbolMap, bool reuseId=true)
Create a rule from an XML definition.
QString filterExpression() const
A filter that will check if this rule applies.
bool active() const
Returns if this rule is active.
void toSld(QDomDocument &doc, QDomElement &element, QVariantMap props) const
Saves the symbol layer as SLD.
Rule based renderer.
static void refineRuleCategories(QgsRuleBasedRenderer::Rule *initialRule, QgsCategorizedSymbolRenderer *r)
take a rule and create a list of new rules based on the categories from categorized symbol renderer
static void convertToDataDefinedSymbology(QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField=QString())
helper function to convert the size scale and rotation fields present in some other renderers to data...
bool legendSymbolItemChecked(const QString &key) override
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.
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.
QgsSymbol * symbol() const
Returns the symbol which will be rendered for every feature.
An interface for classes which can visit style entity (e.g.
@ SymbolRule
Rule based symbology or label child rule.
virtual bool visitExit(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor stops visiting a node.
virtual bool visitEnter(const QgsStyleEntityVisitorInterface::Node &node)
Called when the visitor starts visiting a node.
virtual bool visit(const QgsStyleEntityVisitorInterface::StyleLeaf &entity)
Called when the visitor will visit a style entity.
A symbol entity for QgsStyle databases.
Definition qgsstyle.h:1396
static void applyScaleDependency(QDomDocument &doc, QDomElement &ruleElem, QVariantMap &props)
Checks if the properties contain scaleMinDenom and scaleMaxDenom, if available, they are added into t...
static bool createFunctionElement(QDomDocument &doc, QDomElement &element, const QString &function)
static bool createSymbolLayerListFromSld(QDomElement &element, Qgis::GeometryType geomType, QList< QgsSymbolLayer * > &layers)
Creates a symbol layer list from a DOM element.
static void mergeScaleDependencies(double mScaleMinDenom, double mScaleMaxDenom, QVariantMap &props)
Merges the local scale limits, if any, with the ones already in the map, if any.
static void clearSymbolMap(QgsSymbolMap &symbols)
static QgsSymbolMap loadSymbols(QDomElement &element, const QgsReadWriteContext &context)
Reads a collection of symbols from XML and returns them in a map. Caller is responsible for deleting ...
static QDomElement saveSymbols(QgsSymbolMap &symbols, const QString &tagName, QDomDocument &doc, const QgsReadWriteContext &context)
Writes a collection of symbols to XML with specified tagName for the top-level element.
@ StrokeWidth
Stroke width.
virtual QString layerType() const =0
Returns a string that represents this layer type.
int renderingPass() const
Specifies the rendering pass in which this symbol layer should be rendered.
virtual void setDataDefinedProperty(Property key, const QgsProperty &property)
Sets a data defined property for the layer.
virtual QgsSymbol * subSymbol()
Returns the symbol's sub symbol, if present.
Abstract base class for all rendered symbols.
Definition qgssymbol.h: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:352
Qgis::SymbolType type() const
Returns the symbol's type.
Definition qgssymbol.h:293
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 data sets.
QgsFeatureIterator getFeatures(const QgsFeatureRequest &request=QgsFeatureRequest()) const FINAL
Queries the layer for features specified in request.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
QString qgsEnumValueToKey(const T &value, bool *returnOk=nullptr)
Returns the value for the given key of an enum.
Definition qgis.h:6108
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:5917
QList< QgsLegendSymbolItem > QgsLegendSymbolList
#define QgsDebugMsgLevel(str, level)
Definition qgslogger.h:39
#define QgsDebugError(str)
Definition qgslogger.h:38
#define RENDERER_TAG_NAME
Definition qgsrenderer.h:53
QMap< QString, QgsSymbol * > QgsSymbolMap
Definition qgsrenderer.h:48
QList< QgsSymbol * > QgsSymbolList
Definition qgsrenderer.h:47
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.