QGIS API Documentation 3.41.0-Master (d5b93354e9c)
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
379 //XXX: <se:Name> is the rule identifier, but our the Rule objects
380 // have no properties could be used as identifier. Use the label.
381 QDomElement nameElem = doc.createElement( QStringLiteral( "se:Name" ) );
382 nameElem.appendChild( doc.createTextNode( mLabel ) );
383 ruleElem.appendChild( nameElem );
384
385 if ( !mLabel.isEmpty() || !mDescription.isEmpty() )
386 {
387 QDomElement descrElem = doc.createElement( QStringLiteral( "se:Description" ) );
388 if ( !mLabel.isEmpty() )
389 {
390 QDomElement titleElem = doc.createElement( QStringLiteral( "se:Title" ) );
391 titleElem.appendChild( doc.createTextNode( mLabel ) );
392 descrElem.appendChild( titleElem );
393 }
394 if ( !mDescription.isEmpty() )
395 {
396 QDomElement abstractElem = doc.createElement( QStringLiteral( "se:Abstract" ) );
397 abstractElem.appendChild( doc.createTextNode( mDescription ) );
398 descrElem.appendChild( abstractElem );
399 }
400 ruleElem.appendChild( descrElem );
401 }
402
403 if ( !props.value( QStringLiteral( "filter" ), QString() ).toString().isEmpty() )
404 {
405 QgsSymbolLayerUtils::createFunctionElement( doc, ruleElem, props.value( QStringLiteral( "filter" ), QString() ).toString() );
406 }
407
408 QgsSymbolLayerUtils::applyScaleDependency( doc, ruleElem, props );
409
410 mSymbol->toSld( doc, ruleElem, props );
411
412 // Only create rules if symbol could be converted to SLD, and is not an "empty" symbol. Otherwise we do not generate a rule, as
413 // SLD spec requires a Symbolizer element to be present
415 {
416 element.appendChild( ruleElem );
417 }
418 }
419
420 // loop into children rule list
421 const auto constMChildren = mChildren;
422 for ( Rule *rule : constMChildren )
423 {
424 rule->toSld( doc, element, props );
425 }
426}
427
429{
430 mActiveChildren.clear();
431
432 if ( ! mIsActive )
433 return false;
434
435 // filter out rules which are not compatible with this scale
436 if ( !isScaleOK( context.rendererScale() ) )
437 return false;
438
439 // init this rule
440 if ( mFilter )
441 mFilter->prepare( &context.expressionContext() );
442 if ( mSymbol )
443 mSymbol->startRender( context, fields );
444
445 // init children
446 // build temporary list of active rules (usable with this scale)
447 QStringList subfilters;
448 const auto constMChildren = mChildren;
449 for ( Rule *rule : constMChildren )
450 {
451 QString subfilter;
452 if ( rule->startRender( context, fields, subfilter ) )
453 {
454 // only add those which are active with current scale
455 mActiveChildren.append( rule );
456 subfilters.append( subfilter );
457 }
458 }
459
460 // subfilters (on the same level) are joined with OR
461 // Finally they are joined with their parent (this) with AND
462 QString sf;
463 // If there are subfilters present (and it's not a single empty one), group them and join them with OR
464 if ( subfilters.length() > 1 || !subfilters.value( 0 ).isEmpty() )
465 {
466 if ( subfilters.contains( QStringLiteral( "TRUE" ) ) )
467 {
468 sf = QStringLiteral( "TRUE" );
469 }
470 else
471 {
472 // test for a common case -- all subfilters can be combined into a single "field in (...)" expression
473 if ( QgsExpression::attemptReduceToInClause( subfilters, sf ) )
474 {
475 // success! we can use a simple "field IN (...)" list!
476 }
477 // If we have more than 50 rules (to stay on the safe side) make a binary tree or SQLITE will fail,
478 // see: https://github.com/qgis/QGIS/issues/27269
479 else if ( subfilters.count() > 50 )
480 {
481 std::function<QString( const QStringList & )>bt = [ &bt ]( const QStringList & subf )
482 {
483 if ( subf.count( ) == 1 )
484 {
485 return subf.at( 0 );
486 }
487 else if ( subf.count( ) == 2 )
488 {
489 return subf.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
490 }
491 else
492 {
493 int midpos = static_cast<int>( subf.length() / 2 );
494 return QStringLiteral( "(%1) OR (%2)" ).arg( bt( subf.mid( 0, midpos ) ), bt( subf.mid( midpos ) ) );
495 }
496 };
497 sf = bt( subfilters );
498 }
499 else
500 {
501 sf = subfilters.join( QLatin1String( ") OR (" ) ).prepend( '(' ).append( ')' );
502 }
503 }
504 }
505
506 // Now join the subfilters with their parent (this) based on if
507 // * The parent is an else rule
508 // * The existence of parent filter and subfilters
509
510 // No filter expression: ELSE rule or catchall rule
511 if ( !mFilter )
512 {
513 if ( mSymbol || sf.isEmpty() )
514 filter = QStringLiteral( "TRUE" );
515 else
516 filter = sf;
517 }
518 else if ( mSymbol )
519 filter = mFilterExp;
520 else if ( !mFilterExp.trimmed().isEmpty() && !sf.isEmpty() )
521 filter = QStringLiteral( "(%1) AND (%2)" ).arg( mFilterExp, sf );
522 else if ( !mFilterExp.trimmed().isEmpty() )
523 filter = mFilterExp;
524 else if ( sf.isEmpty() )
525 filter = QStringLiteral( "TRUE" );
526 else
527 filter = sf;
528
529 filter = filter.trimmed();
530
531 return true;
532}
533
535{
536 return !mActiveChildren.empty();
537}
538
540{
541 QSet<int> symbolZLevelsSet;
542
543 // process this rule
544 if ( mSymbol )
545 {
546 // find out which Z-levels are used
547 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
548 {
549 symbolZLevelsSet.insert( mSymbol->symbolLayer( i )->renderingPass() );
550 }
551 }
552
553 // process children
554 QList<Rule *>::iterator it;
555 for ( it = mActiveChildren.begin(); it != mActiveChildren.end(); ++it )
556 {
557 Rule *rule = *it;
558 symbolZLevelsSet.unite( rule->collectZLevels() );
559 }
560 return symbolZLevelsSet;
561}
562
563void QgsRuleBasedRenderer::Rule::setNormZLevels( const QMap<int, int> &zLevelsToNormLevels )
564{
565 if ( mSymbol )
566 {
567 for ( int i = 0; i < mSymbol->symbolLayerCount(); i++ )
568 {
569 int normLevel = zLevelsToNormLevels.value( mSymbol->symbolLayer( i )->renderingPass() );
570 mSymbolNormZLevels.insert( normLevel );
571 }
572 }
573
574 // prepare list of normalized levels for each rule
575 const auto constMActiveChildren = mActiveChildren;
576 for ( Rule *rule : constMActiveChildren )
577 {
578 rule->setNormZLevels( zLevelsToNormLevels );
579 }
580}
581
582
584{
585 if ( !isFilterOK( featToRender.feat, &context ) )
586 return Filtered;
587
588 bool rendered = false;
589
590 // create job for this feature and this symbol, add to list of jobs
591 if ( mSymbol && mIsActive )
592 {
593 // add job to the queue: each symbol's zLevel must be added
594 const auto constMSymbolNormZLevels = mSymbolNormZLevels;
595 for ( int normZLevel : constMSymbolNormZLevels )
596 {
597 //QgsDebugMsgLevel(QString("add job at level %1").arg(normZLevel),2);
598 renderQueue[normZLevel].jobs.append( new RenderJob( featToRender, mSymbol.get() ) );
599 rendered = true;
600 }
601 }
602
603 bool matchedAChild = false;
604
605 // process children
606 const auto constMChildren = mChildren;
607 for ( Rule *rule : constMChildren )
608 {
609 // Don't process else rules yet
610 if ( !rule->isElse() )
611 {
612 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
613 // consider inactive items as "matched" so the else rule will ignore them
614 matchedAChild |= ( res == Rendered || res == Inactive );
615 rendered |= ( res == Rendered );
616 }
617 }
618
619 // If none of the rules passed then we jump into the else rules and process them.
620 if ( !matchedAChild )
621 {
622 const auto constMElseRules = mElseRules;
623 for ( Rule *rule : constMElseRules )
624 {
625 const RenderResult res = rule->renderFeature( featToRender, context, renderQueue );
626 matchedAChild |= ( res == Rendered || res == Inactive );
627 rendered |= res == Rendered;
628 }
629 }
630 if ( !mIsActive || ( mSymbol && !rendered ) || ( matchedAChild && !rendered ) )
631 return Inactive;
632 else if ( rendered )
633 return Rendered;
634 else
635 return Filtered;
636}
637
639{
640 if ( !isFilterOK( feature, context ) )
641 return false;
642
643 if ( mSymbol )
644 return true;
645
646 const auto constMActiveChildren = mActiveChildren;
647 for ( Rule *rule : constMActiveChildren )
648 {
649 if ( rule->isElse() )
650 {
651 if ( rule->children().isEmpty() )
652 {
653 RuleList lst = rulesForFeature( feature, context, false );
654 lst.removeOne( rule );
655
656 if ( lst.empty() )
657 {
658 return true;
659 }
660 }
661 else
662 {
663 return rule->willRenderFeature( feature, context );
664 }
665 }
666 else if ( rule->willRenderFeature( feature, context ) )
667 {
668 return true;
669 }
670 }
671 return false;
672}
673
675{
676 QgsSymbolList lst;
677 if ( !isFilterOK( feature, context ) )
678 return lst;
679 if ( mSymbol )
680 lst.append( mSymbol.get() );
681
682 const auto constMActiveChildren = mActiveChildren;
683 for ( Rule *rule : constMActiveChildren )
684 {
685 lst += rule->symbolsForFeature( feature, context );
686 }
687 return lst;
688}
689
691{
692 QSet< QString> res;
693 if ( !isFilterOK( feature, context ) )
694 return res;
695
696 res.insert( mRuleKey );
697
698 // first determine if any non else rules match at this level
699 bool matchedNonElseRule = false;
700 for ( Rule *rule : std::as_const( mActiveChildren ) )
701 {
702 if ( rule->isElse() )
703 {
704 continue;
705 }
706 if ( rule->willRenderFeature( feature, context ) )
707 {
708 res.unite( rule->legendKeysForFeature( feature, context ) );
709 matchedNonElseRule = true;
710 }
711 }
712
713 // second chance -- allow else rules to take effect if valid
714 if ( !matchedNonElseRule )
715 {
716 for ( Rule *rule : std::as_const( mActiveChildren ) )
717 {
718 if ( rule->isElse() )
719 {
720 if ( rule->children().isEmpty() )
721 {
722 RuleList lst = rulesForFeature( feature, context, false );
723 lst.removeOne( rule );
724
725 if ( lst.empty() )
726 {
727 res.unite( rule->legendKeysForFeature( feature, context ) );
728 }
729 }
730 else
731 {
732 res.unite( rule->legendKeysForFeature( feature, context ) );
733 }
734 }
735 }
736 }
737 return res;
738}
739
741{
742 RuleList lst;
743 if ( ! isFilterOK( feature, context ) || ( context && ! isScaleOK( context->rendererScale() ) ) )
744 return lst;
745
746 if ( mSymbol )
747 lst.append( this );
748
749 RuleList listChildren = children();
750 if ( onlyActive )
751 listChildren = mActiveChildren;
752
753 const auto constListChildren = listChildren;
754 for ( Rule *rule : constListChildren )
755 {
756 lst += rule->rulesForFeature( feature, context, onlyActive );
757 }
758 return lst;
759}
760
762{
763 if ( mSymbol )
764 mSymbol->stopRender( context );
765
766 const auto constMActiveChildren = mActiveChildren;
767 for ( Rule *rule : constMActiveChildren )
768 {
769 rule->stopRender( context );
770 }
771
772 mActiveChildren.clear();
773 mSymbolNormZLevels.clear();
774}
775
776QgsRuleBasedRenderer::Rule *QgsRuleBasedRenderer::Rule::create( QDomElement &ruleElem, QgsSymbolMap &symbolMap, bool reuseId )
777{
778 QString symbolIdx = ruleElem.attribute( QStringLiteral( "symbol" ) );
779 QgsSymbol *symbol = nullptr;
780 if ( !symbolIdx.isEmpty() )
781 {
782 if ( symbolMap.contains( symbolIdx ) )
783 {
784 symbol = symbolMap.take( symbolIdx );
785 }
786 else
787 {
788 QgsDebugError( "symbol for rule " + symbolIdx + " not found!" );
789 }
790 }
791
792 QString filterExp = ruleElem.attribute( QStringLiteral( "filter" ) );
793 QString label = ruleElem.attribute( QStringLiteral( "label" ) );
794 QString description = ruleElem.attribute( QStringLiteral( "description" ) );
795 int scaleMinDenom = ruleElem.attribute( QStringLiteral( "scalemindenom" ), QStringLiteral( "0" ) ).toInt();
796 int scaleMaxDenom = ruleElem.attribute( QStringLiteral( "scalemaxdenom" ), QStringLiteral( "0" ) ).toInt();
797 QString ruleKey;
798 if ( reuseId )
799 ruleKey = ruleElem.attribute( QStringLiteral( "key" ) );
800 else
801 ruleKey = QUuid::createUuid().toString();
802 Rule *rule = new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
803
804 if ( !ruleKey.isEmpty() )
805 rule->mRuleKey = ruleKey;
806
807 rule->setActive( ruleElem.attribute( QStringLiteral( "checkstate" ), QStringLiteral( "1" ) ).toInt() );
808
809 QDomElement childRuleElem = ruleElem.firstChildElement( QStringLiteral( "rule" ) );
810 while ( !childRuleElem.isNull() )
811 {
812 Rule *childRule = create( childRuleElem, symbolMap );
813 if ( childRule )
814 {
815 rule->appendChild( childRule );
816 }
817 else
818 {
819 QgsDebugError( QStringLiteral( "failed to init a child rule!" ) );
820 }
821 childRuleElem = childRuleElem.nextSiblingElement( QStringLiteral( "rule" ) );
822 }
823
824 return rule;
825}
826
828{
829 RuleList l;
830 for ( QgsRuleBasedRenderer::Rule *c : mChildren )
831 {
832 l += c;
833 l += c->descendants();
834 }
835 return l;
836}
837
839{
840 if ( ruleElem.localName() != QLatin1String( "Rule" ) )
841 {
842 QgsDebugError( QStringLiteral( "invalid element: Rule element expected, %1 found!" ).arg( ruleElem.tagName() ) );
843 return nullptr;
844 }
845
846 QString label, description, filterExp;
847 int scaleMinDenom = 0, scaleMaxDenom = 0;
848 QgsSymbolLayerList layers;
849
850 // retrieve the Rule element child nodes
851 QDomElement childElem = ruleElem.firstChildElement();
852 while ( !childElem.isNull() )
853 {
854 if ( childElem.localName() == QLatin1String( "Name" ) )
855 {
856 // <se:Name> tag contains the rule identifier,
857 // so prefer title tag for the label property value
858 if ( label.isEmpty() )
859 label = childElem.firstChild().nodeValue();
860 }
861 else if ( childElem.localName() == QLatin1String( "Description" ) )
862 {
863 // <se:Description> can contains a title and an abstract
864 QDomElement titleElem = childElem.firstChildElement( QStringLiteral( "Title" ) );
865 if ( !titleElem.isNull() )
866 {
867 label = titleElem.firstChild().nodeValue();
868 }
869
870 QDomElement abstractElem = childElem.firstChildElement( QStringLiteral( "Abstract" ) );
871 if ( !abstractElem.isNull() )
872 {
873 description = abstractElem.firstChild().nodeValue();
874 }
875 }
876 else if ( childElem.localName() == QLatin1String( "Abstract" ) )
877 {
878 // <sld:Abstract> (v1.0)
879 description = childElem.firstChild().nodeValue();
880 }
881 else if ( childElem.localName() == QLatin1String( "Title" ) )
882 {
883 // <sld:Title> (v1.0)
884 label = childElem.firstChild().nodeValue();
885 }
886 else if ( childElem.localName() == QLatin1String( "Filter" ) )
887 {
889 if ( filter )
890 {
891 if ( filter->hasParserError() )
892 {
893 QgsDebugError( "parser error: " + filter->parserErrorString() );
894 }
895 else
896 {
897 filterExp = filter->expression();
898 }
899 delete filter;
900 }
901 }
902 else if ( childElem.localName() == QLatin1String( "ElseFilter" ) )
903 {
904 filterExp = QLatin1String( "ELSE" );
905
906 }
907 else if ( childElem.localName() == QLatin1String( "MinScaleDenominator" ) )
908 {
909 bool ok;
910 int v = childElem.firstChild().nodeValue().toInt( &ok );
911 if ( ok )
912 scaleMinDenom = v;
913 }
914 else if ( childElem.localName() == QLatin1String( "MaxScaleDenominator" ) )
915 {
916 bool ok;
917 int v = childElem.firstChild().nodeValue().toInt( &ok );
918 if ( ok )
919 scaleMaxDenom = v;
920 }
921 else if ( childElem.localName().endsWith( QLatin1String( "Symbolizer" ) ) )
922 {
923 // create symbol layers for this symbolizer
924 QgsSymbolLayerUtils::createSymbolLayerListFromSld( childElem, geomType, layers );
925 }
926
927 childElem = childElem.nextSiblingElement();
928 }
929
930 // now create the symbol
931 QgsSymbol *symbol = nullptr;
932 if ( !layers.isEmpty() )
933 {
934 switch ( geomType )
935 {
937 symbol = new QgsLineSymbol( layers );
938 break;
939
941 symbol = new QgsFillSymbol( layers );
942 break;
943
945 symbol = new QgsMarkerSymbol( layers );
946 break;
947
948 default:
949 QgsDebugError( QStringLiteral( "invalid geometry type: found %1" ).arg( qgsEnumValueToKey( geomType ) ) );
950 return nullptr;
951 }
952 }
953
954 // and then create and return the new rule
955 return new Rule( symbol, scaleMinDenom, scaleMaxDenom, filterExp, label, description );
956}
957
958
960
962 : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
963 , mRootRule( root )
964{
965}
966
968 : QgsFeatureRenderer( QStringLiteral( "RuleRenderer" ) )
969{
970 mRootRule = new Rule( nullptr ); // root has no symbol, no filter etc - just a container
971 mRootRule->appendChild( new Rule( defaultSymbol ) );
972}
973
978
979
981{
982 // not used at all
983 return nullptr;
984}
985
987{
989
990 std::function< void( Rule *rule ) > exploreRule;
991 exploreRule = [&res, &exploreRule]( Rule * rule )
992 {
993 if ( !rule )
994 return;
995
996 if ( QgsSymbol *symbol = rule->symbol() )
997 {
998 if ( symbol->flags().testFlag( Qgis::SymbolFlag::AffectsLabeling ) )
1000 }
1001
1002 for ( Rule *child : rule->children() )
1003 {
1004 exploreRule( child );
1005 }
1006 };
1007 exploreRule( mRootRule );
1008
1009 return res;
1010}
1011
1013 QgsRenderContext &context,
1014 int layer,
1015 bool selected,
1016 bool drawVertexMarker )
1017{
1018 Q_UNUSED( layer )
1019
1020 int flags = ( selected ? FeatIsSelected : 0 ) | ( drawVertexMarker ? FeatDrawMarkers : 0 );
1021 mCurrentFeatures.append( FeatureToRender( feature, flags ) );
1022
1023 // check each active rule
1025}
1026
1027
1029{
1030 QgsFeatureRenderer::startRender( context, fields );
1031
1032 // prepare active children
1033 mRootRule->startRender( context, fields, mFilter );
1034
1035 QSet<int> symbolZLevelsSet = mRootRule->collectZLevels();
1036 QList<int> symbolZLevels( symbolZLevelsSet.begin(), symbolZLevelsSet.end() );
1037 std::sort( symbolZLevels.begin(), symbolZLevels.end() );
1038
1039 // create mapping from unnormalized levels [unlimited range] to normalized levels [0..N-1]
1040 // and prepare rendering queue
1041 QMap<int, int> zLevelsToNormLevels;
1042 int maxNormLevel = -1;
1043 const auto constSymbolZLevels = symbolZLevels;
1044 for ( int zLevel : constSymbolZLevels )
1045 {
1046 zLevelsToNormLevels[zLevel] = ++maxNormLevel;
1047 mRenderQueue.append( RenderLevel( zLevel ) );
1048 QgsDebugMsgLevel( QStringLiteral( "zLevel %1 -> %2" ).arg( zLevel ).arg( maxNormLevel ), 4 );
1049 }
1050
1051 mRootRule->setNormZLevels( zLevelsToNormLevels );
1052}
1053
1058
1060{
1062
1063 //
1064 // do the actual rendering
1065 //
1066
1067 // go through all levels
1068 if ( !context.renderingStopped() )
1069 {
1070 const auto constMRenderQueue = mRenderQueue;
1071 for ( const RenderLevel &level : constMRenderQueue )
1072 {
1073 //QgsDebugMsgLevel(QString("level %1").arg(level.zIndex), 2);
1074 // go through all jobs at the level
1075 for ( const RenderJob *job : std::as_const( level.jobs ) )
1076 {
1077 context.expressionContext().setFeature( job->ftr.feat );
1078 //QgsDebugMsgLevel(QString("job fid %1").arg(job->f->id()), 2);
1079 // render feature - but only with symbol layers with specified zIndex
1080 QgsSymbol *s = job->symbol;
1081 int count = s->symbolLayerCount();
1082 for ( int i = 0; i < count; i++ )
1083 {
1084 // TODO: better solution for this
1085 // renderFeatureWithSymbol asks which symbol layer to draw
1086 // but there are multiple transforms going on!
1087 if ( s->symbolLayer( i )->renderingPass() == level.zIndex )
1088 {
1089 int flags = job->ftr.flags;
1090 renderFeatureWithSymbol( job->ftr.feat, job->symbol, context, i, flags & FeatIsSelected, flags & FeatDrawMarkers );
1091 }
1092 }
1093 }
1094 }
1095 }
1096
1097 // clean current features
1098 mCurrentFeatures.clear();
1099
1100 // clean render queue
1101 mRenderQueue.clear();
1102
1103 // clean up rules from temporary stuff
1104 mRootRule->stopRender( context );
1105}
1106
1108{
1109 return mFilter;
1110}
1111
1112QSet<QString> QgsRuleBasedRenderer::usedAttributes( const QgsRenderContext &context ) const
1113{
1114 return mRootRule->usedAttributes( context );
1115}
1116
1121
1123{
1125
1126 // normally with clone() the individual rules get new keys (UUID), but here we want to keep
1127 // the tree of rules intact, so that other components that may use the rule keys work nicely (e.g. map themes)
1128 clonedRoot->setRuleKey( mRootRule->ruleKey() );
1129 RuleList origDescendants = mRootRule->descendants();
1130 RuleList clonedDescendants = clonedRoot->descendants();
1131 Q_ASSERT( origDescendants.count() == clonedDescendants.count() );
1132 for ( int i = 0; i < origDescendants.count(); ++i )
1133 clonedDescendants[i]->setRuleKey( origDescendants[i]->ruleKey() );
1134
1135 QgsRuleBasedRenderer *r = new QgsRuleBasedRenderer( clonedRoot );
1136
1137 copyRendererData( r );
1138 return r;
1139}
1140
1141void QgsRuleBasedRenderer::toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props ) const
1142{
1143 mRootRule->toSld( doc, element, props );
1144}
1145
1146// TODO: ideally this function should be removed in favor of legendSymbol(ogy)Items
1148{
1149 return mRootRule->symbols( context );
1150}
1151
1152QDomElement QgsRuleBasedRenderer::save( QDomDocument &doc, const QgsReadWriteContext &context )
1153{
1154 QDomElement rendererElem = doc.createElement( RENDERER_TAG_NAME );
1155 rendererElem.setAttribute( QStringLiteral( "type" ), QStringLiteral( "RuleRenderer" ) );
1156
1158
1159 QDomElement rulesElem = mRootRule->save( doc, symbols );
1160 rulesElem.setTagName( QStringLiteral( "rules" ) ); // instead of just "rule"
1161 rendererElem.appendChild( rulesElem );
1162
1163 QDomElement symbolsElem = QgsSymbolLayerUtils::saveSymbols( symbols, QStringLiteral( "symbols" ), doc, context );
1164 rendererElem.appendChild( symbolsElem );
1165
1166 saveRendererData( doc, rendererElem, context );
1167
1168 return rendererElem;
1169}
1170
1172{
1173 return true;
1174}
1175
1177{
1178 Rule *rule = mRootRule->findRuleByKey( key );
1179 return rule ? rule->active() : true;
1180}
1181
1182void QgsRuleBasedRenderer::checkLegendSymbolItem( const QString &key, bool state )
1183{
1184 Rule *rule = mRootRule->findRuleByKey( key );
1185 if ( rule )
1186 rule->setActive( state );
1187}
1188
1189QString QgsRuleBasedRenderer::legendKeyToExpression( const QString &key, QgsVectorLayer *, bool &ok ) const
1190{
1191 ok = false;
1192 Rule *rule = mRootRule->findRuleByKey( key );
1193 if ( !rule )
1194 return QString();
1195
1196 std::function<QString( Rule *rule )> ruleToExpression;
1197 ruleToExpression = [&ruleToExpression]( Rule * rule ) -> QString
1198 {
1199 if ( rule->isElse() && rule->parent() )
1200 {
1201 // gather the expressions for all other rules on this level and invert them
1202
1203 QStringList otherRules;
1204 const QList<QgsRuleBasedRenderer::Rule *> siblings = rule->parent()->children();
1205 for ( Rule *sibling : siblings )
1206 {
1207 if ( sibling == rule || sibling->isElse() )
1208 continue;
1209
1210 const QString siblingExpression = ruleToExpression( sibling );
1211 if ( siblingExpression.isEmpty() )
1212 return QStringLiteral( "FALSE" ); // nothing will match this rule
1213
1214 otherRules.append( siblingExpression );
1215 }
1216
1217 if ( otherRules.empty() )
1218 return QStringLiteral( "TRUE" ); // all features will match the else rule
1219 else
1220 return (
1221 otherRules.size() > 1
1222 ? QStringLiteral( "NOT ((%1))" ).arg( otherRules.join( QLatin1String( ") OR (" ) ) )
1223 : QStringLiteral( "NOT (%1)" ).arg( otherRules.at( 0 ) )
1224 );
1225 }
1226 else
1227 {
1228 QStringList ruleParts;
1229 if ( !rule->filterExpression().isEmpty() )
1230 ruleParts.append( rule->filterExpression() );
1231
1232 if ( !qgsDoubleNear( rule->minimumScale(), 0.0 ) )
1233 ruleParts.append( QStringLiteral( "@map_scale <= %1" ).arg( rule->minimumScale() ) );
1234
1235 if ( !qgsDoubleNear( rule->maximumScale(), 0.0 ) )
1236 ruleParts.append( QStringLiteral( "@map_scale >= %1" ).arg( rule->maximumScale() ) );
1237
1238 if ( !ruleParts.empty() )
1239 {
1240 return (
1241 ruleParts.size() > 1
1242 ? QStringLiteral( "(%1)" ).arg( ruleParts.join( QLatin1String( ") AND (" ) ) )
1243 : ruleParts.at( 0 )
1244 );
1245 }
1246 else
1247 {
1248 return QString();
1249 }
1250 }
1251 };
1252
1253 QStringList parts;
1254 while ( rule )
1255 {
1256 const QString ruleFilter = ruleToExpression( rule );
1257 if ( !ruleFilter.isEmpty() )
1258 parts.append( ruleFilter );
1259
1260 rule = rule->parent();
1261 }
1262
1263 ok = true;
1264 return parts.empty() ? QStringLiteral( "TRUE" )
1265 : ( parts.size() > 1
1266 ? QStringLiteral( "(%1)" ).arg( parts.join( QLatin1String( ") AND (" ) ) )
1267 : parts.at( 0 ) );
1268}
1269
1270void QgsRuleBasedRenderer::setLegendSymbolItem( const QString &key, QgsSymbol *symbol )
1271{
1272 Rule *rule = mRootRule->findRuleByKey( key );
1273 if ( rule )
1274 rule->setSymbol( symbol );
1275 else
1276 delete symbol;
1277}
1278
1283
1284
1286{
1287 // load symbols
1288 QDomElement symbolsElem = element.firstChildElement( QStringLiteral( "symbols" ) );
1289 if ( symbolsElem.isNull() )
1290 return nullptr;
1291
1292 QgsSymbolMap symbolMap = QgsSymbolLayerUtils::loadSymbols( symbolsElem, context );
1293
1294 QDomElement rulesElem = element.firstChildElement( QStringLiteral( "rules" ) );
1295
1296 Rule *root = Rule::create( rulesElem, symbolMap );
1297 if ( !root )
1298 return nullptr;
1299
1301
1302 // delete symbols if there are any more
1304
1305 return r;
1306}
1307
1309{
1310 // retrieve child rules
1311 Rule *root = nullptr;
1312
1313 QDomElement ruleElem = element.firstChildElement( QStringLiteral( "Rule" ) );
1314 while ( !ruleElem.isNull() )
1315 {
1316 Rule *child = Rule::createFromSld( ruleElem, geomType );
1317 if ( child )
1318 {
1319 // create the root rule if not done before
1320 if ( !root )
1321 root = new Rule( nullptr );
1322
1323 root->appendChild( child );
1324 }
1325
1326 ruleElem = ruleElem.nextSiblingElement( QStringLiteral( "Rule" ) );
1327 }
1328
1329 if ( !root )
1330 {
1331 // no valid rules was found
1332 return nullptr;
1333 }
1334
1335 // create and return the new renderer
1336 return new QgsRuleBasedRenderer( root );
1337}
1338
1341
1343{
1344 QString attr = r->classAttribute();
1345 // categorizedAttr could be either an attribute name or an expression.
1346 // the only way to differentiate is to test it as an expression...
1347 QgsExpression testExpr( attr );
1348 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1349 {
1350 //not an expression, so need to quote column name
1351 attr = QgsExpression::quotedColumnRef( attr );
1352 }
1353
1354 const auto constCategories = r->categories();
1355 for ( const QgsRendererCategory &cat : constCategories )
1356 {
1357 QString value;
1358 // not quoting numbers saves a type cast
1359 if ( QgsVariantUtils::isNull( cat.value() ) )
1360 value = "NULL";
1361 else if ( cat.value().userType() == QMetaType::Type::Int )
1362 value = cat.value().toString();
1363 else if ( cat.value().userType() == QMetaType::Type::Double )
1364 // we loose precision here - so we may miss some categories :-(
1365 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1366 value = QString::number( cat.value().toDouble(), 'f', 4 );
1367 else
1368 value = QgsExpression::quotedString( cat.value().toString() );
1369 const QString filter = QStringLiteral( "%1 %2 %3" ).arg( attr, QgsVariantUtils::isNull( cat.value() ) ? QStringLiteral( "IS" ) : QStringLiteral( "=" ), value );
1370 const QString label = !cat.label().isEmpty() ? cat.label() :
1371 cat.value().isValid() ? value : QString();
1372 initialRule->appendChild( new Rule( cat.symbol()->clone(), 0, 0, filter, label ) );
1373 }
1374}
1375
1377{
1378 QString attr = r->classAttribute();
1379 // categorizedAttr could be either an attribute name or an expression.
1380 // the only way to differentiate is to test it as an expression...
1381 QgsExpression testExpr( attr );
1382 if ( testExpr.hasParserError() || ( testExpr.isField() && !attr.startsWith( '\"' ) ) )
1383 {
1384 //not an expression, so need to quote column name
1385 attr = QgsExpression::quotedColumnRef( attr );
1386 }
1387 else if ( !testExpr.isField() )
1388 {
1389 //otherwise wrap expression in brackets
1390 attr = QStringLiteral( "(%1)" ).arg( attr );
1391 }
1392
1393 bool firstRange = true;
1394 const auto constRanges = r->ranges();
1395 for ( const QgsRendererRange &rng : constRanges )
1396 {
1397 // due to the loss of precision in double->string conversion we may miss out values at the limit of the range
1398 // TODO: have a possibility to construct expressions directly as a parse tree to avoid loss of precision
1399 QString filter = QStringLiteral( "%1 %2 %3 AND %1 <= %4" ).arg( attr, firstRange ? QStringLiteral( ">=" ) : QStringLiteral( ">" ),
1400 QString::number( rng.lowerValue(), 'f', 4 ),
1401 QString::number( rng.upperValue(), 'f', 4 ) );
1402 firstRange = false;
1403 QString label = rng.label().isEmpty() ? filter : rng.label();
1404 initialRule->appendChild( new Rule( rng.symbol()->clone(), 0, 0, filter, label ) );
1405 }
1406}
1407
1409{
1410 std::sort( scales.begin(), scales.end() ); // make sure the scales are in ascending order
1411 double oldScale = initialRule->maximumScale();
1412 double maxDenom = initialRule->minimumScale();
1413 QgsSymbol *symbol = initialRule->symbol();
1414 const auto constScales = scales;
1415 for ( int scale : constScales )
1416 {
1417 if ( initialRule->maximumScale() >= scale )
1418 continue; // jump over the first scales out of the interval
1419 if ( maxDenom != 0 && maxDenom <= scale )
1420 break; // ignore the latter scales out of the interval
1421 initialRule->appendChild( new Rule( symbol->clone(), oldScale, scale, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( scale ) ) );
1422 oldScale = scale;
1423 }
1424 // last rule
1425 initialRule->appendChild( new Rule( symbol->clone(), oldScale, maxDenom, QString(), QStringLiteral( "%1 - %2" ).arg( oldScale ).arg( maxDenom ) ) );
1426}
1427
1429{
1430 QString msg( QStringLiteral( "Rule-based renderer:\n" ) );
1431 msg += mRootRule->dump();
1432 return msg;
1433}
1434
1436{
1437 return mRootRule->willRenderFeature( feature, &context );
1438}
1439
1441{
1442 return mRootRule->symbolsForFeature( feature, &context );
1443}
1444
1446{
1447 return mRootRule->symbolsForFeature( feature, &context );
1448}
1449
1450QSet< QString > QgsRuleBasedRenderer::legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const
1451{
1452 return mRootRule->legendKeysForFeature( feature, &context );
1453}
1454
1456{
1457 return mRootRule->accept( visitor );
1458}
1459
1461{
1462 std::unique_ptr< QgsRuleBasedRenderer > r;
1463 if ( renderer->type() == QLatin1String( "RuleRenderer" ) )
1464 {
1465 r.reset( dynamic_cast<QgsRuleBasedRenderer *>( renderer->clone() ) );
1466 }
1467 else if ( renderer->type() == QLatin1String( "singleSymbol" ) )
1468 {
1469 const QgsSingleSymbolRenderer *singleSymbolRenderer = dynamic_cast<const QgsSingleSymbolRenderer *>( renderer );
1470 if ( !singleSymbolRenderer )
1471 return nullptr;
1472
1473 std::unique_ptr< QgsSymbol > origSymbol( singleSymbolRenderer->symbol()->clone() );
1474 r = std::make_unique< QgsRuleBasedRenderer >( origSymbol.release() );
1475 }
1476 else if ( renderer->type() == QLatin1String( "categorizedSymbol" ) )
1477 {
1478 const QgsCategorizedSymbolRenderer *categorizedRenderer = dynamic_cast<const QgsCategorizedSymbolRenderer *>( renderer );
1479 if ( !categorizedRenderer )
1480 return nullptr;
1481
1482 QString attr = categorizedRenderer->classAttribute();
1483 // categorizedAttr could be either an attribute name or an expression.
1484 bool isField = false;
1485 if ( layer )
1486 {
1487 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1488 }
1489 else
1490 {
1491 QgsExpression testExpr( attr );
1492 isField = testExpr.hasParserError() || testExpr.isField();
1493 }
1494 if ( isField && !attr.contains( '\"' ) )
1495 {
1496 //not an expression, so need to quote column name
1497 attr = QgsExpression::quotedColumnRef( attr );
1498 }
1499
1500 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1501
1502 QString expression;
1503 QString value;
1504 QgsRendererCategory category;
1505 for ( const QgsRendererCategory &category : categorizedRenderer->categories() )
1506 {
1507 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1508
1509 rule->setLabel( category.label() );
1510
1511 //We first define the rule corresponding to the category
1512 if ( category.value().userType() == QMetaType::Type::QVariantList )
1513 {
1514 QStringList values;
1515 const QVariantList list = category.value().toList();
1516 for ( const QVariant &v : list )
1517 {
1518 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1519 if ( QVariant( v ).convert( QMetaType::Type::Double ) )
1520 {
1521 values << v.toString();
1522 }
1523 else
1524 {
1525 values << QgsExpression::quotedString( v.toString() );
1526 }
1527 }
1528
1529 if ( values.empty() )
1530 {
1531 expression = QStringLiteral( "ELSE" );
1532 }
1533 else
1534 {
1535 expression = QStringLiteral( "%1 IN (%2)" ).arg( attr, values.join( ',' ) );
1536 }
1537 }
1538 else
1539 {
1540 //If the value is a number, we can use it directly, otherwise we need to quote it in the rule
1541 if ( category.value().convert( QMetaType::Type::Double ) )
1542 {
1543 value = category.value().toString();
1544 }
1545 else
1546 {
1547 value = QgsExpression::quotedString( category.value().toString() );
1548 }
1549
1550 //An empty category is equivalent to the ELSE keyword
1551 if ( value == QLatin1String( "''" ) )
1552 {
1553 expression = QStringLiteral( "ELSE" );
1554 }
1555 else
1556 {
1557 expression = QStringLiteral( "%1 = %2" ).arg( attr, value );
1558 }
1559 }
1560 rule->setFilterExpression( expression );
1561
1562 //Then we construct an equivalent symbol.
1563 //Ideally we could simply copy the symbol, but the categorized renderer allows a separate interface to specify
1564 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1565
1566 std::unique_ptr< QgsSymbol > origSymbol( category.symbol()->clone() );
1567 rule->setSymbol( origSymbol.release() );
1568
1569 rootrule->appendChild( rule.release() );
1570 }
1571
1572 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1573 }
1574 else if ( renderer->type() == QLatin1String( "graduatedSymbol" ) )
1575 {
1576 const QgsGraduatedSymbolRenderer *graduatedRenderer = dynamic_cast<const QgsGraduatedSymbolRenderer *>( renderer );
1577 if ( !graduatedRenderer )
1578 return nullptr;
1579
1580 QString attr = graduatedRenderer->classAttribute();
1581 // categorizedAttr could be either an attribute name or an expression.
1582 // the only way to differentiate is to test it as an expression...
1583 bool isField = false;
1584 if ( layer )
1585 {
1586 isField = QgsExpression::expressionToLayerFieldIndex( attr, layer ) != -1;
1587 }
1588 else
1589 {
1590 QgsExpression testExpr( attr );
1591 isField = testExpr.hasParserError() || testExpr.isField();
1592 }
1593 if ( isField && !attr.contains( '\"' ) )
1594 {
1595 //not an expression, so need to quote column name
1596 attr = QgsExpression::quotedColumnRef( attr );
1597 }
1598 else if ( !isField )
1599 {
1600 //otherwise wrap expression in brackets
1601 attr = QStringLiteral( "(%1)" ).arg( attr );
1602 }
1603
1604 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1605
1606 QString expression;
1607 QgsRendererRange range;
1608 for ( int i = 0; i < graduatedRenderer->ranges().size(); ++i )
1609 {
1610 range = graduatedRenderer->ranges().value( i );
1611 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1612 rule->setLabel( range.label() );
1613 if ( i == 0 )//The lower boundary of the first range is included, while it is excluded for the others
1614 {
1615 expression = attr + " >= " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1616 attr + " <= " + QString::number( range.upperValue(), 'f' );
1617 }
1618 else
1619 {
1620 expression = attr + " > " + QString::number( range.lowerValue(), 'f' ) + " AND " + \
1621 attr + " <= " + QString::number( range.upperValue(), 'f' );
1622 }
1623 rule->setFilterExpression( expression );
1624
1625 //Then we construct an equivalent symbol.
1626 //Ideally we could simply copy the symbol, but the graduated renderer allows a separate interface to specify
1627 //data dependent area and rotation, so we need to convert these to obtain the same rendering
1628
1629 std::unique_ptr< QgsSymbol > symbol( range.symbol()->clone() );
1630 rule->setSymbol( symbol.release() );
1631
1632 rootrule->appendChild( rule.release() );
1633 }
1634
1635 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1636 }
1637 else if ( renderer->type() == QLatin1String( "pointDisplacement" ) || renderer->type() == QLatin1String( "pointCluster" ) )
1638 {
1639 if ( const QgsPointDistanceRenderer *pointDistanceRenderer = dynamic_cast<const QgsPointDistanceRenderer *>( renderer ) )
1640 return convertFromRenderer( pointDistanceRenderer->embeddedRenderer() );
1641 }
1642 else if ( renderer->type() == QLatin1String( "invertedPolygonRenderer" ) )
1643 {
1644 if ( const QgsInvertedPolygonRenderer *invertedPolygonRenderer = dynamic_cast<const QgsInvertedPolygonRenderer *>( renderer ) )
1645 r.reset( convertFromRenderer( invertedPolygonRenderer->embeddedRenderer() ) );
1646 }
1647 else if ( renderer->type() == QLatin1String( "mergedFeatureRenderer" ) )
1648 {
1649 if ( const QgsMergedFeatureRenderer *mergedRenderer = dynamic_cast<const QgsMergedFeatureRenderer *>( renderer ) )
1650 r.reset( convertFromRenderer( mergedRenderer->embeddedRenderer() ) );
1651 }
1652 else if ( renderer->type() == QLatin1String( "embeddedSymbol" ) && layer )
1653 {
1654 const QgsEmbeddedSymbolRenderer *embeddedRenderer = dynamic_cast<const QgsEmbeddedSymbolRenderer *>( renderer );
1655
1656 std::unique_ptr< QgsRuleBasedRenderer::Rule > rootrule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1657
1660 req.setNoAttributes();
1661 QgsFeatureIterator it = layer->getFeatures( req );
1662 QgsFeature feature;
1663 while ( it.nextFeature( feature ) && rootrule->children().size() < 500 )
1664 {
1665 if ( feature.embeddedSymbol() )
1666 {
1667 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1668 rule->setFilterExpression( QStringLiteral( "$id=%1" ).arg( feature.id() ) );
1669 rule->setLabel( QString::number( feature.id() ) );
1670 rule->setSymbol( feature.embeddedSymbol()->clone() );
1671 rootrule->appendChild( rule.release() );
1672 }
1673 }
1674
1675 std::unique_ptr< QgsRuleBasedRenderer::Rule > rule = std::make_unique< QgsRuleBasedRenderer::Rule >( nullptr );
1676 rule->setFilterExpression( QStringLiteral( "ELSE" ) );
1677 rule->setLabel( QObject::tr( "All other features" ) );
1678 rule->setSymbol( embeddedRenderer->defaultSymbol()->clone() );
1679 rootrule->appendChild( rule.release() );
1680
1681 r = std::make_unique< QgsRuleBasedRenderer >( rootrule.release() );
1682 }
1683
1684 if ( r )
1685 {
1686 renderer->copyRendererData( r.get() );
1687 }
1688
1689 return r.release();
1690}
1691
1692void QgsRuleBasedRenderer::convertToDataDefinedSymbology( QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField )
1693{
1694 QString sizeExpression;
1695 switch ( symbol->type() )
1696 {
1698 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1699 {
1700 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( symbol->symbolLayer( j ) );
1701 if ( ! sizeScaleField.isEmpty() )
1702 {
1703 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1705 }
1706 if ( ! rotationField.isEmpty() )
1707 {
1709 }
1710 }
1711 break;
1713 if ( ! sizeScaleField.isEmpty() )
1714 {
1715 for ( int j = 0; j < symbol->symbolLayerCount(); ++j )
1716 {
1717 if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "SimpleLine" ) )
1718 {
1719 QgsLineSymbolLayer *lsl = static_cast<QgsLineSymbolLayer *>( symbol->symbolLayer( j ) );
1720 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( lsl->width() ).arg( sizeScaleField );
1722 }
1723 if ( symbol->symbolLayer( j )->layerType() == QLatin1String( "MarkerLine" ) )
1724 {
1725 QgsSymbol *marker = symbol->symbolLayer( j )->subSymbol();
1726 for ( int k = 0; k < marker->symbolLayerCount(); ++k )
1727 {
1728 QgsMarkerSymbolLayer *msl = static_cast<QgsMarkerSymbolLayer *>( marker->symbolLayer( k ) );
1729 sizeExpression = QStringLiteral( "%1*(%2)" ).arg( msl->size() ).arg( sizeScaleField );
1731 }
1732 }
1733 }
1734 }
1735 break;
1736 default:
1737 break;
1738 }
1739}
@ 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 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.
@ 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:6215
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition qgis.h:6024
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.