mirror of
https://github.com/qgis/QGIS.git
synced 2025-03-08 00:02:35 -05:00
add QgsClassificationMethod
an abstract class for classification methods
This commit is contained in:
parent
692d05ba23
commit
dbcd8875cd
297
src/core/symbology/classification/qgsclassificationmethod.cpp
Normal file
297
src/core/symbology/classification/qgsclassificationmethod.cpp
Normal file
@ -0,0 +1,297 @@
|
||||
/***************************************************************************
|
||||
qgsclassificationmethod.cpp
|
||||
---------------------
|
||||
begin : September 2019
|
||||
copyright : (C) 2019 by Denis Rouzaud
|
||||
email : denis@opengis.ch
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgis.h"
|
||||
#include "qgsclassificationmethod.h"
|
||||
#include "qgsvectorlayerutils.h"
|
||||
#include "qgsvectorlayer.h"
|
||||
#include "qgsgraduatedsymbolrenderer.h"
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsclassificationmethodregistry.h"
|
||||
|
||||
const int QgsClassificationMethod::MAX_PRECISION = 15;
|
||||
const int QgsClassificationMethod::MIN_PRECISION = -6;
|
||||
|
||||
static const QRegExp RE_TRAILING_ZEROES = QRegExp( "[.,]?0*$" );
|
||||
static const QRegExp RE_NEGATIVE_ZERO = QRegExp( "^\\-0(?:[.,]0*)?$" );
|
||||
|
||||
QList<double> QgsClassificationMethod::listToValues( const QList<QgsClassificationRange> classes )
|
||||
{
|
||||
QList<double> values;
|
||||
values.reserve( classes.count() );
|
||||
for ( int i = 0 ; i < classes.count(); i++ )
|
||||
values << classes.at( i ).upperBound();
|
||||
return values;
|
||||
}
|
||||
|
||||
QgsClassificationMethod::QgsClassificationMethod( bool valuesRequired, bool symmetricModeAvailable, int codeComplexity )
|
||||
: mValuesRequired( valuesRequired )
|
||||
, mSymmetricModeAvailable( symmetricModeAvailable )
|
||||
, mCodeComplexity( codeComplexity )
|
||||
{
|
||||
}
|
||||
|
||||
void QgsClassificationMethod::copyBase( QgsClassificationMethod *c ) const
|
||||
{
|
||||
c->setSymmetricMode( mSymmetricEnabled, mSymmetryPoint, mAstride );
|
||||
c->setLabelFormat( mLabelFormat );
|
||||
c->setLabelPrecision( mLabelPrecision );
|
||||
c->setLabelTrimTrailingZeroes( mLabelTrimTrailingZeroes );
|
||||
}
|
||||
|
||||
QgsClassificationMethod *QgsClassificationMethod::create( const QDomElement &element, const QgsReadWriteContext &context )
|
||||
{
|
||||
const QString methodId = element.attribute( QStringLiteral( "id" ) );
|
||||
QgsClassificationMethod *method = QgsApplication::classificationMethodRegistry()->method( methodId );
|
||||
|
||||
// symmetric
|
||||
QDomElement symmetricModeElem = element.firstChildElement( QStringLiteral( "symmetricMode" ) );
|
||||
if ( !symmetricModeElem.isNull() )
|
||||
{
|
||||
bool symmetricEnabled = symmetricModeElem.attribute( QStringLiteral( "enabled" ) ).toInt() == 1;
|
||||
double symmetricPoint = symmetricModeElem.attribute( QStringLiteral( "symmetrypoint" ) ).toDouble();
|
||||
bool astride = symmetricModeElem.attribute( QStringLiteral( "astride" ) ).toInt() == 1;
|
||||
method->setSymmetricMode( symmetricEnabled, symmetricPoint, astride );
|
||||
}
|
||||
|
||||
// label format
|
||||
QDomElement labelFormatElem = element.firstChildElement( QStringLiteral( "labelformat" ) );
|
||||
if ( !labelFormatElem.isNull() )
|
||||
{
|
||||
QString format = labelFormatElem.attribute( QStringLiteral( "format" ), "%1" + QStringLiteral( " - " ) + "%2" );
|
||||
int precision = labelFormatElem.attribute( QStringLiteral( "labelprecision" ), QStringLiteral( "4" ) ).toInt();
|
||||
bool trimTrailingZeroes = labelFormatElem.attribute( QStringLiteral( "trimtrailingzeroes" ), QStringLiteral( "false" ) ) == QLatin1String( "true" );
|
||||
method->setLabelFormat( format );
|
||||
method->setLabelPrecision( precision );
|
||||
method->setLabelTrimTrailingZeroes( trimTrailingZeroes );
|
||||
}
|
||||
|
||||
// Read specific properties from the implementation
|
||||
QDomElement extraElem = element.firstChildElement( QStringLiteral( "extraInformation" ) );
|
||||
if ( !extraElem.isNull() )
|
||||
method->readExtra( extraElem, context );
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
QDomElement QgsClassificationMethod::save( QDomDocument &doc, const QgsReadWriteContext &context ) const
|
||||
{
|
||||
QDomElement methodElem = doc.createElement( QStringLiteral( "classificationMethod" ) );
|
||||
|
||||
methodElem.setAttribute( QStringLiteral( "id" ), id() );
|
||||
|
||||
// symmetric
|
||||
QDomElement symmetricModeElem = doc.createElement( QStringLiteral( "symmetricMode" ) );
|
||||
symmetricModeElem.setAttribute( QStringLiteral( "enabled" ), symmetricModeEnabled() ? 1 : 0 );
|
||||
symmetricModeElem.setAttribute( QStringLiteral( "symmetrypoint" ), symmetryPoint() );
|
||||
symmetricModeElem.setAttribute( QStringLiteral( "astride" ), mAstride ? 1 : 0 );
|
||||
methodElem.appendChild( symmetricModeElem );
|
||||
|
||||
// label format
|
||||
QDomElement labelFormatElem = doc.createElement( QStringLiteral( "labelFormat" ) );
|
||||
labelFormatElem.setAttribute( QStringLiteral( "format" ), labelFormat() );
|
||||
labelFormatElem.setAttribute( QStringLiteral( "labelprecision" ), labelPrecision() );
|
||||
labelFormatElem.setAttribute( QStringLiteral( "trimtrailingzeroes" ), labelTrimTrailingZeroes() ? 1 : 0 );
|
||||
methodElem.appendChild( labelFormatElem );
|
||||
|
||||
// extra information
|
||||
QDomElement extraElem = doc.createElement( QStringLiteral( "extraInformation" ) );
|
||||
saveExtra( extraElem, context );
|
||||
methodElem.appendChild( extraElem );
|
||||
|
||||
return methodElem;
|
||||
}
|
||||
|
||||
|
||||
void QgsClassificationMethod::setSymmetricMode( bool enabled, double symmetryPoint, bool astride )
|
||||
{
|
||||
mSymmetricEnabled = enabled;
|
||||
mSymmetryPoint = symmetryPoint;
|
||||
mAstride = astride;
|
||||
}
|
||||
|
||||
void QgsClassificationMethod::setLabelPrecision( int precision )
|
||||
{
|
||||
// Limit the range of decimal places to a reasonable range
|
||||
precision = qBound( MIN_PRECISION, precision, MAX_PRECISION );
|
||||
mLabelPrecision = precision;
|
||||
mLabelNumberScale = 1.0;
|
||||
mLabelNumberSuffix.clear();
|
||||
while ( precision < 0 )
|
||||
{
|
||||
precision++;
|
||||
mLabelNumberScale /= 10.0;
|
||||
mLabelNumberSuffix.append( '0' );
|
||||
}
|
||||
}
|
||||
|
||||
QString QgsClassificationMethod::formatNumber( double value ) const
|
||||
{
|
||||
if ( mLabelPrecision > 0 )
|
||||
{
|
||||
QString valueStr = QLocale().toString( value, 'f', mLabelPrecision );
|
||||
if ( mLabelTrimTrailingZeroes )
|
||||
valueStr = valueStr.remove( RE_TRAILING_ZEROES );
|
||||
if ( RE_NEGATIVE_ZERO.exactMatch( valueStr ) )
|
||||
valueStr = valueStr.mid( 1 );
|
||||
return valueStr;
|
||||
}
|
||||
else
|
||||
{
|
||||
QString valueStr = QLocale().toString( value * mLabelNumberScale, 'f', 0 );
|
||||
if ( valueStr == QLatin1String( "-0" ) )
|
||||
valueStr = '0';
|
||||
if ( valueStr != QLatin1String( "0" ) )
|
||||
valueStr = valueStr + mLabelNumberSuffix;
|
||||
return valueStr;
|
||||
}
|
||||
}
|
||||
|
||||
QList<QgsClassificationRange> QgsClassificationMethod::classes( const QgsVectorLayer *vl, const QString &expression, int numberOfClasses )
|
||||
{
|
||||
if ( expression.isEmpty() )
|
||||
return QList<QgsClassificationRange>();
|
||||
|
||||
if ( numberOfClasses < 1 )
|
||||
numberOfClasses = 1;
|
||||
|
||||
QList<double> values;
|
||||
double minimum;
|
||||
double maximum;
|
||||
|
||||
int fieldIndex = vl->fields().indexFromName( expression );
|
||||
|
||||
bool ok;
|
||||
if ( mValuesRequired || fieldIndex == -1 )
|
||||
{
|
||||
values = QgsVectorLayerUtils::getDoubleValues( vl, expression, ok );
|
||||
if ( !ok || values.isEmpty() )
|
||||
return QList<QgsClassificationRange>();
|
||||
|
||||
auto result = std::minmax_element( values.begin(), values.end() );
|
||||
minimum = *result.first;
|
||||
maximum = *result.second;
|
||||
}
|
||||
else
|
||||
{
|
||||
minimum = vl->minimumValue( fieldIndex ).toDouble();
|
||||
maximum = vl->maximumValue( fieldIndex ).toDouble();
|
||||
}
|
||||
|
||||
// get the breaks
|
||||
const QList<double> breaks = calculateBreaks( minimum, maximum, values, numberOfClasses );
|
||||
// create classes
|
||||
return breaksToClasses( breaks );
|
||||
}
|
||||
|
||||
QList<QgsClassificationRange> QgsClassificationMethod::classes( const QList<double> &values, int numberOfClasses )
|
||||
{
|
||||
auto result = std::minmax_element( values.begin(), values.end() );
|
||||
double minimum = *result.first;
|
||||
double maximum = *result.second;
|
||||
|
||||
// get the breaks
|
||||
const QList<double> breaks = calculateBreaks( minimum, maximum, values, numberOfClasses );
|
||||
// create classes
|
||||
return breaksToClasses( breaks );
|
||||
}
|
||||
|
||||
QList<QgsClassificationRange> QgsClassificationMethod::classes( double minimum, double maximum, int numberOfClasses )
|
||||
{
|
||||
if ( mValuesRequired )
|
||||
{
|
||||
QgsDebugMsg( QString( "The classification method %1 tries to calculate classes without values while they are required." ).arg( name() ) );
|
||||
}
|
||||
|
||||
// get the breaks
|
||||
QList<double> breaks = calculateBreaks( minimum, maximum, QList<double>(), numberOfClasses );
|
||||
breaks.insert( 0, minimum );
|
||||
// create classes
|
||||
return breaksToClasses( breaks );
|
||||
}
|
||||
|
||||
QList<QgsClassificationRange> QgsClassificationMethod::breaksToClasses( const QList<double> &breaks ) const
|
||||
{
|
||||
QList<QgsClassificationRange> classes;
|
||||
|
||||
QString label;
|
||||
for ( int i = 1; i < breaks.count(); i++ )
|
||||
{
|
||||
|
||||
const double lowerValue = breaks.at( i - 1 );
|
||||
const double upperValue = breaks.at( i );
|
||||
|
||||
ClassPosition pos = Inner;
|
||||
if ( i == 0 )
|
||||
pos = LowerBound;
|
||||
else if ( i == breaks.count() - 1 )
|
||||
pos = UpperBound;
|
||||
|
||||
QString label = labelForRange( lowerValue, upperValue, pos );
|
||||
classes << QgsClassificationRange( label, lowerValue, upperValue );
|
||||
}
|
||||
|
||||
return classes;
|
||||
}
|
||||
|
||||
void QgsClassificationMethod::makeBreaksSymmetric( QList<double> &breaks, double symmetryPoint, bool astride )
|
||||
{
|
||||
// remove the breaks that are above the existing opposite sign classes
|
||||
// to keep colors symmetrically balanced around symmetryPoint
|
||||
// if astride is true, remove the symmetryPoint break so that
|
||||
// the 2 classes form only one
|
||||
|
||||
if ( breaks.count() < 2 )
|
||||
return;
|
||||
|
||||
std::sort( breaks.begin(), breaks.end() );
|
||||
// breaks contain the maximum of the distrib but not the minimum
|
||||
double distBelowSymmetricValue = std::fabs( breaks[0] - symmetryPoint );
|
||||
double distAboveSymmetricValue = std::fabs( breaks[ breaks.size() - 2 ] - symmetryPoint ) ;
|
||||
double absMin = std::min( distAboveSymmetricValue, distBelowSymmetricValue );
|
||||
|
||||
// make symmetric
|
||||
for ( int i = 0; i <= breaks.size() - 2; ++i )
|
||||
{
|
||||
// part after "absMin" is for doubles rounding issues
|
||||
if ( std::fabs( breaks.at( i ) - symmetryPoint ) >= ( absMin - std::fabs( breaks[0] - breaks[1] ) / 100. ) )
|
||||
{
|
||||
breaks.removeAt( i );
|
||||
--i;
|
||||
}
|
||||
}
|
||||
// remove symmetry point
|
||||
if ( astride ) // && breaks.indexOf( symmetryPoint ) != -1) // if symmetryPoint is found
|
||||
{
|
||||
breaks.removeAt( breaks.indexOf( symmetryPoint ) );
|
||||
}
|
||||
}
|
||||
|
||||
QString QgsClassificationMethod::labelForRange( const QgsRendererRange &range, QgsClassificationMethod::ClassPosition position ) const
|
||||
{
|
||||
return labelForRange( range.lowerValue(), range.upperValue(), position );
|
||||
}
|
||||
|
||||
QString QgsClassificationMethod::labelForRange( const double &lowerValue, const double &upperValue, ClassPosition position ) const
|
||||
{
|
||||
Q_UNUSED( position )
|
||||
|
||||
const QString lowerLabel = valueToLabel( lowerValue );
|
||||
const QString upperLabel = valueToLabel( upperValue );
|
||||
|
||||
QString label( mLabelFormat );
|
||||
label.replace( QLatin1String( "%1" ), lowerLabel ).replace( QLatin1String( "%2" ), upperLabel );
|
||||
return label;
|
||||
}
|
286
src/core/symbology/classification/qgsclassificationmethod.h
Normal file
286
src/core/symbology/classification/qgsclassificationmethod.h
Normal file
@ -0,0 +1,286 @@
|
||||
/***************************************************************************
|
||||
qgsclassificationmethod.h
|
||||
---------------------
|
||||
begin : September 2019
|
||||
copyright : (C) 2019 by Denis Rouzaud
|
||||
email : denis@opengis.ch
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSCLASSIFICATIONMETHOD_H
|
||||
#define QGSCLASSIFICATIONMETHOD_H
|
||||
|
||||
#include "qgis_core.h"
|
||||
#include "qgsprocessingparameters.h"
|
||||
|
||||
class QgsVectorLayer;
|
||||
class QgsRendererRange;
|
||||
|
||||
|
||||
#ifdef SIP_RUN
|
||||
// This is required for the ConvertToSubClassCode to work properly
|
||||
// so RTTI for casting is available in the whole module.
|
||||
% ModuleCode
|
||||
#include "qgsclassificationequalinterval.h"
|
||||
#include "qgsclassificationjenks.h"
|
||||
#include "qgsclassificationprettybreaks.h"
|
||||
#include "qgsclassificationquantile.h"
|
||||
#include "qgsclassificationstandarddeviation.h"
|
||||
% End
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* QgsClassificationRange contains the information about a classification range
|
||||
*/
|
||||
class CORE_EXPORT QgsClassificationRange
|
||||
{
|
||||
public:
|
||||
//! Constructor
|
||||
QgsClassificationRange( const QString &label, const double &lowerBound, const double &upperBound )
|
||||
: mLabel( label )
|
||||
, mLowerBound( lowerBound )
|
||||
, mUpperBound( upperBound )
|
||||
{}
|
||||
//! Returns the lower bound
|
||||
double lowerBound() const {return mLowerBound;}
|
||||
//! Returns the upper bound
|
||||
double upperBound() const {return mUpperBound;}
|
||||
|
||||
//! Returns the lower bound
|
||||
QString label() const {return mLabel;}
|
||||
|
||||
private:
|
||||
QString mLabel;
|
||||
double mLowerBound;
|
||||
double mUpperBound;
|
||||
};
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* \ingroup core
|
||||
* QgsClassification is an abstract class for implementations of classification methods
|
||||
* \since QGIS 3.10
|
||||
*/
|
||||
class CORE_EXPORT QgsClassificationMethod SIP_ABSTRACT
|
||||
{
|
||||
|
||||
#ifdef SIP_RUN
|
||||
SIP_CONVERT_TO_SUBCLASS_CODE
|
||||
if ( dynamic_cast<QgsClassificationEqualInterval *>( sipCpp ) )
|
||||
sipType = sipType_QgsClassificationEqualInterval;
|
||||
else if ( dynamic_cast<QgsClassificationJenks *>( sipCpp ) )
|
||||
sipType = sipType_QgsClassificationJenks;
|
||||
else if ( dynamic_cast<QgsClassificationPrettyBreaks *>( sipCpp ) )
|
||||
sipType = sipType_QgsClassificationPrettyBreaks;
|
||||
else if ( dynamic_cast<QgsClassificationQuantile *>( sipCpp ) )
|
||||
sipType = sipType_QgsClassificationQuantile;
|
||||
else if ( dynamic_cast<QgsClassificationStandardDeviation *>( sipCpp ) )
|
||||
sipType = sipType_QgsClassificationStandardDeviation;
|
||||
else
|
||||
sipType = 0;
|
||||
SIP_END
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
//! Defines the class position
|
||||
enum ClassPosition
|
||||
{
|
||||
LowerBound,
|
||||
Inner,
|
||||
UpperBound
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a classification method.
|
||||
* \param valuesRequired if TRUE, this means that the method requires a set of values to determine the classes
|
||||
* \param symmetricModeAvailable if TRUE, this allows using symmetric classification
|
||||
* \param codeCommplexity as the exponent in the big O notation
|
||||
* \param
|
||||
*/
|
||||
explicit QgsClassificationMethod( bool valuesRequired, bool symmetricModeAvailable, int codeComplexity = 1 );
|
||||
|
||||
virtual ~QgsClassificationMethod() = default;
|
||||
|
||||
virtual QgsClassificationMethod *clone() const = 0 SIP_FACTORY;
|
||||
|
||||
//! The readable and translate name of the method
|
||||
virtual QString name() const = 0;
|
||||
|
||||
//! The id of the method
|
||||
virtual QString id() const = 0; // as saved in the project, must be unique in registry
|
||||
|
||||
/**
|
||||
* Returns the label for a range
|
||||
*/
|
||||
virtual QString labelForRange( const double &lowerValue, const double &upperValue, ClassPosition position = Inner ) const;
|
||||
|
||||
|
||||
//! Writes extra information about the method
|
||||
virtual void saveExtra( QDomElement &element, const QgsReadWriteContext &context ) const {Q_UNUSED( element ); Q_UNUSED( context )}
|
||||
//! Reads extra information to apply it to the method
|
||||
virtual void readExtra( const QDomElement &element, const QgsReadWriteContext &context ) {Q_UNUSED( element ); Q_UNUSED( context )}
|
||||
|
||||
// *******************
|
||||
// non-virtual methods
|
||||
|
||||
/**
|
||||
* Returns if the method requires values to calculate the classes
|
||||
* If not, bounds are sufficient
|
||||
*/
|
||||
bool valuesRequired() const {return mValuesRequired;}
|
||||
|
||||
//! Code complexity as the exponent in Big O notation
|
||||
int codeComplexity() const {return mCodeComplexity;}
|
||||
|
||||
/**
|
||||
* Returns if the method supports symmetric calculation
|
||||
*/
|
||||
bool symmetricModeAvailable() const {return mSymmetricModeAvailable;}
|
||||
|
||||
/**
|
||||
* Returns if the symmetric mode is enabled
|
||||
*/
|
||||
bool symmetricModeEnabled() const {return mSymmetricModeAvailable && mSymmetricEnabled;}
|
||||
|
||||
/**
|
||||
* Returns the symmetry point for symmetric mode
|
||||
*/
|
||||
double symmetryPoint() const {return mSymmetryPoint;}
|
||||
|
||||
/**
|
||||
* Returns if the symmetric mode is astride
|
||||
* if TRUE, it will remove the symmetry point break so that the 2 classes form only one
|
||||
*/
|
||||
bool astride() const {return mAstride;}
|
||||
|
||||
/**
|
||||
* Defines if the symmetric mode is enables and configures its parameters.
|
||||
* If the symmetric mode is not available in the current implementation, calling this method has no effect.
|
||||
* \param enabled if the symmetric mode is enabled
|
||||
* \param symmetryPoint the value of the symmetry point
|
||||
* \param astride if TRUE, it will remove the symmetry point break so that the 2 classes form only one
|
||||
*/
|
||||
void setSymmetricMode( bool enabled, double symmetryPoint = 0, bool astride = false );
|
||||
|
||||
// Label properties
|
||||
//! Returns the format of the label for the classes
|
||||
QString labelFormat() const { return mLabelFormat; }
|
||||
//! Defines the format of the labels for the classes, using %1 and %2 for the bounds
|
||||
void setLabelFormat( const QString &format ) { mLabelFormat = format; }
|
||||
//! Returns the precision for the formatting of the labels
|
||||
int labelPrecision() const { return mLabelPrecision; }
|
||||
//! Defines the precision for the formatting of the labels
|
||||
void setLabelPrecision( int labelPrecision );
|
||||
//! Returns if the trailing 0 are trimmed in the label
|
||||
bool labelTrimTrailingZeroes() const { return mLabelTrimTrailingZeroes; }
|
||||
//! Defines if the trailing 0 are trimmed in the label
|
||||
void setLabelTrimTrailingZeroes( bool trimTrailingZeroes ) { mLabelTrimTrailingZeroes = trimTrailingZeroes; }
|
||||
|
||||
//! Transforms a list of classes to a list of breaks
|
||||
static QList<double> listToValues( const QList<QgsClassificationRange> classes );
|
||||
|
||||
/**
|
||||
* This will calculate the breaks for a given layer to define the classes.
|
||||
* The breaks do not contain the uppper and lower bounds (minimum and maximum values).
|
||||
* \param vl The vector layer
|
||||
* \param fieldName The name of the field on which the classes are calculated
|
||||
* \param numberOfClasses The number of classes to be returned
|
||||
*/
|
||||
QList<QgsClassificationRange> classes( const QgsVectorLayer *vl, const QString &expression, int numberOfClasses );
|
||||
|
||||
/**
|
||||
* This will calculate the breaks for a list of values.
|
||||
* The breaks do not contain the uppper and lower bounds (minimum and maximum values)
|
||||
* \param values The list of values
|
||||
* \param numberOfClasses The number of classes to be returned
|
||||
*/
|
||||
QList<QgsClassificationRange> classes( const QList<double> &values, int numberOfClasses );
|
||||
|
||||
/**
|
||||
* This will calculate the classes for defined bounds without any values.
|
||||
* The breaks do not contain the uppper and lower bounds (minimum and maximum values)
|
||||
* \warning If the method implementation requires values, this will return an empty list.
|
||||
* \param values The list of values
|
||||
* \param numberOfClasses The number of classes to be returned
|
||||
*/
|
||||
QList<QgsClassificationRange> classes( double minimum, double maximum, int numberOfClasses );
|
||||
|
||||
QDomElement save( QDomDocument &doc, const QgsReadWriteContext &context ) const;
|
||||
static QgsClassificationMethod *create( const QDomElement &element, const QgsReadWriteContext &context ) SIP_FACTORY;
|
||||
|
||||
/**
|
||||
* Remove the breaks that are above the existing opposite sign classes to keep colors symmetrically balanced around symmetryPoint
|
||||
* Does not put a break on the symmetryPoint. This is done before.
|
||||
* \param breaks The breaks of an already-done classification
|
||||
* \param symmetryPoint The point around which we want a symmetry
|
||||
* \param astride A bool indicating if the symmetry is made astride the symmetryPoint or not ( [-1,1] vs. [-1,0][0,1] )
|
||||
*/
|
||||
static void makeBreaksSymmetric( QList<double> &breaks SIP_INOUT, double symmetryPoint, bool astride );
|
||||
|
||||
/**
|
||||
* Returns the label for a range
|
||||
*/
|
||||
QString labelForRange( const QgsRendererRange &range, ClassPosition position = Inner ) const;
|
||||
|
||||
|
||||
|
||||
static const int MAX_PRECISION;
|
||||
static const int MIN_PRECISION;
|
||||
|
||||
protected:
|
||||
|
||||
//! Copy the parameters (shall be used in clone implementation)
|
||||
void copyBase( QgsClassificationMethod *c ) const;
|
||||
|
||||
//! Format the number according to label properties
|
||||
QString formatNumber( double value ) const;
|
||||
|
||||
// parameters (set by setters)
|
||||
// if some are added here, they should be handled in the clone method
|
||||
bool mSymmetricEnabled = false;
|
||||
double mSymmetryPoint = 0;
|
||||
bool mAstride = false;
|
||||
QString mLabelFormat;
|
||||
int mLabelPrecision = 4;
|
||||
bool mLabelTrimTrailingZeroes = true;
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Calculate the breaks, should be reimplemented, values might be an empty list
|
||||
* If the symmetric mode is available, the implementation is responsible of applying the symmetry
|
||||
* The maximum value is expected to be added at the end of the list, but not the minimum
|
||||
*/
|
||||
virtual QList<double> calculateBreaks( double minimum, double maximum,
|
||||
const QList<double> &values, int numberOfClasses ) = 0;
|
||||
|
||||
//! This is called after calculating the breaks or restoring from XML, so it can rely on private variables
|
||||
virtual QString valueToLabel( const double &value ) const {return formatNumber( value );}
|
||||
|
||||
|
||||
//! Create a list of ranges from a list of classes
|
||||
QList<QgsClassificationRange> breaksToClasses( const QList<double> &breaks ) const;
|
||||
|
||||
// implementation properties (set by initialization)
|
||||
bool mValuesRequired; // if all values are required to calculate breaks
|
||||
bool mSymmetricModeAvailable;
|
||||
int mCodeComplexity;
|
||||
|
||||
// values used to manage number formatting - precision and trailing zeroes
|
||||
double mLabelNumberScale = 1.0;
|
||||
QString mLabelNumberSuffix;
|
||||
};
|
||||
|
||||
#endif // QGSCLASSIFICATIONMETHOD_H
|
Loading…
x
Reference in New Issue
Block a user