16#include <QRegularExpression>
35 values.reserve(
classes.count() );
36 for (
int i = 0 ; i <
classes.count(); i++ )
37 values <<
classes.at( i ).upperBound();
42 : mFlags( properties )
43 , mCodeComplexity( codeComplexity )
44 , mLabelFormat( QStringLiteral(
"%1 - %2" ) )
50 qDeleteAll( mParameters );
55 c->setSymmetricMode( mSymmetricEnabled, mSymmetryPoint, mSymmetryAstride );
56 c->setLabelFormat( mLabelFormat );
57 c->setLabelPrecision( mLabelPrecision );
58 c->setLabelTrimTrailingZeroes( mLabelTrimTrailingZeroes );
59 c->setParameterValues( mParameterValues );
64 const QString methodId = element.attribute( QStringLiteral(
"id" ) );
68 QDomElement symmetricModeElem = element.firstChildElement( QStringLiteral(
"symmetricMode" ) );
69 if ( !symmetricModeElem.isNull() )
71 bool symmetricEnabled = symmetricModeElem.attribute( QStringLiteral(
"enabled" ) ).toInt() == 1;
72 double symmetricPoint = symmetricModeElem.attribute( QStringLiteral(
"symmetrypoint" ) ).toDouble();
73 bool astride = symmetricModeElem.attribute( QStringLiteral(
"astride" ) ).toInt() == 1;
78 QDomElement labelFormatElem = element.firstChildElement( QStringLiteral(
"labelFormat" ) );
79 if ( !labelFormatElem.isNull() )
81 QString format = labelFormatElem.attribute( QStringLiteral(
"format" ),
"%1" + QStringLiteral(
" - " ) +
"%2" );
82 int precision = labelFormatElem.attribute( QStringLiteral(
"labelprecision" ), QStringLiteral(
"4" ) ).toInt();
83 bool trimTrailingZeroes = labelFormatElem.attribute( QStringLiteral(
"trimtrailingzeroes" ), QStringLiteral(
"false" ) ) == QLatin1String(
"true" );
90 QDomElement parametersElem = element.firstChildElement( QStringLiteral(
"parameters" ) );
95 QDomElement extraElem = element.firstChildElement( QStringLiteral(
"extraInformation" ) );
96 if ( !extraElem.isNull() )
97 method->
readXml( extraElem, context );
104 QDomElement methodElem = doc.createElement( QStringLiteral(
"classificationMethod" ) );
106 methodElem.setAttribute( QStringLiteral(
"id" ),
id() );
109 QDomElement symmetricModeElem = doc.createElement( QStringLiteral(
"symmetricMode" ) );
111 symmetricModeElem.setAttribute( QStringLiteral(
"symmetrypoint" ),
symmetryPoint() );
112 symmetricModeElem.setAttribute( QStringLiteral(
"astride" ), mSymmetryAstride ? 1 : 0 );
113 methodElem.appendChild( symmetricModeElem );
116 QDomElement labelFormatElem = doc.createElement( QStringLiteral(
"labelFormat" ) );
117 labelFormatElem.setAttribute( QStringLiteral(
"format" ),
labelFormat() );
118 labelFormatElem.setAttribute( QStringLiteral(
"labelprecision" ),
labelPrecision() );
120 methodElem.appendChild( labelFormatElem );
123 QDomElement parametersElem = doc.createElement( QStringLiteral(
"parameters" ) );
125 methodElem.appendChild( parametersElem );
128 QDomElement extraElem = doc.createElement( QStringLiteral(
"extraInformation" ) );
130 methodElem.appendChild( extraElem );
138 mSymmetricEnabled = enabled;
140 mSymmetryAstride = astride;
148 mLabelNumberScale = 1.0;
149 mLabelNumberSuffix.clear();
153 mLabelNumberScale /= 10.0;
154 mLabelNumberSuffix.append(
'0' );
160 static const QRegularExpression RE_TRAILING_ZEROES = QRegularExpression(
"[.,]?0*$" );
161 static const QRegularExpression RE_NEGATIVE_ZERO = QRegularExpression(
"^\\-0(?:[.,]0*)?$" );
162 if ( mLabelPrecision > 0 )
164 QString valueStr = QLocale().toString( value,
'f', mLabelPrecision );
165 if ( mLabelTrimTrailingZeroes )
166 valueStr = valueStr.remove( RE_TRAILING_ZEROES );
167 if ( RE_NEGATIVE_ZERO.match( valueStr ).hasMatch() )
168 valueStr = valueStr.mid( 1 );
173 QString valueStr = QLocale().toString( value * mLabelNumberScale,
'f', 0 );
174 if ( valueStr == QLatin1String(
"-0" ) )
176 if ( valueStr != QLatin1String(
"0" ) )
177 valueStr = valueStr + mLabelNumberSuffix;
184 mParameters.append( definition );
191 if ( def->name() == parameterName )
200 mParameterValues = values;
201 for (
auto it = mParameterValues.constBegin(); it != mParameterValues.constEnd(); ++it )
212 if ( expression.isEmpty() )
213 return QList<QgsClassificationRange>();
218 QList<double> values;
229 if ( !ok || values.isEmpty() )
230 return QList<QgsClassificationRange>();
232 auto result = std::minmax_element( values.begin(), values.end() );
233 minimum = *result.first;
234 maximum = *result.second;
241 minimum = minVal.toDouble();
242 maximum = maxVal.toDouble();
246 QList<double> breaks = calculateBreaks( minimum, maximum, values, nclasses );
247 breaks.insert( 0, minimum );
249 return breaksToClasses( breaks );
254 auto result = std::minmax_element( values.begin(), values.end() );
255 double minimum = *result.first;
256 double maximum = *result.second;
259 QList<double> breaks = calculateBreaks( minimum, maximum, values, nclasses );
260 breaks.insert( 0, minimum );
262 return breaksToClasses( breaks );
269 QgsDebugError( QStringLiteral(
"The classification method %1 tries to calculate classes without values while they are required." ).arg(
name() ) );
273 QList<double> breaks = calculateBreaks( minimum, maximum, QList<double>(), nclasses );
274 breaks.insert( 0, minimum );
276 return breaksToClasses( breaks );
279QList<QgsClassificationRange> QgsClassificationMethod::breaksToClasses(
const QList<double> &breaks )
const
281 QList<QgsClassificationRange>
classes;
283 for (
int i = 1; i < breaks.count(); i++ )
286 const double lowerValue = breaks.at( i - 1 );
287 const double upperValue = breaks.at( i );
292 else if ( i == breaks.count() - 1 )
295 QString label =
labelForRange( lowerValue, upperValue, pos );
309 if ( breaks.count() < 2 )
312 std::sort( breaks.begin(), breaks.end() );
314 double distBelowSymmetricValue = std::fabs( breaks[0] -
symmetryPoint );
315 double distAboveSymmetricValue = std::fabs( breaks[ breaks.size() - 2 ] -
symmetryPoint ) ;
316 double absMin = std::min( distAboveSymmetricValue, distBelowSymmetricValue );
319 for (
int i = 0; i <= breaks.size() - 2; ++i )
322 if ( std::fabs( breaks.at( i ) -
symmetryPoint ) >= ( absMin - std::fabs( breaks[0] - breaks[1] ) / 100. ) )
324 breaks.removeAt( i );
344 const QString lowerLabel = valueToLabel( lowerValue );
345 const QString upperLabel = valueToLabel( upperValue );
347 return labelFormat().replace( QLatin1String(
"%1" ), lowerLabel ).replace( QLatin1String(
"%2" ), upperLabel );
static QgsClassificationMethodRegistry * classificationMethodRegistry()
Returns the application's classification methods registry, used in graduated renderer.
QgsClassificationMethod * method(const QString &id)
Returns a new instance of the method for the given id.
QgsClassificationMethod is an abstract class for implementations of classification methods.
double symmetryPoint() const
Returns the symmetry point for symmetric mode.
bool symmetricModeEnabled() const
Returns if the symmetric mode is enabled.
static void makeBreaksSymmetric(QList< double > &breaks, double symmetryPoint, bool astride)
Remove the breaks that are above the existing opposite sign classes to keep colors symmetrically bala...
QList< QgsClassificationRange > classes(const QgsVectorLayer *layer, const QString &expression, int nclasses)
This will calculate the classes for a given layer to define the classes.
int labelPrecision() const
Returns the precision for the formatting of the labels.
void setLabelTrimTrailingZeroes(bool trimTrailingZeroes)
Defines if the trailing 0 are trimmed in the label.
QVariantMap parameterValues() const
Returns the values of the processing parameters.
void setSymmetricMode(bool enabled, double symmetryPoint=0, bool symmetryAstride=false)
Defines if the symmetric mode is enables and configures its parameters.
ClassPosition
Defines the class position.
@ LowerBound
The class is at the lower bound.
@ UpperBound
The class is at the upper bound.
@ Inner
The class is not at a bound.
static const int MIN_PRECISION
void addParameter(QgsProcessingParameterDefinition *definition)
Add a parameter to the method.
virtual void readXml(const QDomElement &element, const QgsReadWriteContext &context)
Reads extra information to apply it to the method.
static QList< double > rangesToBreaks(const QList< QgsClassificationRange > &classes)
Transforms a list of classes to a list of breaks.
virtual void writeXml(QDomElement &element, const QgsReadWriteContext &context) const
Writes extra information about the method.
QString labelFormat() const
Returns the format of the label for the classes.
virtual bool valuesRequired() const
Returns if the method requires values to calculate the classes If not, bounds are sufficient.
void setParameterValues(const QVariantMap &values)
Defines the values of the additional parameters.
virtual QString name() const =0
The readable and translate name of the method.
QgsClassificationMethod(MethodProperties properties=NoFlag, int codeComplexity=1)
Creates a classification method.
void setLabelFormat(const QString &format)
Defines the format of the labels for the classes, using %1 and %2 for the bounds.
static QgsClassificationMethod * create(const QDomElement &element, const QgsReadWriteContext &context)
Reads the DOM element and return a new classification method from it.
const QgsProcessingParameterDefinition * parameterDefinition(const QString ¶meterName) const
Returns the parameter from its name.
QString formatNumber(double value) const
Format the number according to label properties.
static const int MAX_PRECISION
bool labelTrimTrailingZeroes() const
Returns if the trailing 0 are trimmed in the label.
void setLabelPrecision(int labelPrecision)
Defines the precision for the formatting of the labels.
void copyBase(QgsClassificationMethod *c) const
Copy the parameters (shall be used in clone implementation)
virtual QString labelForRange(double lowerValue, double upperValue, ClassPosition position=Inner) const
Returns the label for a range.
virtual ~QgsClassificationMethod()
QDomElement save(QDomDocument &doc, const QgsReadWriteContext &context) const
Saves the method to a DOM element and return it.
QgsClassificationRange contains the information about a classification range.
int indexFromName(const QString &fieldName) const
Gets the field index from the field name.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
Base class for the definition of processing parameters.
The class is used as a container of context for various read/write operations on other objects.
double upperValue() const
Returns the upper bound of the range.
double lowerValue() const
Returns the lower bound of the range.
static QList< double > getDoubleValues(const QgsVectorLayer *layer, const QString &fieldOrExpression, bool &ok, bool selectedOnly=false, int *nullCount=nullptr, QgsFeedback *feedback=nullptr)
Fetches all double values from a specified field name or expression.
Represents a vector layer which manages a vector based data sets.
QgsFields fields() const FINAL
Returns the list of fields of this layer.
void minimumAndMaximumValue(int index, QVariant &minimum, QVariant &maximum) const
Calculates both the minimum and maximum value for an attribute column.
static QDomElement writeVariant(const QVariant &value, QDomDocument &doc)
Write a QVariant to a QDomElement.
static QVariant readVariant(const QDomElement &element)
Read a QVariant from a QDomElement.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
#define QgsDebugError(str)