Branch data Line data Source code
1 : : /***************************************************************************
2 : : qgsrulebasedlabeling.h
3 : : ---------------------
4 : : begin : September 2015
5 : : copyright : (C) 2015 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 : : #ifndef QGSRULEBASEDLABELING_H
16 : : #define QGSRULEBASEDLABELING_H
17 : :
18 : : #include "qgis_core.h"
19 : : #include <QStringList>
20 : : #include <QMap>
21 : : #include <QUuid>
22 : :
23 : : #include "qgsvectorlayerlabeling.h"
24 : : #include "qgsvectorlayerlabelprovider.h"
25 : :
26 : : class QDomDocument;
27 : : class QDomElement;
28 : :
29 : : class QgsExpression;
30 : : class QgsFeature;
31 : : class QgsPalLayerSettings;
32 : : class QgsRenderContext;
33 : : class QgsGeometry;
34 : : class QgsRuleBasedLabelProvider;
35 : :
36 : : /**
37 : : * \ingroup core
38 : : * \class QgsRuleBasedLabeling
39 : : * \brief Rule based labeling for a vector layer.
40 : : * \since QGIS 3.0
41 : : */
42 : : class CORE_EXPORT QgsRuleBasedLabeling : public QgsAbstractVectorLayerLabeling
43 : : {
44 : : public:
45 : : class Rule;
46 : : typedef QList<QgsRuleBasedLabeling::Rule *> RuleList;
47 : : typedef QMap<QgsRuleBasedLabeling::Rule *, QgsVectorLayerLabelProvider *> RuleToProviderMap;
48 : :
49 : : /**
50 : : * \ingroup core
51 : : * \class QgsRuleBasedLabeling::Rule
52 : : * \brief A child rule for QgsRuleBasedLabeling.
53 : : * \since QGIS 3.0
54 : : */
55 : : class CORE_EXPORT Rule
56 : : {
57 : : public:
58 : : //! takes ownership of settings, settings may be NULLPTR
59 : : Rule( QgsPalLayerSettings *settings SIP_TRANSFER, double maximumScale = 0, double minimumScale = 0, const QString &filterExp = QString(), const QString &description = QString(), bool elseRule = false );
60 : : ~Rule();
61 : :
62 : : //! Rules cannot be copied.
63 : : Rule( const Rule &rh ) = delete;
64 : : //! Rules cannot be copied.
65 : : Rule &operator=( const Rule &rh ) = delete;
66 : :
67 : : //! The result of registering a rule
68 : : enum RegisterResult
69 : : {
70 : : Filtered = 0, //!< The rule does not apply
71 : : Inactive, //!< The rule is inactive
72 : : Registered //!< Something was registered
73 : : };
74 : :
75 : : /**
76 : : * Returns the labeling settings. May return NULLPTR.
77 : : */
78 : 0 : QgsPalLayerSettings *settings() const { return mSettings.get(); }
79 : :
80 : : /**
81 : : * Determines if scale based labeling is active
82 : : *
83 : : * \returns TRUE if scale based labeling is active
84 : : */
85 : : bool dependsOnScale() const { return !qgsDoubleNear( mMinimumScale, 0.0 ) || !qgsDoubleNear( mMaximumScale, 0 ); }
86 : :
87 : : /**
88 : : * Returns the maximum map scale (i.e. most "zoomed in" scale) at which the label rule will be active.
89 : : * The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
90 : : * A scale of 0 indicates no maximum scale visibility.
91 : : * \see minimumScale()
92 : : * \see setMaximumScale()
93 : : * \since QGIS 3.0
94 : : */
95 : 0 : double maximumScale() const { return mMaximumScale; }
96 : :
97 : : /**
98 : : * Returns the minimum map scale (i.e. most "zoomed out" scale) at which the label rule will be active.
99 : : * The scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
100 : : * A scale of 0 indicates no minimum scale visibility.
101 : : * \see maximumScale()
102 : : * \see setMinimumScale()
103 : : * \since QGIS 3.0
104 : : */
105 : 0 : double minimumScale() const { return mMinimumScale; }
106 : :
107 : : /**
108 : : * A filter that will check if this rule applies
109 : : * \returns An expression
110 : : */
111 : 0 : QString filterExpression() const { return mFilterExp; }
112 : :
113 : : /**
114 : : * A human readable description for this rule
115 : : *
116 : : * \returns Description
117 : : */
118 : : QString description() const { return mDescription; }
119 : :
120 : : /**
121 : : * Returns if this rule is active
122 : : *
123 : : * \returns TRUE if the rule is active
124 : : */
125 : : bool active() const { return mIsActive; }
126 : :
127 : : /**
128 : : * Check if this rule is an ELSE rule
129 : : *
130 : : * \returns TRUE if this rule is an else rule
131 : : */
132 : 0 : bool isElse() const { return mElseRule; }
133 : :
134 : : //! Unique rule identifier (for identification of rule within labeling, used as provider ID)
135 : 0 : QString ruleKey() const { return mRuleKey; }
136 : :
137 : : //! Sets new settings (or NULLPTR). Deletes old settings if any.
138 : : void setSettings( QgsPalLayerSettings *settings SIP_TRANSFER );
139 : :
140 : : /**
141 : : * Sets the minimum map \a scale (i.e. most "zoomed out" scale) at which the label rule will be active.
142 : : * The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
143 : : * A \a scale of 0 indicates no minimum scale visibility.
144 : : * \see minimumScale()
145 : : * \see setMaximumScale()
146 : : */
147 : : void setMinimumScale( double scale ) { mMinimumScale = scale; }
148 : :
149 : : /**
150 : : * Sets the maximum map \a scale (i.e. most "zoomed in" scale) at which the rule will be active.
151 : : * The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
152 : : * A \a scale of 0 indicates no maximum scale visibility.
153 : : * \see maximumScale()
154 : : * \see setMinimumScale()
155 : : */
156 : : void setMaximumScale( double scale ) { mMaximumScale = scale; }
157 : :
158 : : /**
159 : : * Set the expression used to check if a given feature shall be rendered with this rule
160 : : *
161 : : * \param filterExp An expression
162 : : */
163 : : void setFilterExpression( const QString &filterExp ) { mFilterExp = filterExp; initFilter(); }
164 : :
165 : : /**
166 : : * Set a human readable description for this rule
167 : : *
168 : : * \param description Description
169 : : */
170 : : void setDescription( const QString &description ) { mDescription = description; }
171 : :
172 : : /**
173 : : * Sets if this rule is active
174 : : * \param state Determines if the rule should be activated or deactivated
175 : : */
176 : 0 : void setActive( bool state ) { mIsActive = state; }
177 : :
178 : : /**
179 : : * Sets if this rule is an ELSE rule
180 : : *
181 : : * \param iselse If TRUE, this rule is an ELSE rule
182 : : */
183 : : void setIsElse( bool iselse ) { mElseRule = iselse; }
184 : :
185 : : //! Override the assigned rule key (should be used just internally by rule-based labeling)
186 : 0 : void setRuleKey( const QString &key ) { mRuleKey = key; }
187 : :
188 : : // parent / child operations
189 : :
190 : : /**
191 : : * Returns all children rules of this rule
192 : : *
193 : : * \returns A list of rules
194 : : */
195 : : const QgsRuleBasedLabeling::RuleList &children() const { return mChildren; }
196 : :
197 : : /**
198 : : * Returns all children rules of this rule
199 : : *
200 : : * \returns A list of rules
201 : : */
202 : 0 : QgsRuleBasedLabeling::RuleList &children() SIP_SKIP { return mChildren; }
203 : :
204 : : /**
205 : : * Returns all children, grand-children, grand-grand-children, grand-gra... you get it
206 : : *
207 : : * \returns A list of descendant rules
208 : : */
209 : : QgsRuleBasedLabeling::RuleList descendants() const;
210 : :
211 : : /**
212 : : * The parent rule
213 : : *
214 : : * \returns Parent rule
215 : : */
216 : : const QgsRuleBasedLabeling::Rule *parent() const SIP_SKIP { return mParent; }
217 : :
218 : : /**
219 : : * The parent rule
220 : : *
221 : : * \returns Parent rule
222 : : */
223 : : QgsRuleBasedLabeling::Rule *parent() { return mParent; }
224 : :
225 : : //! add child rule, take ownership, sets this as parent
226 : : void appendChild( QgsRuleBasedLabeling::Rule *rule SIP_TRANSFER );
227 : :
228 : : //! add child rule, take ownership, sets this as parent
229 : : void insertChild( int i, QgsRuleBasedLabeling::Rule *rule SIP_TRANSFER );
230 : :
231 : : //! delete child rule
232 : : void removeChildAt( int i );
233 : :
234 : : //! Try to find a rule given its unique key
235 : : const QgsRuleBasedLabeling::Rule *findRuleByKey( const QString &key ) const;
236 : :
237 : : /**
238 : : * Find a labeling rule thanks to its key.
239 : : *
240 : : * \param key The key of the rule to find
241 : : *
242 : : * \returns The rule or NULLPTR if not found
243 : : *
244 : : * \since QGIS 3.0
245 : : */
246 : : QgsRuleBasedLabeling::Rule *findRuleByKey( const QString &key ) SIP_SKIP;
247 : :
248 : : //! clone this rule, return new instance
249 : : QgsRuleBasedLabeling::Rule *clone() const SIP_FACTORY;
250 : :
251 : : // load / save
252 : :
253 : : /**
254 : : * Create a rule from an XML definition
255 : : * \param ruleElem The XML rule element
256 : : * \param context reading context
257 : : * \returns A new rule
258 : : */
259 : : static QgsRuleBasedLabeling::Rule *create( const QDomElement &ruleElem, const QgsReadWriteContext &context ) SIP_FACTORY;
260 : :
261 : : //! store labeling info to XML element
262 : : QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const;
263 : :
264 : : // evaluation
265 : :
266 : : /**
267 : : * add providers
268 : : * \note not available in Python bindings
269 : : */
270 : : void createSubProviders( QgsVectorLayer *layer, RuleToProviderMap &subProviders, QgsRuleBasedLabelProvider *provider ) SIP_SKIP;
271 : :
272 : : /**
273 : : * append rule keys of descendants that contain valid settings (i.e. they will be sub-providers)
274 : : * \note not available in Python bindings
275 : : */
276 : : void subProviderIds( QStringList &list ) const SIP_SKIP;
277 : :
278 : : /**
279 : : * call prepare() on sub-providers and populate attributeNames
280 : : * \note not available in Python bindings
281 : : */
282 : : void prepare( QgsRenderContext &context, QSet<QString> &attributeNames, RuleToProviderMap &subProviders ) SIP_SKIP;
283 : :
284 : : /**
285 : : * register individual features
286 : : * \note not available in Python bindings
287 : : */
288 : : RegisterResult registerFeature( const QgsFeature &feature, QgsRenderContext &context, RuleToProviderMap &subProviders, const QgsGeometry &obstacleGeometry = QgsGeometry(), const QgsSymbol *symbol = nullptr ) SIP_SKIP;
289 : :
290 : : /**
291 : : * Returns TRUE if this rule or any of its children requires advanced composition effects
292 : : * to render.
293 : : */
294 : : bool requiresAdvancedEffects() const;
295 : :
296 : : /**
297 : : * Accepts the specified symbology \a visitor, causing it to visit all child rules associated
298 : : * with the rule.
299 : : *
300 : : * Returns TRUE if the visitor should continue visiting other objects, or FALSE if visiting
301 : : * should be canceled.
302 : : *
303 : : * \since QGIS 3.10
304 : : */
305 : : bool accept( QgsStyleEntityVisitorInterface *visitor ) const;
306 : :
307 : : private:
308 : : #ifdef SIP_RUN
309 : : Rule( const QgsRuleBasedLabeling::Rule &rh );
310 : : #endif
311 : :
312 : : /**
313 : : * Check if a given feature shall be labelled by this rule
314 : : *
315 : : * \param f The feature to test
316 : : * \param context The context in which the rendering happens
317 : : * \returns TRUE if the feature shall be rendered
318 : : */
319 : : bool isFilterOK( const QgsFeature &f, QgsRenderContext &context ) const;
320 : :
321 : : /**
322 : : * Check if this rule applies for a given \a scale.
323 : : * The \a scale value indicates the scale denominator, e.g. 1000.0 for a 1:1000 map.
324 : : * If set to 0, it will always return TRUE.
325 : : *
326 : : * \returns If the rule will be evaluated at this scale
327 : : */
328 : : bool isScaleOK( double scale ) const;
329 : :
330 : : /**
331 : : * Initialize filters. Automatically called by setFilterExpression.
332 : : */
333 : : void initFilter();
334 : :
335 : : /**
336 : : * Check which child rules are else rules and update the internal list of else rules
337 : : */
338 : : void updateElseRules();
339 : :
340 : : private:
341 : : Rule *mParent = nullptr; // parent rule (nullptr only for root rule)
342 : : std::unique_ptr<QgsPalLayerSettings> mSettings;
343 : : double mMaximumScale = 0;
344 : : double mMinimumScale = 0;
345 : : QString mFilterExp;
346 : : QString mDescription;
347 : : bool mElseRule = false;
348 : : RuleList mChildren;
349 : : RuleList mElseRules;
350 : : bool mIsActive = true; // whether it is enabled or not
351 : :
352 : : QString mRuleKey = QUuid::createUuid().toString(); // string used for unique identification of rule within labeling
353 : :
354 : : std::unique_ptr<QgsExpression> mFilter;
355 : :
356 : : };
357 : :
358 : :
359 : : //! Constructs the labeling from given tree of rules (takes ownership)
360 : : explicit QgsRuleBasedLabeling( QgsRuleBasedLabeling::Rule *root SIP_TRANSFER );
361 : : ~QgsRuleBasedLabeling() override;
362 : :
363 : : QgsRuleBasedLabeling::Rule *rootRule();
364 : : const Rule *rootRule() const SIP_SKIP;
365 : :
366 : : //! Create the instance from a DOM element with saved configuration
367 : : static QgsRuleBasedLabeling *create( const QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY;
368 : :
369 : : // implementation of parent interface
370 : :
371 : : QString type() const override;
372 : : QgsRuleBasedLabeling *clone() const override SIP_FACTORY;
373 : : QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const override;
374 : : //! \note not available in Python bindings
375 : : QgsVectorLayerLabelProvider *provider( QgsVectorLayer *layer ) const override SIP_SKIP;
376 : : QStringList subProviders() const override;
377 : : QgsPalLayerSettings settings( const QString &providerId = QString() ) const override;
378 : : bool accept( QgsStyleEntityVisitorInterface *visitor ) const override;
379 : :
380 : : /**
381 : : * Set pal settings for a specific provider (takes ownership).
382 : : *
383 : : * \param settings Pal layer settings
384 : : * \param providerId The id of the provider
385 : : *
386 : : * \since QGIS 3.0
387 : : */
388 : : void setSettings( QgsPalLayerSettings *settings SIP_TRANSFER, const QString &providerId = QString() ) override;
389 : : bool requiresAdvancedEffects() const override;
390 : : void toSld( QDomNode &parent, const QVariantMap &props ) const override;
391 : :
392 : : protected:
393 : : std::unique_ptr<Rule> mRootRule;
394 : : };
395 : :
396 : : #ifndef SIP_RUN
397 : :
398 : : /**
399 : : * \ingroup core
400 : : * \class QgsRuleBasedLabelProvider
401 : : * \brief Label provider for rule based labeling.
402 : : * \note not available in Python bindings
403 : : * \note this class is not a part of public API yet. See notes in QgsLabelingEngine
404 : : */
405 : 0 : class CORE_EXPORT QgsRuleBasedLabelProvider : public QgsVectorLayerLabelProvider
406 : : {
407 : : public:
408 : : QgsRuleBasedLabelProvider( const QgsRuleBasedLabeling &rules, QgsVectorLayer *layer, bool withFeatureLoop = true );
409 : :
410 : : // reimplemented
411 : :
412 : : bool prepare( QgsRenderContext &context, QSet<QString> &attributeNames ) override;
413 : :
414 : : void registerFeature( const QgsFeature &feature, QgsRenderContext &context, const QgsGeometry &obstacleGeometry = QgsGeometry(), const QgsSymbol *symbol = nullptr ) override;
415 : :
416 : : //! create a label provider
417 : : virtual QgsVectorLayerLabelProvider *createProvider( QgsVectorLayer *layer, const QString &providerId, bool withFeatureLoop, const QgsPalLayerSettings *settings );
418 : :
419 : : //! Returns subproviders
420 : : QList<QgsAbstractLabelProvider *> subProviders() override;
421 : :
422 : : protected:
423 : : //! owned copy
424 : : std::unique_ptr<QgsRuleBasedLabeling> mRules;
425 : : //! label providers are owned by labeling engine
426 : : QgsRuleBasedLabeling::RuleToProviderMap mSubProviders;
427 : : };
428 : :
429 : : #endif
430 : :
431 : : #endif // QGSRULEBASEDLABELING_H
|