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