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