QGIS API Documentation 3.43.0-Master (c67cf405802)
qgsalgorithmrastercalculator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmrastercalculator.cpp
3 ---------------------
4 begin : July 2023
5 copyright : (C) 2023 by Alexander Bruy
6 email : alexander dot bruy 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 "qgsrastercalculator.h"
21
23
24Qgis::ProcessingAlgorithmFlags QgsRasterCalculatorAlgorithm::flags() const
25{
27}
28
29QString QgsRasterCalculatorAlgorithm::name() const
30{
31 return QStringLiteral( "rastercalc" );
32}
33
34QString QgsRasterCalculatorAlgorithm::displayName() const
35{
36 return QObject::tr( "Raster calculator" );
37}
38
39QStringList QgsRasterCalculatorAlgorithm::tags() const
40{
41 return QObject::tr( "raster,calculator" ).split( ',' );
42}
43
44QString QgsRasterCalculatorAlgorithm::group() const
45{
46 return QObject::tr( "Raster analysis" );
47}
48
49QString QgsRasterCalculatorAlgorithm::groupId() const
50{
51 return QStringLiteral( "rasteranalysis" );
52}
53
54QString QgsRasterCalculatorAlgorithm::shortHelpString() const
55{
56 return QObject::tr( "Performing algebraic operations using raster layers." );
57}
58
59QgsRasterCalculatorAlgorithm *QgsRasterCalculatorAlgorithm::createInstance() const
60{
61 return new QgsRasterCalculatorAlgorithm();
62}
63
64void QgsRasterCalculatorAlgorithm::initAlgorithm( const QVariantMap & )
65{
66 addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "LAYERS" ), QObject::tr( "Input layers" ), Qgis::ProcessingSourceType::Raster ) );
67 addParameter( new QgsProcessingParameterExpression( QStringLiteral( "EXPRESSION" ), QObject::tr( "Expression" ), QVariant(), QStringLiteral( "LAYERS" ), false, Qgis::ExpressionType::RasterCalculator ) );
68 auto extentParam = std::make_unique<QgsProcessingParameterExtent>( QStringLiteral( "EXTENT" ), QObject::tr( "Output extent" ), QVariant(), true );
69 extentParam->setHelp( QObject::tr( "Extent of the output layer. If not specified, the extent will be the overall extent of all input layers" ) );
70 addParameter( extentParam.release() );
71 auto cellSizeParam = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "CELL_SIZE" ), QObject::tr( "Output cell size (leave empty to set automatically)" ), Qgis::ProcessingNumberParameterType::Double, QVariant(), true, 0.0 );
72 cellSizeParam->setHelp( QObject::tr( "Cell size of the output layer. If not specified, the smallest cell size from the input layers will be used" ) );
73 addParameter( cellSizeParam.release() );
74 auto crsParam = std::make_unique<QgsProcessingParameterCrs>( QStringLiteral( "CRS" ), QObject::tr( "Output CRS" ), QVariant(), true );
75 crsParam->setHelp( QObject::tr( "CRS of the output layer. If not specified, the CRS of the first input layer will be used" ) );
76 addParameter( crsParam.release() );
77
78 auto createOptsParam = std::make_unique<QgsProcessingParameterString>( QStringLiteral( "CREATE_OPTIONS" ), QObject::tr( "Creation options" ), QVariant(), false, true );
79 createOptsParam->setMetadata( QVariantMap( { { QStringLiteral( "widget_wrapper" ), QVariantMap( { { QStringLiteral( "widget_type" ), QStringLiteral( "rasteroptions" ) } } ) } } ) );
80 createOptsParam->setFlags( createOptsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
81 addParameter( createOptsParam.release() );
82
83 addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Calculated" ) ) );
84}
85
86bool QgsRasterCalculatorAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
87{
88 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );
89
90 for ( const QgsMapLayer *layer : std::as_const( layers ) )
91 {
92 QgsMapLayer *clonedLayer { layer->clone() };
93 clonedLayer->moveToThread( nullptr );
94 mLayers << clonedLayer;
95 }
96
97 if ( mLayers.isEmpty() )
98 {
99 feedback->reportError( QObject::tr( "No layers selected" ), false );
100 return false;
101 }
102
103 return true;
104}
105
106QVariantMap QgsRasterCalculatorAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
107{
108 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
109 {
110 layer->moveToThread( QThread::currentThread() );
111 }
112
114 if ( parameters.value( QStringLiteral( "CRS" ) ).isValid() )
115 {
116 crs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context );
117 }
118 else
119 {
120 crs = mLayers.at( 0 )->crs();
121 }
122
123 QgsRectangle bbox;
124 if ( parameters.value( QStringLiteral( "EXTENT" ) ).isValid() )
125 {
126 bbox = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, crs );
127 }
128 else
129 {
130 bbox = QgsProcessingUtils::combineLayerExtents( mLayers, crs, context );
131 }
132
133 double minCellSize = 1e9;
134
135 QVector<QgsRasterCalculatorEntry> entries;
136 for ( QgsMapLayer *layer : mLayers )
137 {
138 QgsRasterLayer *rLayer = qobject_cast<QgsRasterLayer *>( layer );
139 if ( !rLayer )
140 {
141 continue;
142 }
143
144 const int nBands = rLayer->dataProvider()->bandCount();
145 for ( int i = 0; i < nBands; ++i )
146 {
148 entry.ref = QStringLiteral( "%1@%2" ).arg( rLayer->name() ).arg( i + 1 );
149 entry.raster = rLayer;
150 entry.bandNumber = i + 1;
151 entries << entry;
152 }
153
154 QgsRectangle ext = rLayer->extent();
155 if ( rLayer->crs() != crs )
156 {
157 QgsCoordinateTransform ct( rLayer->crs(), crs, context.transformContext() );
158 ext = ct.transformBoundingBox( ext );
159 }
160
161 double cellSize = ( ext.xMaximum() - ext.xMinimum() ) / rLayer->width();
162 if ( cellSize < minCellSize )
163 {
164 minCellSize = cellSize;
165 }
166 }
167
168 double cellSize = parameterAsDouble( parameters, QStringLiteral( "CELL_SIZE" ), context );
169 if ( cellSize == 0 )
170 {
171 cellSize = minCellSize;
172 }
173
174 const QString createOptions = parameterAsString( parameters, QStringLiteral( "CREATE_OPTIONS" ), context ).trimmed();
175 const QString expression = parameterAsExpression( parameters, QStringLiteral( "EXPRESSION" ), context );
176 const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
177 const QFileInfo fi( outputFile );
178 const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
179
180 double width = std::round( ( bbox.xMaximum() - bbox.xMinimum() ) / cellSize );
181 double height = std::round( ( bbox.yMaximum() - bbox.yMinimum() ) / cellSize );
182
183 QgsRasterCalculator calc( expression, outputFile, outputFormat, bbox, crs, width, height, entries, context.transformContext() );
184 calc.setCreateOptions( createOptions.split( '|', Qt::SplitBehaviorFlags::SkipEmptyParts ) );
185 QgsRasterCalculator::Result result = calc.processCalculation( feedback );
186 qDeleteAll( mLayers );
187 mLayers.clear();
188 switch ( result )
189 {
191 throw QgsProcessingException( QObject::tr( "Error creating output file." ) );
193 throw QgsProcessingException( QObject::tr( "Error reading input layer." ) );
195 throw QgsProcessingException( QObject::tr( "Error parsing formula." ) );
197 throw QgsProcessingException( QObject::tr( "Error allocating memory for result." ) );
199 throw QgsProcessingException( QObject::tr( "Invalid band number for input." ) );
201 throw QgsProcessingException( QObject::tr( "Error occurred while performing calculation." ) );
202 default:
203 break;
204 }
205
206 QVariantMap outputs;
207 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
208 return outputs;
209}
210
211Qgis::ProcessingAlgorithmFlags QgsRasterCalculatorModelerAlgorithm::flags() const
212{
214}
215
216QString QgsRasterCalculatorModelerAlgorithm::name() const
217{
218 return QStringLiteral( "modelerrastercalc" );
219}
220
221QString QgsRasterCalculatorModelerAlgorithm::displayName() const
222{
223 return QObject::tr( "Raster calculator" );
224}
225
226QStringList QgsRasterCalculatorModelerAlgorithm::tags() const
227{
228 return QObject::tr( "raster,calculator" ).split( ',' );
229}
230
231QString QgsRasterCalculatorModelerAlgorithm::group() const
232{
233 return QObject::tr( "Raster analysis" );
234}
235
236QString QgsRasterCalculatorModelerAlgorithm::groupId() const
237{
238 return QStringLiteral( "rasteranalysis" );
239}
240
241QgsRasterCalculatorModelerAlgorithm *QgsRasterCalculatorModelerAlgorithm::createInstance() const
242{
243 return new QgsRasterCalculatorModelerAlgorithm();
244}
245
246QVariantMap QgsRasterCalculatorModelerAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
247{
248 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
249 {
250 layer->moveToThread( QThread::currentThread() );
251 }
252
254 if ( parameters.value( QStringLiteral( "CRS" ) ).isValid() )
255 {
256 crs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context );
257 }
258 else
259 {
260 crs = mLayers.at( 0 )->crs();
261 }
262
263 QgsRectangle bbox;
264 if ( parameters.value( QStringLiteral( "EXTENT" ) ).isValid() )
265 {
266 bbox = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, crs );
267 }
268 else
269 {
270 bbox = QgsProcessingUtils::combineLayerExtents( mLayers, crs, context );
271 }
272
273 double minCellSize = 1e9;
274
275 QVector<QgsRasterCalculatorEntry> entries;
276 int n = 0;
277 for ( QgsMapLayer *layer : mLayers )
278 {
279 QgsRasterLayer *rLayer = qobject_cast<QgsRasterLayer *>( layer );
280 if ( !rLayer )
281 {
282 continue;
283 }
284
285 n++;
286 const int nBands = rLayer->dataProvider()->bandCount();
287 for ( int i = 0; i < nBands; ++i )
288 {
290 entry.ref = QStringLiteral( "%1@%2" ).arg( indexToName( n ) ).arg( i + 1 );
291 entry.raster = rLayer;
292 entry.bandNumber = i + 1;
293 entries << entry;
294 }
295
296 QgsRectangle ext = rLayer->extent();
297 if ( rLayer->crs() != crs )
298 {
299 QgsCoordinateTransform ct( rLayer->crs(), crs, context.transformContext() );
300 ext = ct.transformBoundingBox( ext );
301 }
302
303 double cellSize = ( ext.xMaximum() - ext.xMinimum() ) / rLayer->width();
304 if ( cellSize < minCellSize )
305 {
306 minCellSize = cellSize;
307 }
308 }
309
310 double cellSize = parameterAsDouble( parameters, QStringLiteral( "CELL_SIZE" ), context );
311 if ( cellSize == 0 )
312 {
313 cellSize = minCellSize;
314 }
315
316 const QString expression = parameterAsExpression( parameters, QStringLiteral( "EXPRESSION" ), context );
317 const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
318 const QFileInfo fi( outputFile );
319 const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
320
321 double width = std::round( ( bbox.xMaximum() - bbox.xMinimum() ) / cellSize );
322 double height = std::round( ( bbox.yMaximum() - bbox.yMinimum() ) / cellSize );
323
324 QgsRasterCalculator calc( expression, outputFile, outputFormat, bbox, crs, width, height, entries, context.transformContext() );
325 QgsRasterCalculator::Result result = calc.processCalculation( feedback );
326 qDeleteAll( mLayers );
327 mLayers.clear();
328 switch ( result )
329 {
331 throw QgsProcessingException( QObject::tr( "Error creating output file." ) );
333 throw QgsProcessingException( QObject::tr( "Error reading input layer." ) );
335 throw QgsProcessingException( QObject::tr( "Error parsing formula." ) );
337 throw QgsProcessingException( QObject::tr( "Error allocating memory for result." ) );
339 throw QgsProcessingException( QObject::tr( "Invalid band number for input." ) );
341 throw QgsProcessingException( QObject::tr( "Error occurred while performing calculation." ) );
342 default:
343 break;
344 }
345
346 QVariantMap outputs;
347 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
348 return outputs;
349}
350
351QString QgsRasterCalculatorModelerAlgorithm::indexToName( int index ) const
352{
353 QString name;
354 int div = index;
355 int mod = 0;
356
357 while ( div > 0 )
358 {
359 mod = ( div - 1 ) % 26;
360 name = static_cast<char>( 65 + mod ) + name;
361 div = ( int ) ( ( div - mod ) / 26 );
362 }
363 return name;
364}
365
@ RasterCalculator
Raster calculator expression.
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3476
@ HideFromToolbox
Algorithm should be hidden from the toolbox.
@ HideFromModeler
Algorithm should be hidden from the modeler.
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Represents a coordinate reference system (CRS).
Handles coordinate transforms between two coordinate systems.
Base class for all map layer types.
Definition qgsmaplayer.h:77
QString name
Definition qgsmaplayer.h:81
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:84
virtual QgsMapLayer * clone() const =0
Returns a new instance equivalent to this one except for the id which is still unique.
virtual Qgis::ProcessingAlgorithmFlags flags() const
Returns the flags indicating how and when the algorithm operates and should be exposed to users.
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void reportError(const QString &error, bool fatalError=false)
Reports that the algorithm encountered an error while executing.
An expression parameter for processing algorithms.
A parameter for processing algorithms which accepts multiple map layers.
A raster layer destination parameter, for specifying the destination path for a raster layer created ...
static QgsRectangle combineLayerExtents(const QList< QgsMapLayer * > &layers, const QgsCoordinateReferenceSystem &crs, QgsProcessingContext &context)
Combines the extent of several map layers.
Represents an individual raster layer/band number entry within a raster calculation.
QgsRasterLayer * raster
Raster layer associated with entry.
int bandNumber
Band number for entry.
QString ref
Name of entry.
Performs raster layer calculations.
Result
Result of the calculation.
@ InputLayerError
Error reading input layer.
@ ParserError
Error parsing formula.
@ CalculationError
Error occurred while performing calculation.
@ MemoryError
Error allocating memory for result.
@ BandError
Invalid band number for input.
@ CreateOutputError
Error creating output data file.
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
virtual int bandCount() const =0
Gets number of bands.
Represents a raster layer.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
int width() const
Returns the width of the (unclipped) raster.
A rectangle specified with double values.
double xMinimum
double yMinimum
double xMaximum
double yMaximum
const QgsCoordinateReferenceSystem & crs