Branch data Line data Source code
1 : : /*************************************************************************** 2 : : qgsclassificationmethod.h 3 : : --------------------- 4 : : begin : September 2019 5 : : copyright : (C) 2019 by Denis Rouzaud 6 : : email : denis@opengis.ch 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 QGSCLASSIFICATIONMETHOD_H 17 : : #define QGSCLASSIFICATIONMETHOD_H 18 : : 19 : : #include <QIcon> 20 : : 21 : : #include "qgis_sip.h" 22 : : #include "qgis_core.h" 23 : : #include "qgsprocessingparameters.h" 24 : : 25 : : class QgsVectorLayer; 26 : : class QgsRendererRange; 27 : : 28 : : 29 : : #ifdef SIP_RUN 30 : : // This is required for the ConvertToSubClassCode to work properly 31 : : // so RTTI for casting is available in the whole module. 32 : : % ModuleCode 33 : : #include "qgsclassificationequalinterval.h" 34 : : #include "qgsclassificationjenks.h" 35 : : #include "qgsclassificationprettybreaks.h" 36 : : #include "qgsclassificationquantile.h" 37 : : #include "qgsclassificationstandarddeviation.h" 38 : : % End 39 : : #endif 40 : : 41 : : 42 : : 43 : : /** 44 : : * \ingroup core 45 : : * \brief QgsClassificationRange contains the information about a classification range 46 : : * \since QGIS 3.10 47 : : */ 48 : 0 : class CORE_EXPORT QgsClassificationRange 49 : : { 50 : : public: 51 : : //! Constructor 52 : 0 : QgsClassificationRange( const QString &label, double lowerBound, double upperBound ) 53 : 0 : : mLabel( label ) 54 : 0 : , mLowerBound( lowerBound ) 55 : 0 : , mUpperBound( upperBound ) 56 : 0 : {} 57 : : //! Returns the lower bound 58 : 0 : double lowerBound() const {return mLowerBound;} 59 : : //! Returns the upper bound 60 : 0 : double upperBound() const {return mUpperBound;} 61 : : 62 : : //! Returns the lower bound 63 : 0 : QString label() const {return mLabel;} 64 : : 65 : : #ifdef SIP_RUN 66 : : SIP_PYOBJECT __repr__(); 67 : : % MethodCode 68 : : QString str = QStringLiteral( "<QgsClassificationRange: '%1'>" ).arg( sipCpp->label() ); 69 : : sipRes = PyUnicode_FromString( str.toUtf8().constData() ); 70 : : % End 71 : : #endif 72 : : 73 : : private: 74 : : QString mLabel; 75 : : double mLowerBound; 76 : : double mUpperBound; 77 : : }; 78 : : 79 : : 80 : : 81 : : /** 82 : : * \ingroup core 83 : : * \brief QgsClassificationMethod is an abstract class for implementations of classification methods 84 : : * \see QgsClassificationMethodRegistry 85 : : * \since QGIS 3.10 86 : : */ 87 : : class CORE_EXPORT QgsClassificationMethod SIP_ABSTRACT 88 : : { 89 : : 90 : : #ifdef SIP_RUN 91 : : SIP_CONVERT_TO_SUBCLASS_CODE 92 : : if ( dynamic_cast<QgsClassificationEqualInterval *>( sipCpp ) ) 93 : : sipType = sipType_QgsClassificationEqualInterval; 94 : : else if ( dynamic_cast<QgsClassificationJenks *>( sipCpp ) ) 95 : : sipType = sipType_QgsClassificationJenks; 96 : : else if ( dynamic_cast<QgsClassificationPrettyBreaks *>( sipCpp ) ) 97 : : sipType = sipType_QgsClassificationPrettyBreaks; 98 : : else if ( dynamic_cast<QgsClassificationQuantile *>( sipCpp ) ) 99 : : sipType = sipType_QgsClassificationQuantile; 100 : : else if ( dynamic_cast<QgsClassificationStandardDeviation *>( sipCpp ) ) 101 : : sipType = sipType_QgsClassificationStandardDeviation; 102 : : else 103 : : sipType = 0; 104 : : SIP_END 105 : : #endif 106 : : 107 : : public: 108 : : 109 : : //! Flags for the classification method 110 : : enum MethodProperty 111 : : { 112 : : NoFlag = 0, //!< No flag 113 : : ValuesNotRequired = 1 << 1, //!< Deprecated since QGIS 3.12 114 : : SymmetricModeAvailable = 1 << 2, //!< This allows using symmetric classification 115 : : }; 116 : : Q_DECLARE_FLAGS( MethodProperties, MethodProperty ) 117 : : 118 : : 119 : : //! Defines the class position 120 : : enum ClassPosition 121 : : { 122 : : LowerBound, //!< The class is at the lower bound 123 : : Inner, //!< The class is not at a bound 124 : : UpperBound //!< The class is at the upper bound 125 : : }; 126 : : 127 : : /** 128 : : * Creates a classification method. 129 : : * \param properties The properties of the implemented method 130 : : * \param codeComplexity as the exponent in the big O notation 131 : : */ 132 : : explicit QgsClassificationMethod( MethodProperties properties = NoFlag, int codeComplexity = 1 ); 133 : : 134 : : virtual ~QgsClassificationMethod(); 135 : : 136 : : /** 137 : : * Returns a clone of the method. 138 : : * Implementation can take advantage of copyBase method which copies the parameters of the base class 139 : : */ 140 : : virtual QgsClassificationMethod *clone() const = 0 SIP_FACTORY; 141 : : 142 : : //! The readable and translate name of the method 143 : : virtual QString name() const = 0; 144 : : 145 : : //! The id of the method as saved in the project, must be unique in registry 146 : : virtual QString id() const = 0; 147 : : 148 : : //! The icon of the method 149 : 0 : virtual QIcon icon() const {return QIcon();} 150 : : 151 : : /** 152 : : * Returns the label for a range 153 : : */ 154 : : virtual QString labelForRange( double lowerValue, double upperValue, ClassPosition position = Inner ) const; 155 : : 156 : : 157 : : //! Writes extra information about the method 158 : 0 : virtual void writeXml( QDomElement &element, const QgsReadWriteContext &context ) const {Q_UNUSED( element ); Q_UNUSED( context )} 159 : : //! Reads extra information to apply it to the method 160 : 0 : virtual void readXml( const QDomElement &element, const QgsReadWriteContext &context ) {Q_UNUSED( element ); Q_UNUSED( context )} 161 : : 162 : : /** 163 : : * Returns if the method requires values to calculate the classes 164 : : * If not, bounds are sufficient 165 : : */ 166 : 0 : virtual bool valuesRequired() const {return true;} 167 : : 168 : : 169 : : // ******************* 170 : : // non-virtual methods 171 : : 172 : : //! Code complexity as the exponent in Big O notation 173 : : int codeComplexity() const {return mCodeComplexity;} 174 : : 175 : : /** 176 : : * Returns if the method supports symmetric calculation 177 : : */ 178 : 0 : bool symmetricModeAvailable() const {return mFlags.testFlag( SymmetricModeAvailable );} 179 : : 180 : : /** 181 : : * Returns if the symmetric mode is enabled 182 : : */ 183 : 0 : bool symmetricModeEnabled() const {return symmetricModeAvailable() && mSymmetricEnabled;} 184 : : 185 : : /** 186 : : * Returns the symmetry point for symmetric mode 187 : : */ 188 : 0 : double symmetryPoint() const {return mSymmetryPoint;} 189 : : 190 : : /** 191 : : * Returns if the symmetric mode is astride 192 : : * if TRUE, it will remove the symmetry point break so that the 2 classes form only one 193 : : */ 194 : 0 : bool symmetryAstride() const {return mSymmetryAstride;} 195 : : 196 : : /** 197 : : * Defines if the symmetric mode is enables and configures its parameters. 198 : : * If the symmetric mode is not available in the current implementation, calling this method has no effect. 199 : : * \param enabled if the symmetric mode is enabled 200 : : * \param symmetryPoint the value of the symmetry point 201 : : * \param symmetryAstride if TRUE, it will remove the symmetry point break so that the 2 classes form only one 202 : : */ 203 : : void setSymmetricMode( bool enabled, double symmetryPoint = 0, bool symmetryAstride = false ); 204 : : 205 : : // Label properties 206 : : //! Returns the format of the label for the classes 207 : 0 : QString labelFormat() const { return mLabelFormat; } 208 : : //! Defines the format of the labels for the classes, using %1 and %2 for the bounds 209 : 0 : void setLabelFormat( const QString &format ) { mLabelFormat = format; } 210 : : //! Returns the precision for the formatting of the labels 211 : 0 : int labelPrecision() const { return mLabelPrecision; } 212 : : //! Defines the precision for the formatting of the labels 213 : : void setLabelPrecision( int labelPrecision ); 214 : : //! Returns if the trailing 0 are trimmed in the label 215 : 0 : bool labelTrimTrailingZeroes() const { return mLabelTrimTrailingZeroes; } 216 : : //! Defines if the trailing 0 are trimmed in the label 217 : 0 : void setLabelTrimTrailingZeroes( bool trimTrailingZeroes ) { mLabelTrimTrailingZeroes = trimTrailingZeroes; } 218 : : 219 : : //! Transforms a list of classes to a list of breaks 220 : : static QList<double> rangesToBreaks( const QList<QgsClassificationRange> &classes ); 221 : : 222 : : /** 223 : : * This will calculate the classes for a given layer to define the classes. 224 : : * \param layer The vector layer 225 : : * \param expression The name of the field on which the classes are calculated 226 : : * \param nclasses The number of classes to be returned 227 : : */ 228 : : QList<QgsClassificationRange> classes( const QgsVectorLayer *layer, const QString &expression, int nclasses ); 229 : : 230 : : /** 231 : : * This will calculate the classes for a list of values. 232 : : * \param values The list of values 233 : : * \param nclasses The number of classes to be returned 234 : : */ 235 : : QList<QgsClassificationRange> classes( const QList<double> &values, int nclasses ); 236 : : 237 : : /** 238 : : * This will calculate the classes for defined bounds without any values. 239 : : * \warning If the method implementation requires values, this will return an empty list. 240 : : * \param minimum The minimum value for the breaks 241 : : * \param maximum The maximum value for the breaks 242 : : * \param nclasses The number of classes to be returned 243 : : */ 244 : : QList<QgsClassificationRange> classes( double minimum, double maximum, int nclasses ); 245 : : 246 : : /** 247 : : * Saves the method to a DOM element and return it 248 : : * \param doc the DOM document 249 : : * \param context the read/write context 250 : : */ 251 : : QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const; 252 : : 253 : : /** 254 : : * Reads the DOM element and return a new classification method from it 255 : : * \param element the DOM element 256 : : * \param context the read/write context 257 : : */ 258 : : static QgsClassificationMethod *create( const QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY; 259 : : 260 : : /** 261 : : * Remove the breaks that are above the existing opposite sign classes to keep colors symmetrically balanced around symmetryPoint 262 : : * Does not put a break on the symmetryPoint. This is done before. 263 : : * \param breaks The breaks of an already-done classification 264 : : * \param symmetryPoint The point around which we want a symmetry 265 : : * \param astride A bool indicating if the symmetry is made astride the symmetryPoint or not ( [-1,1] vs. [-1,0][0,1] ) 266 : : */ 267 : : static void makeBreaksSymmetric( QList<double> &breaks SIP_INOUT, double symmetryPoint, bool astride ); 268 : : 269 : : /** 270 : : * Returns the label for a range 271 : : */ 272 : : QString labelForRange( const QgsRendererRange &range, ClassPosition position = Inner ) const; 273 : : 274 : : /** 275 : : * Returns the parameter from its name 276 : : * \since QGIS 3.12 277 : : */ 278 : : const QgsProcessingParameterDefinition *parameterDefinition( const QString ¶meterName ) const; 279 : : 280 : : /** 281 : : * Returns the list of parameters 282 : : * \since QGIS 3.12 283 : : */ 284 : : QgsProcessingParameterDefinitions parameterDefinitions() const {return mParameters;} 285 : : 286 : : /** 287 : : * Defines the values of the additional parameters 288 : : * \since QGIS 3.12 289 : : */ 290 : : void setParameterValues( const QVariantMap &values ); 291 : : 292 : : /** 293 : : * Returns the values of the processing parameters. 294 : : * One could use QgsProcessingParameters::parameterAsXxxx to retrieve the actual value of a parameter. 295 : : * \since QGIS 3.12 296 : : */ 297 : 0 : QVariantMap parameterValues() const {return mParameterValues;} 298 : : 299 : : static const int MAX_PRECISION; 300 : : static const int MIN_PRECISION; 301 : : 302 : : protected: 303 : : 304 : : //! Copy the parameters (shall be used in clone implementation) 305 : : void copyBase( QgsClassificationMethod *c ) const; 306 : : 307 : : //! Format the number according to label properties 308 : : QString formatNumber( double value ) const; 309 : : 310 : : /** 311 : : * Add a parameter to the method. 312 : : * The paramaeter is a processing parameter which will allow its configuration in the GUI. 313 : : * \note Only parameters having their widget implementation in C++ are supported. i.e. pure 314 : : * Python parameters are not supported. 315 : : * \since QGIS 3.12 316 : : */ 317 : : void addParameter( QgsProcessingParameterDefinition *definition SIP_TRANSFER ); 318 : : 319 : : private: 320 : : 321 : : /** 322 : : * Calculate the breaks, should be reimplemented, values might be an empty list if they are not required 323 : : * If the symmetric mode is available, the implementation is responsible of applying the symmetry 324 : : * The maximum value is expected to be added at the end of the list, but not the minimum 325 : : */ 326 : : virtual QList<double> calculateBreaks( double &minimum, double &maximum, 327 : : const QList<double> &values, int nclasses ) = 0; 328 : : 329 : : //! This is called after calculating the breaks or restoring from XML, so it can rely on private variables 330 : 0 : virtual QString valueToLabel( double value ) const {return formatNumber( value );} 331 : : 332 : : //! Create a list of ranges from a list of classes 333 : : QList<QgsClassificationRange> breaksToClasses( const QList<double> &breaks ) const; 334 : : 335 : : // implementation properties (set by initialization) 336 : : MethodProperties mFlags = MethodProperties(); 337 : : int mCodeComplexity = 1; 338 : : 339 : : // parameters (set by setters) 340 : : // if some are added here, they should be handled in the clone method 341 : : bool mSymmetricEnabled = false; 342 : : double mSymmetryPoint = 0; 343 : : bool mSymmetryAstride = false; 344 : : int mLabelPrecision = 4; 345 : : bool mLabelTrimTrailingZeroes = true; 346 : : QString mLabelFormat; 347 : : 348 : : // values used to manage number formatting - precision and trailing zeroes 349 : : double mLabelNumberScale = 1.0; 350 : : QString mLabelNumberSuffix; 351 : : 352 : : //! additional parameters 353 : : QgsProcessingParameterDefinitions mParameters; 354 : : QVariantMap mParameterValues; 355 : : }; 356 : : 357 : : Q_DECLARE_OPERATORS_FOR_FLAGS( QgsClassificationMethod::MethodProperties ) 358 : : 359 : : #endif // QGSCLASSIFICATIONMETHOD_H