1 /***************************************************************************
2     qgsgraduatedsymbolrenderer.h
3     ---------------------
4     begin                : November 2009
5     copyright            : (C) 2009 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 QGSGRADUATEDSYMBOLRENDERER_H
16 #define QGSGRADUATEDSYMBOLRENDERER_H
17 
18 #include "qgis_core.h"
19 #include "qgis_sip.h"
20 #include "qgis.h"
21 #include "qgsrenderer.h"
22 #include "qgsrendererrange.h"
23 #include "qgsclassificationmethod.h"
24 
25 class QgsVectorLayer;
26 class QgsColorRamp;
27 class QgsDataDefinedSizeLegend;
28 class QgsSymbol;
29 class QgsExpression;
30 
31 /**
32  * \ingroup core
33  * \class QgsGraduatedSymbolRenderer
34  */
35 class CORE_EXPORT QgsGraduatedSymbolRenderer : public QgsFeatureRenderer
36 {
37   public:
38 
39     QgsGraduatedSymbolRenderer( const QString &attrName = QString(), const QgsRangeList &ranges = QgsRangeList() );
40 
41     ~QgsGraduatedSymbolRenderer() override;
42 
43     QgsSymbol *symbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const override;
44     QgsSymbol *originalSymbolForFeature( const QgsFeature &feature, QgsRenderContext &context ) const override;
45     void startRender( QgsRenderContext &context, const QgsFields &fields ) override;
46     void stopRender( QgsRenderContext &context ) override;
47     QSet<QString> usedAttributes( const QgsRenderContext &context ) const override;
48     bool filterNeedsGeometry() const override;
49     QString dump() const override;
50     QgsGraduatedSymbolRenderer *clone() const override SIP_FACTORY;
51     void toSld( QDomDocument &doc, QDomElement &element, const QVariantMap &props = QVariantMap() ) const override;
capabilities()52     QgsFeatureRenderer::Capabilities capabilities() override { return SymbolLevels | Filter; }
53     QgsSymbolList symbols( QgsRenderContext &context ) const override;
54     bool accept( QgsStyleEntityVisitorInterface *visitor ) const override;
55 
classAttribute()56     QString classAttribute() const { return mAttrName; }
setClassAttribute(const QString & attr)57     void setClassAttribute( const QString &attr ) { mAttrName = attr; }
58 
ranges()59     const QgsRangeList &ranges() const { return mRanges; }
60 
61     bool updateRangeSymbol( int rangeIndex, QgsSymbol *symbol SIP_TRANSFER );
62     bool updateRangeLabel( int rangeIndex, const QString &label );
63     bool updateRangeUpperValue( int rangeIndex, double value );
64     bool updateRangeLowerValue( int rangeIndex, double value );
65     //! \since QGIS 2.5
66     bool updateRangeRenderState( int rangeIndex, bool render );
67 
68     void addClass( QgsSymbol *symbol );
69     //! \note available in Python bindings as addClassRange
70     void addClass( const QgsRendererRange &range ) SIP_PYNAME( addClassRange );
71     //! \note available in Python bindings as addClassLowerUpper
72     void addClass( double lower, double upper ) SIP_PYNAME( addClassLowerUpper );
73 
74     /**
75      * Add a breakpoint by splitting existing classes so that the specified
76      * value becomes a break between two classes.
77      * \param breakValue position to insert break
78      * \param updateSymbols set to TRUE to reapply ramp colors to the new
79      * symbol ranges
80      * \since QGIS 2.9
81      */
82     void addBreak( double breakValue, bool updateSymbols = true );
83 
84     void deleteClass( int idx );
85     void deleteAllClasses();
86 
87     //! Moves the category at index position from to index position to.
88     void moveClass( int from, int to );
89 
90     /**
91      * Tests whether classes assigned to the renderer have ranges which overlap.
92      * \returns TRUE if ranges overlap
93      * \since QGIS 2.10
94      */
95     bool rangesOverlap() const;
96 
97     /**
98      * Tests whether classes assigned to the renderer have gaps between the ranges.
99      * \returns TRUE if ranges have gaps
100      * \since QGIS 2.10
101      */
102     bool rangesHaveGaps() const;
103 
104     void sortByValue( Qt::SortOrder order = Qt::AscendingOrder );
105     void sortByLabel( Qt::SortOrder order = Qt::AscendingOrder );
106 
107     /**
108      * Returns the classification method
109      * \since QGIS 3.10
110      */
111     QgsClassificationMethod *classificationMethod() const;
112 
113     /**
114      * Defines the classification method
115      * This will take ownership of the method
116      * \since QGIS 3.10
117      */
118     void setClassificationMethod( QgsClassificationMethod *method SIP_TRANSFER );
119 
120     /**
121       * Classification mode
122       * \deprecated since QGIS 3.10 use QgsClassificationMethod::MethodId instead
123       */
124     enum Mode
125     {
126       EqualInterval,
127       Quantile,
128       Jenks,
129       StdDev,
130       Pretty,
131       Custom
132     };
133     // TODO QGIS 4: remove
134     // this could not be tagged with Q_DECL_DEPRECATED due to Doxygen warning
135     // might be fixed in newer Doxygen (does not on 1.8.13, might be ok on 1.8.16)
136 
137 
138     //! \deprecated since QGIS 3.10 use classficationMethod instead
mode()139     Q_DECL_DEPRECATED Mode mode() const SIP_DEPRECATED { return modeFromMethodId( mClassificationMethod->id() ); }
140     //! \deprecated since QGIS 3.10 use classficationMethod instead
141     Q_DECL_DEPRECATED void setMode( Mode mode ) SIP_DEPRECATED;
142 
143     /**
144      * Returns if we want to classify symmetric around a given value
145      * \since QGIS 3.4
146      * \deprecated since QGIS 3.10 use classficationMethod instead
147      */
useSymmetricMode()148     Q_DECL_DEPRECATED bool useSymmetricMode() const SIP_DEPRECATED { return mClassificationMethod->symmetricModeEnabled(); }
149 
150     /**
151      * Set if we want to classify symmetric around a given value
152      * \since QGIS 3.4
153      * \deprecated since QGIS 3.10 use classficationMethod instead
154      */
155     Q_DECL_DEPRECATED  void setUseSymmetricMode( bool useSymmetricMode ) SIP_DEPRECATED;
156 
157     /**
158      * Returns the pivot value for symmetric classification
159      * \since QGIS 3.4
160      * \deprecated since QGIS 3.10 use classficationMethod instead
161      */
symmetryPoint()162     Q_DECL_DEPRECATED double symmetryPoint() const SIP_DEPRECATED { return mClassificationMethod->symmetryPoint(); }
163 
164     /**
165      * Set the pivot point
166      * \since QGIS 3.4
167      * \deprecated since QGIS 3.10 use classficationMethod instead
168      */
169     Q_DECL_DEPRECATED void setSymmetryPoint( double symmetryPoint ) SIP_DEPRECATED;
170 
171 
172     /**
173      * Returns if we want to have a central class astride the pivot value
174      * \since QGIS 3.4
175      * \deprecated since QGIS 3.10 use classficationMethod instead
176      */
astride()177     Q_DECL_DEPRECATED bool astride() const SIP_DEPRECATED { return mClassificationMethod->symmetryAstride(); }
178 
179     /**
180      * Set if we want a central class astride the pivot value
181      * \since QGIS 3.4
182      * \deprecated since QGIS 3.10 use classficationMethod instead
183      */
184     Q_DECL_DEPRECATED void setAstride( bool astride ) SIP_DEPRECATED;
185 
186     /**
187      * Remove the breaks that are above the existing opposite sign classes to keep colors symmetrically balanced around symmetryPoint
188      * Does not put a break on the symmetryPoint. This is done before.
189      * \param breaks The breaks of an already-done classification
190      * \param symmetryPoint The point around which we want a symmetry
191      * \param astride A bool indicating if the symmetry is made astride the symmetryPoint or not ( [-1,1] vs. [-1,0][0,1] )
192      * \since QGIS 3.4
193      * \deprecated since QGIS 3.10, use QgsClassificationMethod::makeBreaksSymmetric instead
194      */
195     Q_DECL_DEPRECATED static void makeBreaksSymmetric( QList<double> &breaks SIP_INOUT, double symmetryPoint, bool astride ) SIP_DEPRECATED;
196 
197     /**
198      * Compute the equal interval classification
199      * \param minimum The minimum value of the distribution
200      * \param maximum The maximum value of the distribution
201      * \param classes The number of classes desired
202      * \param useSymmetricMode A bool indicating if we want to have classes and hence colors ramp symmetric around a value
203      * \param symmetryPoint The point around which we want a symmetry
204      * \param astride A bool indicating if the symmetry is made astride the symmetryPoint or not ( [-1,1] vs. [-1,0][0,1] )
205      * \deprecated since QGIS 3.10 use QgsClassificationEqualInterval class instead
206      */
207     Q_DECL_DEPRECATED static QList<double> calcEqualIntervalBreaks( double minimum, double maximum, int classes, bool useSymmetricMode, double symmetryPoint, bool astride ) SIP_DEPRECATED;
208 
209     /**
210      * Recalculate classes for a layer
211      * \param vlayer  The layer being rendered (from which data values are calculated)
212      * \param mode    The calculation mode
213      * \param nclasses The number of classes to calculate (approximate for some modes)
214      * \param useSymmetricMode A bool indicating if we want to have classes and hence colors ramp symmetric around a value
215      * \param symmetryPoint The value around which the classes will be symmetric if useSymmetricMode is checked
216      * \param astride A bool indicating if the symmetry is made astride the symmetryPoint or not ( [-1,1] vs. [-1,0][0,1] )
217      * \since QGIS 2.6 (three first arguments) and 3.4 (three last arguments)
218      * \deprecated since QGIS 3.10
219      */
220     Q_DECL_DEPRECATED void updateClasses( QgsVectorLayer *vlayer, Mode mode, int nclasses, bool useSymmetricMode = false, double symmetryPoint = 0.0, bool astride = false ) SIP_DEPRECATED;
221 
222     /**
223      * Recalculate classes for a layer
224      * \param vl  The layer being rendered (from which data values are calculated)
225      * \param nclasses the number of classes
226      */
227     void updateClasses( const QgsVectorLayer *vl, int nclasses );
228 
229     Q_NOWARN_DEPRECATED_PUSH;
230 
231     /**
232      * Returns the label format used to generate default classification labels
233      * \since QGIS 2.6
234      * \deprecated since QGIS 3.10 use classificationMethod() and QgsClassificationMethod::setLabelFormat instead
235      */
236     Q_DECL_DEPRECATED QgsRendererRangeLabelFormat labelFormat() const SIP_DEPRECATED;
237 
238     /**
239      * Set the label format used to generate default classification labels
240      * \param labelFormat The string appended to classification labels
241      * \param updateRanges If TRUE then ranges ending with the old unit string are updated to the new.
242      * \since QGIS 2.6
243      * \deprecated since QGIS 3.10 use classificationMethod() and QgsClassificationMethod::setLabelFormat instead
244      */
245     Q_DECL_DEPRECATED void setLabelFormat( const QgsRendererRangeLabelFormat &labelFormat, bool updateRanges = false ) SIP_DEPRECATED;
246 
247     Q_NOWARN_DEPRECATED_POP;
248 
249     /**
250      * Reset the label decimal places to a numberbased on the minimum class interval
251      * \param updateRanges if TRUE then ranges currently using the default label will be updated
252      * \since QGIS 2.6
253      */
254     void calculateLabelPrecision( bool updateRanges = true );
255 
256     Q_NOWARN_DEPRECATED_PUSH;
257 
258     /**
259      * Creates a new graduated renderer.
260      * \param vlayer vector layer
261      * \param attrName attribute to classify
262      * \param classes number of classes
263      * \param mode classification mode
264      * \param symbol base symbol
265      * \param ramp color ramp for classes
266      * \param legendFormat
267      * \param useSymmetricMode A bool indicating if we want to have classes and hence colors ramp symmetric around a value
268      * \param symmetryPoint The value around which the classes will be symmetric if useSymmetricMode is checked
269      * \param listForCboPrettyBreaks The list of potential pivot values for symmetric mode with prettybreaks mode
270      * \param astride A bool indicating if the symmetry is made astride the symmetryPoint or not ( [-1,1] vs. [-1,0][0,1] )
271      * \returns new QgsGraduatedSymbolRenderer object
272      * \deprecated since QGIS 3.10
273      */
274     Q_DECL_DEPRECATED static QgsGraduatedSymbolRenderer *createRenderer( QgsVectorLayer *vlayer,
275         const QString &attrName,
276         int classes,
277         Mode mode,
278         QgsSymbol *symbol SIP_TRANSFER,
279         QgsColorRamp *ramp SIP_TRANSFER,
280         const QgsRendererRangeLabelFormat &legendFormat = QgsRendererRangeLabelFormat(),
281         bool useSymmetricMode = false,
282         double symmetryPoint = 0.0,
283         const QStringList &listForCboPrettyBreaks = QStringList(),
284         bool astride = false ) SIP_DEPRECATED;
285     Q_NOWARN_DEPRECATED_POP;
286 
287     //! create renderer from XML element
288     static QgsFeatureRenderer *create( QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY;
289 
290     QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) override;
291     QgsLegendSymbolList legendSymbolItems() const override;
292     QSet< QString > legendKeysForFeature( const QgsFeature &feature, QgsRenderContext &context ) const override;
293 
294     /**
295      * Returns the renderer's source symbol, which is the base symbol used for the each classes' symbol before applying
296      * the classes' color.
297      * \see setSourceSymbol()
298      * \see sourceColorRamp()
299      */
300     QgsSymbol *sourceSymbol();
301 
302     /**
303      * Returns the renderer's source symbol, which is the base symbol used for the each classes' symbol before applying
304      * the classes' color.
305      * \see setSourceSymbol()
306      * \see sourceColorRamp()
307      * \note Not available in Python bindings.
308      */
309     const QgsSymbol *sourceSymbol() const SIP_SKIP;
310 
311     /**
312      * Sets the source symbol for the renderer, which is the base symbol used for the each classes' symbol before applying
313      * the classes' color.
314      * \param sym source symbol, ownership is transferred to the renderer
315      * \see sourceSymbol()
316      * \see setSourceColorRamp()
317      */
318     void setSourceSymbol( QgsSymbol *sym SIP_TRANSFER );
319 
320     /**
321      * Returns the source color ramp, from which each classes' color is derived.
322      * \see setSourceColorRamp()
323      * \see sourceSymbol()
324      */
325     QgsColorRamp *sourceColorRamp();
326 
327     /**
328      * Returns the source color ramp, from which each classes' color is derived.
329      * \see setSourceColorRamp()
330      * \see sourceSymbol()
331      * \note Not available in Python bindings.
332      */
333     const QgsColorRamp *sourceColorRamp() const SIP_SKIP;
334 
335     /**
336      * Sets the source color ramp.
337      * \param ramp color ramp. Ownership is transferred to the renderer
338      */
339     void setSourceColorRamp( QgsColorRamp *ramp SIP_TRANSFER );
340 
341     /**
342      * Update the color ramp used. Also updates all symbols colors.
343      * Doesn't alter current breaks.
344      * \param ramp color ramp. Ownership is transferred to the renderer
345      */
346     void updateColorRamp( QgsColorRamp *ramp SIP_TRANSFER = nullptr );
347 
348     /**
349      * Update all the symbols but leave breaks and colors. This method also sets the source
350      * symbol for the renderer.
351      * \param sym source symbol to use for classes. Ownership is not transferred.
352      * \see setSourceSymbol()
353      */
354     void updateSymbols( QgsSymbol *sym );
355 
356     /**
357      * set varying symbol size for classes
358      * \note the classes must already be set so that symbols exist
359      * \since QGIS 2.10
360      */
361     void setSymbolSizes( double minSize, double maxSize );
362 
363     /**
364      * Returns the min symbol size when graduated by size
365      * \since QGIS 2.10
366      */
367     double minSymbolSize() const;
368 
369     /**
370      * Returns the max symbol size when graduated by size
371      * \since QGIS 2.10
372      */
373     double maxSymbolSize() const;
374 
375     enum GraduatedMethod
376     {
377       GraduatedColor = 0,
378       GraduatedSize = 1
379     };
380 
381     /**
382      * Returns the method used for graduation (either size or color)
383      * \since QGIS 2.10
384      */
graduatedMethod()385     GraduatedMethod graduatedMethod() const { return mGraduatedMethod; }
386 
387     /**
388      * set the method used for graduation (either size or color)
389      * \since QGIS 2.10
390      */
setGraduatedMethod(GraduatedMethod method)391     void setGraduatedMethod( GraduatedMethod method ) { mGraduatedMethod = method; }
392 
393     bool legendSymbolItemsCheckable() const override;
394     bool legendSymbolItemChecked( const QString &key ) override;
395     void checkLegendSymbolItem( const QString &key, bool state = true ) override;
396     void setLegendSymbolItem( const QString &key, QgsSymbol *symbol SIP_TRANSFER ) override;
legendClassificationAttribute()397     QString legendClassificationAttribute() const override { return classAttribute(); }
398 
399     /**
400      * creates a QgsGraduatedSymbolRenderer from an existing renderer.
401      * \returns a new renderer if the conversion was possible, otherwise NULLPTR.
402      * \since QGIS 2.6
403      */
404     static QgsGraduatedSymbolRenderer *convertFromRenderer( const QgsFeatureRenderer *renderer ) SIP_FACTORY;
405 
406     /**
407      * Configures appearance of legend when renderer is configured to use data-defined size for marker symbols.
408      * This allows configuring for which values (symbol sizes) should be shown in the legend, whether to display
409      * different symbol sizes collapsed in one legend node or separated across multiple legend nodes etc.
410      *
411      * When renderer does not use data-defined size or does not use marker symbols, these settings will be ignored.
412      * Takes ownership of the passed settings objects. NULLPTR is a valid input that disables data-defined
413      * size legend.
414      * \since QGIS 3.0
415      */
416     void setDataDefinedSizeLegend( QgsDataDefinedSizeLegend *settings SIP_TRANSFER );
417 
418     /**
419      * Returns configuration of appearance of legend when using data-defined size for marker symbols.
420      * Will return NULLPTR if the functionality is disabled.
421      * \since QGIS 3.0
422      */
423     QgsDataDefinedSizeLegend *dataDefinedSizeLegend() const;
424 
425     /**
426      * Updates the labels of the ranges
427      * \since QGIS 3.10
428      */
429     void updateRangeLabels();
430 
431     /**
432      * Returns the renderer range matching the provided \a value, or NULLPTR if no
433      * range matches the value.
434      *
435      * \since QGIS 3.10.1
436      */
437     const QgsRendererRange *rangeForValue( double value ) const;
438 
439   protected:
440     QString mAttrName;
441     QgsRangeList mRanges;
442     std::unique_ptr<QgsSymbol> mSourceSymbol;
443     std::unique_ptr<QgsColorRamp> mSourceColorRamp;
444 
445     std::unique_ptr<QgsExpression> mExpression;
446     GraduatedMethod mGraduatedMethod = GraduatedColor;
447     //! attribute index (derived from attribute name in startRender)
448 
449     int mAttrNum = -1;
450     bool mCounting = false;
451 
452     std::unique_ptr<QgsDataDefinedSizeLegend> mDataDefinedSizeLegend;
453 
454     /**
455      * Gets the symbol which is used to represent \a value.
456      */
457     QgsSymbol *symbolForValue( double value ) const;
458 
459     /**
460      * Returns the matching legend key for a value.
461      */
462     QString legendKeyForValue( double value ) const;
463 
464     //! \note not available in Python bindings
465     static QString graduatedMethodStr( GraduatedMethod method ) SIP_SKIP;
466 
467     std::shared_ptr<QgsClassificationMethod> mClassificationMethod;
468 
469   private:
470 
471     /**
472      * Returns calculated value used for classifying a feature.
473      */
474     QVariant valueForFeature( const QgsFeature &feature, QgsRenderContext &context ) const;
475 
476     //! Returns list of legend symbol items from individual ranges
477     QgsLegendSymbolList baseLegendSymbolItems() const;
478 
479     // TODO QGIS 4: remove
480     Q_NOWARN_DEPRECATED_PUSH
481     static QString methodIdFromMode( QgsGraduatedSymbolRenderer::Mode mode );
482     static QgsGraduatedSymbolRenderer::Mode modeFromMethodId( const QString &methodId );
483     Q_NOWARN_DEPRECATED_POP
484 
485 #ifdef SIP_RUN
486     QgsGraduatedSymbolRenderer( const QgsGraduatedSymbolRenderer & );
487     QgsGraduatedSymbolRenderer &operator=( const QgsGraduatedSymbolRenderer & );
488 #endif
489 
490     friend class TestQgsGraduatedSymbolRenderer;
491 
492 };
493 
494 #endif // QGSGRADUATEDSYMBOLRENDERER_H
495