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