1 /***************************************************************************
2     qgsrulebasedrenderer.h - 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 
16 #ifndef QGSRULEBASEDRENDERER_H
17 #define QGSRULEBASEDRENDERER_H
18 
19 #include "qgis_core.h"
20 #include "qgis_sip.h"
21 #include "qgsfields.h"
22 #include "qgsfeature.h"
23 #include "qgis.h"
24 
25 #include "qgsrenderer.h"
26 #include "qgsrendercontext.h"
27 
28 class QgsExpression;
29 
30 class QgsCategorizedSymbolRenderer;
31 class QgsGraduatedSymbolRenderer;
32 
33 /**
34  * \ingroup core
35  * \brief Rule based renderer.
36  *
37  * When drawing a vector layer with rule-based renderer, it goes through
38  * the rules and draws features with symbols from rules that match.
39  */
40 class CORE_EXPORT QgsRuleBasedRenderer : public QgsFeatureRenderer
41 {
42   public:
43     // TODO: use QVarLengthArray instead of QList
44 
45     enum FeatureFlags
46     {
47       FeatIsSelected = 1,
48       FeatDrawMarkers = 2
49     };
50 
51     /**
52      * Feature for rendering by a QgsRuleBasedRenderer. Contains a QgsFeature and some flags.
53      * \ingroup core
54      */
55     struct FeatureToRender
56     {
FeatureToRenderFeatureToRender57       FeatureToRender( const QgsFeature &_f, int _flags )
58         : feat( _f )
59         , flags( _flags )
60       {}
61       QgsFeature feat;
62       int flags; // selected and/or draw markers
63     };
64 
65     /**
66      * A QgsRuleBasedRenderer rendering job, consisting of a feature to be rendered with a particular symbol.
67      * \ingroup core
68      */
69     struct RenderJob
70     {
RenderJobRenderJob71         RenderJob( QgsRuleBasedRenderer::FeatureToRender &_ftr, QgsSymbol *_s )
72           : ftr( _ftr )
73           , symbol( _s )
74         {}
75 
76         //! Feature to render
77         QgsRuleBasedRenderer::FeatureToRender &ftr;
78 
79         //! Symbol to render feature with (not owned by this object).
80         QgsSymbol *symbol = nullptr;
81 
82       private:
83 #ifdef SIP_RUN
84         RenderJob &operator=( const RenderJob & );
85 #endif
86     };
87 
88     /**
89      * Render level: a list of jobs to be drawn at particular level for a QgsRuleBasedRenderer.
90      * \ingroup core
91      */
92     struct RenderLevel
93     {
RenderLevelRenderLevel94       explicit RenderLevel( int z )
95         : zIndex( z )
96       {}
97 
~RenderLevelRenderLevel98       ~RenderLevel() { qDeleteAll( jobs ); }
99       int zIndex;
100 
101       //! List of jobs to render, owned by this object.
102       QList<QgsRuleBasedRenderer::RenderJob *> jobs;
103 
104       QgsRuleBasedRenderer::RenderLevel &operator=( const QgsRuleBasedRenderer::RenderLevel &rh )
105       {
106         zIndex = rh.zIndex;
107         qDeleteAll( jobs );
108         jobs.clear();
109         for ( auto it = rh.jobs.constBegin(); it != rh.jobs.constEnd(); ++it )
110         {
111           jobs << new RenderJob( *( *it ) );
112         }
113         return *this;
114       }
115 
RenderLevelRenderLevel116       RenderLevel( const QgsRuleBasedRenderer::RenderLevel &other )
117         : zIndex( other.zIndex ), jobs()
118       {
119         for ( auto it = other.jobs.constBegin(); it != other.jobs.constEnd(); ++it )
120         {
121           jobs << new RenderJob( * ( *it ) );
122         }
123       }
124 
125     };
126 
127     //! Rendering queue: a list of rendering levels
128     typedef QList<QgsRuleBasedRenderer::RenderLevel> RenderQueue;
129 
130     class Rule;
131     typedef QList<QgsRuleBasedRenderer::Rule *> RuleList;
132 
133     /**
134      * \ingroup core
135      * \brief  This class keeps data about a rules for rule-based renderer.
136      *
137      * A rule consists of a symbol, filter expression and range of scales.
138      * If filter is empty, it matches all features.
139      * If scale range has both values zero, it matches all scales.
140      * If one of the min/max scale denominators is zero, there is no lower/upper bound for scales.
141      * A rule matches if both filter and scale range match.
142      */
143     class CORE_EXPORT Rule
144     {
145       public:
146         //! The result of rendering a rule
147         enum RenderResult
148         {
149           Filtered = 0, //!< The rule does not apply
150           Inactive,     //!< The rule is inactive
151           Rendered      //!< Something was rendered
152         };
153 
154         //! Constructor takes ownership of the symbol
155         Rule( QgsSymbol *symbol SIP_TRANSFER, int maximumScale = 0, int minimumScale = 0, const QString &filterExp = QString(),
156               const QString &label = QString(), const QString &description = QString(), bool elseRule = false );
157         ~Rule();
158 
159         //! Rules cannot be copied
160         Rule( const Rule &rh ) = delete;
161         //! Rules cannot be copied
162         Rule &operator=( const Rule &rh ) = delete;
163 
164         /**
165          * Dump for debug purpose
166          * \param indent How many characters to indent. Will increase by two with every of the recursive calls
167          * \returns A string representing this rule
168          */
169         QString dump( int indent = 0 ) const;
170 
171         /**
172          * Returns the attributes used to evaluate the expression of this rule
173          * \returns A set of attribute names
174          */
175         QSet<QString> usedAttributes( const QgsRenderContext &context ) const;
176 
177         /**
178          * Returns TRUE if this rule or one of its children needs the geometry to be applied.
179          */
180         bool needsGeometry() const;
181 
182         //! \note available in Python bindings as symbol2
183         QgsSymbolList symbols( const QgsRenderContext &context = QgsRenderContext() ) const;
184 
185         //! \since QGIS 2.6
186         QgsLegendSymbolList legendSymbolItems( int currentLevel = -1 ) const;
187 
188         /**
189          * Check if a given feature shall be rendered by this rule
190          *
191          * \param f         The feature to test
192          * \param context   The context in which the rendering happens
193          * \returns          TRUE if the feature shall be rendered
194          */
195         bool isFilterOK( const QgsFeature &f, QgsRenderContext *context = nullptr ) const;
196 
197         /**
198          * Check if this rule applies for a given \a scale.
199          * The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
200          * If set to 0, it will always return TRUE.
201          *
202          * \returns If the rule will be evaluated at this scale
203          */
204         bool isScaleOK( double scale ) const;
205 
symbol()206         QgsSymbol *symbol() { return mSymbol.get(); }
label()207         QString label() const { return mLabel; }
dependsOnScale()208         bool dependsOnScale() const { return mMaximumScale != 0 || mMinimumScale != 0; }
209 
210         /**
211          * Returns the maximum map scale (i.e. most "zoomed in" scale) at which the rule will be active.
212          * The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
213          * A scale of 0 indicates no maximum scale visibility.
214          * \see minimumScale()
215          * \see setMaximumScale()
216          * \since QGIS 3.0
217         */
maximumScale()218         double maximumScale() const { return mMaximumScale; }
219 
220         /**
221          * Returns the minimum map scale (i.e. most "zoomed out" scale) at which the rule will be active.
222          * The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
223          * A scale of 0 indicates no minimum scale visibility.
224          * \see maximumScale()
225          * \see setMinimumScale()
226          * \since QGIS 3.0
227         */
minimumScale()228         double minimumScale() const { return mMinimumScale; }
229 
230         /**
231          * A filter that will check if this rule applies
232          * \returns An expression
233          */
filter()234         QgsExpression *filter() const { return mFilter.get(); }
235 
236         /**
237          * A filter that will check if this rule applies
238          * \returns An expression
239          */
filterExpression()240         QString filterExpression() const { return mFilterExp; }
241 
242         /**
243          * A human readable description for this rule
244          *
245          * \returns Description
246          */
description()247         QString description() const { return mDescription; }
248 
249         /**
250          * Returns if this rule is active
251          *
252          * \returns TRUE if the rule is active
253          */
active()254         bool active() const { return mIsActive; }
255 
256         /**
257          * Unique rule identifier (for identification of rule within renderer)
258          * \since QGIS 2.6
259          */
ruleKey()260         QString ruleKey() const { return mRuleKey; }
261 
262         /**
263          * Override the assigned rule key (should be used just internally by rule-based renderer)
264          * \since QGIS 2.6
265          */
setRuleKey(const QString & key)266         void setRuleKey( const QString &key ) { mRuleKey = key; }
267 
268         //! Sets a new symbol (or NULLPTR). Deletes old symbol.
269         void setSymbol( QgsSymbol *sym SIP_TRANSFER );
setLabel(const QString & label)270         void setLabel( const QString &label ) { mLabel = label; }
271 
272         /**
273          * Sets the minimum map \a scale (i.e. most "zoomed out" scale) at which the rule will be active.
274          * The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
275          * A \a scale of 0 indicates no minimum scale visibility.
276          * \see minimumScale()
277          * \see setMaximumScale()
278          */
setMinimumScale(double scale)279         void setMinimumScale( double scale ) { mMinimumScale = scale; }
280 
281         /**
282          * Sets the maximum map \a scale (i.e. most "zoomed in" scale) at which the rule will be active.
283          * The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
284          * A \a scale of 0 indicates no maximum scale visibility.
285          * \see maximumScale()
286          * \see setMinimumScale()
287          */
setMaximumScale(double scale)288         void setMaximumScale( double scale ) { mMaximumScale = scale; }
289 
290         /**
291          * Set the expression used to check if a given feature shall be rendered with this rule
292          *
293          * \param filterExp An expression
294          */
295         void setFilterExpression( const QString &filterExp );
296 
297         /**
298          * Set a human readable description for this rule
299          *
300          * \param description Description
301          */
setDescription(const QString & description)302         void setDescription( const QString &description ) { mDescription = description; }
303 
304         /**
305          * Sets if this rule is active
306          * \param state Determines if the rule should be activated or deactivated
307          */
setActive(bool state)308         void setActive( bool state ) { mIsActive = state; }
309 
310         //! clone this rule, return new instance
311         QgsRuleBasedRenderer::Rule *clone() const SIP_FACTORY;
312 
313         //! Saves the symbol layer as SLD
314         void toSld( QDomDocument &doc, QDomElement &element, QVariantMap props ) const;
315 
316         /**
317          * Create a rule from the SLD provided in element and for the specified geometry type.
318          */
319         static QgsRuleBasedRenderer::Rule *createFromSld( QDomElement &element, QgsWkbTypes::GeometryType geomType ) SIP_FACTORY;
320 
321         QDomElement save( QDomDocument &doc, QgsSymbolMap &symbolMap ) const;
322 
323         //! prepare the rule for rendering and its children (build active children array)
324         bool startRender( QgsRenderContext &context, const QgsFields &fields, QString &filter );
325 
326         //! Gets all used z-levels from this rule and children
327         QSet<int> collectZLevels();
328 
329         /**
330          * assign normalized z-levels [0..N-1] for this rule's symbol for quick access during rendering
331          * \note not available in Python bindings
332          */
333         void setNormZLevels( const QMap<int, int> &zLevelsToNormLevels ) SIP_SKIP;
334 
335         /**
336          * Render a given feature, will recursively call subclasses and only render if the constraints apply.
337          *
338          * \param featToRender The feature to render
339          * \param context      The rendering context
340          * \param renderQueue  The rendering queue to which the feature should be added
341          * \returns             The result of the rendering. In explicit if the feature is added to the queue or
342          *                     the reason for not rendering the feature.
343          */
344         QgsRuleBasedRenderer::Rule::RenderResult renderFeature( QgsRuleBasedRenderer::FeatureToRender &featToRender, QgsRenderContext &context, QgsRuleBasedRenderer::RenderQueue &renderQueue );
345 
346         //! only tell whether a feature will be rendered without actually rendering it
347         bool willRenderFeature( const QgsFeature &feature, QgsRenderContext *context = nullptr );
348 
349         //! tell which symbols will be used to render the feature
350         QgsSymbolList symbolsForFeature( const QgsFeature &feature, QgsRenderContext *context = nullptr );
351 
352         /**
353          * Returns which legend keys match the feature
354          * \since QGIS 2.14
355          */
356         QSet< QString > legendKeysForFeature( const QgsFeature &feature, QgsRenderContext *context = nullptr );
357 
358         /**
359          * Returns the list of rules used to render the feature in a specific
360          * context.
361          *
362          * \param feature The feature for which rules have to be find
363          * \param context The rendering context
364          * \param onlyActive TRUE to search for active rules only, FALSE otherwise
365          */
366         QgsRuleBasedRenderer::RuleList rulesForFeature( const QgsFeature &feature, QgsRenderContext *context = nullptr, bool onlyActive = true );
367 
368         /**
369          * Stop a rendering process. Used to clean up the internal state of this rule
370          *
371          * \param context The rendering context
372          */
373         void stopRender( QgsRenderContext &context );
374 
375         /**
376          * Create a rule from an XML definition
377          *
378          * \param ruleElem  The XML rule element
379          * \param symbolMap Symbol map
380          *
381          * \returns A new rule
382          */
383         static QgsRuleBasedRenderer::Rule *create( QDomElement &ruleElem, QgsSymbolMap &symbolMap ) SIP_FACTORY;
384 
385         /**
386          * Returns all children rules of this rule
387          *
388          * \returns A list of rules
389          */
children()390         const QgsRuleBasedRenderer::RuleList &children() { return mChildren; }
391 
392         /**
393          * Returns all children, grand-children, grand-grand-children, grand-gra... you get it
394          *
395          * \returns A list of descendant rules
396          */
397         QgsRuleBasedRenderer::RuleList descendants() const;
398 
399         /**
400          * The parent rule
401          *
402          * \returns Parent rule
403          */
parent()404         QgsRuleBasedRenderer::Rule *parent() { return mParent; }
405 
406         //! add child rule, take ownership, sets this as parent
407         void appendChild( QgsRuleBasedRenderer::Rule *rule SIP_TRANSFER );
408 
409         //! add child rule, take ownership, sets this as parent
410         void insertChild( int i, QgsRuleBasedRenderer::Rule *rule SIP_TRANSFER );
411 
412         //! delete child rule
413         void removeChild( QgsRuleBasedRenderer::Rule *rule );
414 
415         //! delete child rule
416         void removeChildAt( int i );
417 
418         //! take child rule out, set parent as NULLPTR
419         QgsRuleBasedRenderer::Rule *takeChild( QgsRuleBasedRenderer::Rule *rule ) SIP_TRANSFERBACK;
420 
421         //! take child rule out, set parent as NULLPTR
422         QgsRuleBasedRenderer::Rule *takeChildAt( int i ) SIP_TRANSFERBACK;
423 
424         /**
425          * Try to find a rule given its unique key
426          * \since QGIS 2.6
427          */
428         QgsRuleBasedRenderer::Rule *findRuleByKey( const QString &key );
429 
430         /**
431          * Sets if this rule is an ELSE rule
432          *
433          * \param iselse If TRUE, this rule is an ELSE rule
434          */
435         void setIsElse( bool iselse );
436 
437         /**
438          * Check if this rule is an ELSE rule
439          *
440          * \returns TRUE if this rule is an else rule
441          */
isElse()442         bool isElse() const { return mElseRule; }
443 
444         /**
445          * Accepts the specified symbology \a visitor, causing it to visit all child rules associated
446          * with the rule.
447          *
448          * Returns TRUE if the visitor should continue visiting other objects, or FALSE if visiting
449          * should be canceled.
450          *
451          * \since QGIS 3.10
452          */
453         bool accept( QgsStyleEntityVisitorInterface *visitor ) const;
454 
455       protected:
456         void initFilter();
457 
458       private:
459 #ifdef SIP_RUN
460         Rule( const QgsRuleBasedRenderer::Rule &rh );
461 #endif
462 
463         Rule *mParent = nullptr; // parent rule (nullptr only for root rule)
464         std::unique_ptr< QgsSymbol > mSymbol;
465         double mMaximumScale = 0;
466         double mMinimumScale = 0;
467         QString mFilterExp, mLabel, mDescription;
468         bool mElseRule = false;
469         RuleList mChildren;
470         RuleList mElseRules;
471         bool mIsActive = true; // whether it is enabled or not
472 
473         QString mRuleKey; // string used for unique identification of rule within renderer
474 
475         // temporary
476         std::unique_ptr< QgsExpression > mFilter;
477         // temporary while rendering
478         QSet<int> mSymbolNormZLevels;
479         RuleList mActiveChildren;
480 
481         /**
482          * Check which child rules are else rules and update the internal list of else rules
483          *
484          */
485         void updateElseRules();
486     };
487 
488     /////
489 
490     //! Creates a new rule-based renderer instance from XML
491     static QgsFeatureRenderer *create( QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY;
492 
493     //! Constructs the renderer from given tree of rules (takes ownership)
494     QgsRuleBasedRenderer( QgsRuleBasedRenderer::Rule *root SIP_TRANSFER );
495     //! Constructor for convenience. Creates a root rule and adds a default rule with symbol (takes ownership)
496     QgsRuleBasedRenderer( QgsSymbol *defaultSymbol SIP_TRANSFER );
497 
498     ~QgsRuleBasedRenderer() override;
499 
500     //! Returns symbol for current feature. Should not be used individually: there could be more symbols for a feature
501     QgsSymbol *symbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const override;
502 
503     bool renderFeature( const QgsFeature &feature, QgsRenderContext &context, int layer = -1, bool selected = false, bool drawVertexMarker = false ) override SIP_THROW( QgsCsException );
504 
505     void startRender( QgsRenderContext &context, const QgsFields &fields ) override;
506 
507     void stopRender( QgsRenderContext &context ) override;
508 
509     QString filter( const QgsFields &fields = QgsFields() ) override;
510 
511     QSet<QString> usedAttributes( const QgsRenderContext &context ) const override;
512 
513     bool filterNeedsGeometry() const override;
514 
515     QgsRuleBasedRenderer *clone() const override SIP_FACTORY;
516 
517     void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props = QVariantMap() ) const override;
518 
519     static QgsFeatureRenderer *createFromSld( QDomElement &element, QgsWkbTypes::GeometryType geomType ) SIP_FACTORY;
520 
521     QgsSymbolList symbols( QgsRenderContext &context ) const override;
522 
523     QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) override;
524     bool legendSymbolItemsCheckable() const override;
525     bool legendSymbolItemChecked( const QString &key ) override;
526     void checkLegendSymbolItem( const QString &key, bool state = true ) override;
527 
528     void setLegendSymbolItem( const QString &key, QgsSymbol *symbol SIP_TRANSFER ) override;
529     QgsLegendSymbolList legendSymbolItems() const override;
530     QString dump() const override;
531     bool willRenderFeature( const QgsFeature &feature, QgsRenderContext &context ) const override;
532     QgsSymbolList symbolsForFeature( const QgsFeature &feature, QgsRenderContext &context ) const override;
533     QgsSymbolList originalSymbolsForFeature( const QgsFeature &feature, QgsRenderContext &context ) const override;
534     QSet<QString> legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const override;
capabilities()535     QgsFeatureRenderer::Capabilities capabilities() override { return MoreSymbolsPerFeature | Filter | ScaleDependent; }
536     bool accept( QgsStyleEntityVisitorInterface *visitor ) const override;
537 
538     /////
539 
rootRule()540     QgsRuleBasedRenderer::Rule *rootRule() { return mRootRule; }
541 
542     //////
543 
544     //! take a rule and create a list of new rules based on the categories from categorized symbol renderer
545     static void refineRuleCategories( QgsRuleBasedRenderer::Rule *initialRule, QgsCategorizedSymbolRenderer *r );
546     //! take a rule and create a list of new rules based on the ranges from graduated symbol renderer
547     static void refineRuleRanges( QgsRuleBasedRenderer::Rule *initialRule, QgsGraduatedSymbolRenderer *r );
548     //! take a rule and create a list of new rules with intervals of scales given by the passed scale denominators
549     static void refineRuleScales( QgsRuleBasedRenderer::Rule *initialRule, QList<int> scales );
550 
551     /**
552      * Creates a new QgsRuleBasedRenderer from an existing \a renderer.
553      *
554      * Since QGIS 3.20, the optional \a layer parameter is required for conversions of some renderer types.
555      *
556      * \returns a new renderer if the conversion was possible, otherwise NULLPTR.
557      * \since QGIS 2.5
558      */
559     static QgsRuleBasedRenderer *convertFromRenderer( const QgsFeatureRenderer *renderer, QgsVectorLayer *layer = nullptr ) SIP_FACTORY;
560 
561     //! helper function to convert the size scale and rotation fields present in some other renderers to data defined symbology
562     static void convertToDataDefinedSymbology( QgsSymbol *symbol, const QString &sizeScaleField, const QString &rotationField = QString() );
563 
564   protected:
565     //! the root node with hierarchical list of rules
566     Rule *mRootRule = nullptr;
567 
568     // temporary
569     RenderQueue mRenderQueue;
570     QList<FeatureToRender> mCurrentFeatures;
571 
572     QString mFilter;
573 
574   private:
575 #ifdef SIP_RUN
576     QgsRuleBasedRenderer( const QgsRuleBasedRenderer & );
577     QgsRuleBasedRenderer &operator=( const QgsRuleBasedRenderer & );
578 #endif
579 };
580 
581 #endif // QGSRULEBASEDRENDERER_H
582