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