QGIS API Documentation 3.28.0-Firenze (ed3ad0430f)
qgsalgorithmreclassifybylayer.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmreclassifybylayer.cpp
3 ---------------------
4 begin : June, 2018
5 copyright : (C) 2018 by Nyall Dawson
6 email : nyall dot dawson at gmail dot com
7 ***************************************************************************/
8
9/***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
17
19#include "qgsrasterfilewriter.h"
20#include "qgsreclassifyutils.h"
22#include "qgis.h"
23#include "qgsvariantutils.h"
24
26
27//
28// QgsReclassifyAlgorithmBase
29//
30
31
32QString QgsReclassifyAlgorithmBase::group() const
33{
34 return QObject::tr( "Raster analysis" );
35}
36
37QString QgsReclassifyAlgorithmBase::groupId() const
38{
39 return QStringLiteral( "rasteranalysis" );
40}
41
42void QgsReclassifyAlgorithmBase::initAlgorithm( const QVariantMap & )
43{
44 addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT_RASTER" ),
45 QObject::tr( "Raster layer" ) ) );
46 addParameter( new QgsProcessingParameterBand( QStringLiteral( "RASTER_BAND" ),
47 QObject::tr( "Band number" ), 1, QStringLiteral( "INPUT_RASTER" ) ) );
48
49 addAlgorithmParams();
50
51 std::unique_ptr< QgsProcessingParameterNumber > noDataValueParam = std::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "NO_DATA" ),
52 QObject::tr( "Output no data value" ), QgsProcessingParameterNumber::Double, -9999 );
53 noDataValueParam->setFlags( QgsProcessingParameterDefinition::FlagAdvanced );
54 addParameter( noDataValueParam.release() );
55
56 std::unique_ptr< QgsProcessingParameterEnum > boundsHandling = std::make_unique< QgsProcessingParameterEnum >( QStringLiteral( "RANGE_BOUNDARIES" ),
57 QObject::tr( "Range boundaries" ), QStringList() << QObject::tr( "min < value <= max" )
58 << QObject::tr( "min <= value < max" )
59 << QObject::tr( "min <= value <= max" )
60 << QObject::tr( "min < value < max" ),
61 false, 0 );
62 boundsHandling->setFlags( QgsProcessingParameterDefinition::FlagAdvanced );
63 addParameter( boundsHandling.release() );
64
65 std::unique_ptr< QgsProcessingParameterBoolean > missingValuesParam = std::make_unique< QgsProcessingParameterBoolean >( QStringLiteral( "NODATA_FOR_MISSING" ),
66 QObject::tr( "Use no data when no range matches value" ), false, false );
67 missingValuesParam->setFlags( QgsProcessingParameterDefinition::FlagAdvanced );
68 addParameter( missingValuesParam.release() );
69
70 std::unique_ptr< QgsProcessingParameterDefinition > typeChoice = QgsRasterAnalysisUtils::createRasterTypeParameter( QStringLiteral( "DATA_TYPE" ), QObject::tr( "Output data type" ), Qgis::DataType::Float32 );
72 addParameter( typeChoice.release() );
73
74 addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Reclassified raster" ) ) );
75}
76
77bool QgsReclassifyAlgorithmBase::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
78{
79 mDataType = QgsRasterAnalysisUtils::rasterTypeChoiceToDataType( parameterAsEnum( parameters, QStringLiteral( "DATA_TYPE" ), context ) );
80 QgsRasterLayer *layer = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT_RASTER" ), context );
81
82 if ( !layer )
83 throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT_RASTER" ) ) );
84
85 mBand = parameterAsInt( parameters, QStringLiteral( "RASTER_BAND" ), context );
86 if ( mBand < 1 || mBand > layer->bandCount() )
87 throw QgsProcessingException( QObject::tr( "Invalid band number for RASTER_BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand )
88 .arg( layer->bandCount() ) );
89
90 mInterface.reset( layer->dataProvider()->clone() );
91 mExtent = layer->extent();
92 mCrs = layer->crs();
93 mRasterUnitsPerPixelX = std::abs( layer->rasterUnitsPerPixelX() );
94 mRasterUnitsPerPixelY = std::abs( layer->rasterUnitsPerPixelY() );
95 mNbCellsXProvider = mInterface->xSize();
96 mNbCellsYProvider = mInterface->ySize();
97
98 mNoDataValue = parameterAsDouble( parameters, QStringLiteral( "NO_DATA" ), context );
99 mUseNoDataForMissingValues = parameterAsBoolean( parameters, QStringLiteral( "NODATA_FOR_MISSING" ), context );
100
101 const int boundsType = parameterAsEnum( parameters, QStringLiteral( "RANGE_BOUNDARIES" ), context );
102 switch ( boundsType )
103 {
104 case 0:
105 mBoundsType = QgsReclassifyUtils::RasterClass::IncludeMax;
106 break;
107
108 case 1:
109 mBoundsType = QgsReclassifyUtils::RasterClass::IncludeMin;
110 break;
111
112 case 2:
113 mBoundsType = QgsReclassifyUtils::RasterClass::IncludeMinAndMax;
114 break;
115
116 case 3:
117 mBoundsType = QgsReclassifyUtils::RasterClass::Exclusive;
118 break;
119 }
120
121 return _prepareAlgorithm( parameters, context, feedback );
122}
123
124QVariantMap QgsReclassifyAlgorithmBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
125{
126 const QVector< QgsReclassifyUtils::RasterClass > classes = createClasses( mBoundsType, parameters, context, feedback );
127
128 QgsReclassifyUtils::reportClasses( classes, feedback );
129 QgsReclassifyUtils::checkForOverlaps( classes, feedback );
130
131 const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
132 const QFileInfo fi( outputFile );
133 const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
134
135 std::unique_ptr< QgsRasterFileWriter > writer = std::make_unique< QgsRasterFileWriter >( outputFile );
136 writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
137 writer->setOutputFormat( outputFormat );
138 std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( mDataType, mNbCellsXProvider, mNbCellsYProvider, mExtent, mCrs ) );
139 if ( !provider )
140 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
141 if ( !provider->isValid() )
142 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
143
144 provider->setNoDataValue( 1, mNoDataValue );
145
146 QgsReclassifyUtils::reclassify( classes, mInterface.get(), mBand, mExtent, mNbCellsXProvider, mNbCellsYProvider, provider.get(), mNoDataValue, mUseNoDataForMissingValues,
147 feedback );
148
149 QVariantMap outputs;
150 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
151 return outputs;
152}
153
154
155//
156// QgsReclassifyByLayerAlgorithm
157//
158
159QString QgsReclassifyByLayerAlgorithm::name() const
160{
161 return QStringLiteral( "reclassifybylayer" );
162}
163
164QString QgsReclassifyByLayerAlgorithm::displayName() const
165{
166 return QObject::tr( "Reclassify by layer" );
167}
168
169QStringList QgsReclassifyByLayerAlgorithm::tags() const
170{
171 return QObject::tr( "raster,reclassify,classes,calculator" ).split( ',' );
172}
173
174QString QgsReclassifyByLayerAlgorithm::shortHelpString() const
175{
176 return QObject::tr( "This algorithm reclassifies a raster band by assigning new class values based on the ranges specified in a vector table." );
177}
178
179QgsReclassifyByLayerAlgorithm *QgsReclassifyByLayerAlgorithm::createInstance() const
180{
181 return new QgsReclassifyByLayerAlgorithm();
182}
183
184void QgsReclassifyByLayerAlgorithm::addAlgorithmParams()
185{
186 addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT_TABLE" ),
187 QObject::tr( "Layer containing class breaks" ), QList< int >() << QgsProcessing::TypeVector ) );
188 addParameter( new QgsProcessingParameterField( QStringLiteral( "MIN_FIELD" ),
189 QObject::tr( "Minimum class value field" ), QVariant(), QStringLiteral( "INPUT_TABLE" ), QgsProcessingParameterField::Numeric ) );
190 addParameter( new QgsProcessingParameterField( QStringLiteral( "MAX_FIELD" ),
191 QObject::tr( "Maximum class value field" ), QVariant(), QStringLiteral( "INPUT_TABLE" ), QgsProcessingParameterField::Numeric ) );
192 addParameter( new QgsProcessingParameterField( QStringLiteral( "VALUE_FIELD" ),
193 QObject::tr( "Output value field" ), QVariant(), QStringLiteral( "INPUT_TABLE" ), QgsProcessingParameterField::Numeric ) );
194}
195
196bool QgsReclassifyByLayerAlgorithm::_prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
197{
198 std::unique_ptr< QgsFeatureSource >tableSource( parameterAsSource( parameters, QStringLiteral( "INPUT_TABLE" ), context ) );
199 if ( !tableSource )
200 throw QgsProcessingException( invalidSourceError( parameters, QStringLiteral( "INPUT_TABLE" ) ) );
201
202 const QString fieldMin = parameterAsString( parameters, QStringLiteral( "MIN_FIELD" ), context );
203 mMinFieldIdx = tableSource->fields().lookupField( fieldMin );
204 if ( mMinFieldIdx < 0 )
205 throw QgsProcessingException( QObject::tr( "Invalid field specified for MIN_FIELD: %1" ).arg( fieldMin ) );
206 const QString fieldMax = parameterAsString( parameters, QStringLiteral( "MAX_FIELD" ), context );
207 mMaxFieldIdx = tableSource->fields().lookupField( fieldMax );
208 if ( mMaxFieldIdx < 0 )
209 throw QgsProcessingException( QObject::tr( "Invalid field specified for MAX_FIELD: %1" ).arg( fieldMax ) );
210 const QString fieldValue = parameterAsString( parameters, QStringLiteral( "VALUE_FIELD" ), context );
211 mValueFieldIdx = tableSource->fields().lookupField( fieldValue );
212 if ( mValueFieldIdx < 0 )
213 throw QgsProcessingException( QObject::tr( "Invalid field specified for VALUE_FIELD: %1" ).arg( fieldValue ) );
214
215 QgsFeatureRequest request;
217 request.setSubsetOfAttributes( QgsAttributeList() << mMinFieldIdx << mMaxFieldIdx << mValueFieldIdx );
218 mTableIterator = tableSource->getFeatures( request );
219
220 return true;
221}
222
223QVector<QgsReclassifyUtils::RasterClass> QgsReclassifyByLayerAlgorithm::createClasses( QgsRasterRange::BoundsType boundsType, const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
224{
225 QVector< QgsReclassifyUtils::RasterClass > classes;
226 QgsFeature f;
227 while ( mTableIterator.nextFeature( f ) )
228 {
229 bool ok = false;
230
231 // null values map to nan, which corresponds to a range extended to +/- infinity....
232 const QVariant minVariant = f.attribute( mMinFieldIdx );
233 double minValue;
234 if ( QgsVariantUtils::isNull( minVariant ) || minVariant.toString().isEmpty() )
235 {
236 minValue = std::numeric_limits<double>::quiet_NaN();
237 }
238 else
239 {
240 minValue = minVariant.toDouble( &ok );
241 if ( !ok )
242 throw QgsProcessingException( QObject::tr( "Invalid value for minimum: %1" ).arg( minVariant.toString() ) );
243 }
244 const QVariant maxVariant = f.attribute( mMaxFieldIdx );
245 double maxValue;
246 if ( QgsVariantUtils::isNull( maxVariant ) || maxVariant.toString().isEmpty() )
247 {
248 maxValue = std::numeric_limits<double>::quiet_NaN();
249 ok = true;
250 }
251 else
252 {
253 maxValue = maxVariant.toDouble( &ok );
254 if ( !ok )
255 throw QgsProcessingException( QObject::tr( "Invalid value for maximum: %1" ).arg( maxVariant.toString() ) );
256 }
257
258 const double value = f.attribute( mValueFieldIdx ).toDouble( &ok );
259 if ( !ok )
260 throw QgsProcessingException( QObject::tr( "Invalid output value: %1" ).arg( f.attribute( mValueFieldIdx ).toString() ) );
261
262 classes << QgsReclassifyUtils::RasterClass( minValue, maxValue, boundsType, value );
263 }
264 return classes;
265}
266
267
268//
269// QgsReclassifyByTableAlgorithm
270//
271
272QString QgsReclassifyByTableAlgorithm::name() const
273{
274 return QStringLiteral( "reclassifybytable" );
275}
276
277QString QgsReclassifyByTableAlgorithm::displayName() const
278{
279 return QObject::tr( "Reclassify by table" );
280}
281
282QStringList QgsReclassifyByTableAlgorithm::tags() const
283{
284 return QObject::tr( "raster,reclassify,classes,calculator" ).split( ',' );
285}
286
287QString QgsReclassifyByTableAlgorithm::shortHelpString() const
288{
289 return QObject::tr( "This algorithm reclassifies a raster band by assigning new class values based on the ranges specified in a fixed table." );
290}
291
292QgsReclassifyByTableAlgorithm *QgsReclassifyByTableAlgorithm::createInstance() const
293{
294 return new QgsReclassifyByTableAlgorithm();
295}
296
297void QgsReclassifyByTableAlgorithm::addAlgorithmParams()
298{
299 addParameter( new QgsProcessingParameterMatrix( QStringLiteral( "TABLE" ),
300 QObject::tr( "Reclassification table" ),
301 1, false, QStringList() << QObject::tr( "Minimum" )
302 << QObject::tr( "Maximum" )
303 << QObject::tr( "Value" ) ) );
304}
305
306bool QgsReclassifyByTableAlgorithm::_prepareAlgorithm( const QVariantMap &, QgsProcessingContext &, QgsProcessingFeedback * )
307{
308 return true;
309}
310
311QVector<QgsReclassifyUtils::RasterClass> QgsReclassifyByTableAlgorithm::createClasses( QgsReclassifyUtils::RasterClass::BoundsType boundsType, const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
312{
313 const QVariantList table = parameterAsMatrix( parameters, QStringLiteral( "TABLE" ), context );
314 if ( table.count() % 3 != 0 )
315 throw QgsProcessingException( QObject::tr( "Invalid value for TABLE: list must contain a multiple of 3 elements (found %1)" ).arg( table.count() ) );
316
317 const int rows = table.count() / 3;
318 QVector< QgsReclassifyUtils::RasterClass > classes;
319 classes.reserve( rows );
320 for ( int row = 0; row < rows; ++row )
321 {
322 bool ok = false;
323
324 // null values map to nan, which corresponds to a range extended to +/- infinity....
325 const QVariant minVariant = table.at( row * 3 );
326 double minValue;
327 if ( QgsVariantUtils::isNull( minVariant ) || minVariant.toString().isEmpty() )
328 {
329 minValue = std::numeric_limits<double>::quiet_NaN();
330 }
331 else
332 {
333 minValue = minVariant.toDouble( &ok );
334 if ( !ok )
335 throw QgsProcessingException( QObject::tr( "Invalid value for minimum: %1" ).arg( table.at( row * 3 ).toString() ) );
336 }
337 const QVariant maxVariant = table.at( row * 3 + 1 );
338 double maxValue;
339 if ( QgsVariantUtils::isNull( maxVariant ) || maxVariant.toString().isEmpty() )
340 {
341 maxValue = std::numeric_limits<double>::quiet_NaN();
342 ok = true;
343 }
344 else
345 {
346 maxValue = maxVariant.toDouble( &ok );
347 if ( !ok )
348 throw QgsProcessingException( QObject::tr( "Invalid value for maximum: %1" ).arg( table.at( row * 3 + 1 ).toString() ) );
349 }
350
351 const double value = table.at( row * 3 + 2 ).toDouble( &ok );
352 if ( !ok )
353 throw QgsProcessingException( QObject::tr( "Invalid output value: %1" ).arg( table.at( row * 3 + 2 ).toString() ) );
354
355 classes << QgsReclassifyUtils::RasterClass( minValue, maxValue, boundsType, value );
356 }
357 return classes;
358}
359
361
362
@ Float32
Thirty two bit floating point (float)
This class wraps a request for features to a vector layer (or directly its vector data provider).
QgsFeatureRequest & setFlags(QgsFeatureRequest::Flags flags)
Sets flags that affect how features will be fetched.
QgsFeatureRequest & setSubsetOfAttributes(const QgsAttributeList &attrs)
Set a subset of attributes that will be fetched.
@ NoGeometry
Geometry is not required. It may still be returned if e.g. required for a filter condition.
The feature class encapsulates a single feature including its unique ID, geometry and a list of field...
Definition: qgsfeature.h:56
QVariant attribute(const QString &name) const
Lookup attribute value by attribute name.
Definition: qgsfeature.cpp:338
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:79
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
Base class for providing feedback from a processing algorithm.
A raster band parameter for Processing algorithms.
@ FlagAdvanced
Parameter is an advanced parameter which should be hidden from users by default.
An input feature source (such as vector layers) parameter for processing algorithms.
A vector layer or feature source field parameter for processing algorithms.
@ Numeric
Accepts numeric fields.
A table (matrix) parameter for processing algorithms.
A raster layer destination parameter, for specifying the destination path for a raster layer created ...
A raster layer parameter for processing algorithms.
@ TypeVector
Tables (i.e. vector layers with or without geometry). When used for a sink this indicates the sink ha...
Definition: qgsprocessing.h:54
QgsRasterDataProvider * clone() const override=0
Clone itself, create deep copy.
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
Represents a raster layer.
int bandCount() const
Returns the number of bands in this layer.
double rasterUnitsPerPixelX() const
Returns the number of raster units per each raster pixel in X axis.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
double rasterUnitsPerPixelY() const
Returns the number of raster units per each raster pixel in Y axis.
BoundsType
Handling for min and max bounds.
static bool isNull(const QVariant &variant)
Returns true if the specified variant should be considered a NULL value.
QList< int > QgsAttributeList
Definition: qgsfield.h:26