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