QGIS API Documentation 3.41.0-Master (57ec4277f5e)
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#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 std::unique_ptr<QgsProcessingParameterExtent> 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 std::unique_ptr<QgsProcessingParameterNumber> 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 std::unique_ptr<QgsProcessingParameterCrs> 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 addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Calculated" ) ) );
78}
79
80bool QgsRasterCalculatorAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
81{
82 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, QStringLiteral( "LAYERS" ), context );
83
84 for ( const QgsMapLayer *layer : std::as_const( layers ) )
85 {
86 QgsMapLayer *clonedLayer { layer->clone() };
87 clonedLayer->moveToThread( nullptr );
88 mLayers << clonedLayer;
89 }
90
91 if ( mLayers.isEmpty() )
92 {
93 feedback->reportError( QObject::tr( "No layers selected" ), false );
94 return false;
95 }
96
97 return true;
98}
99
100
101QVariantMap QgsRasterCalculatorAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
102{
103 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
104 {
105 layer->moveToThread( QThread::currentThread() );
106 }
107
109 if ( parameters.value( QStringLiteral( "CRS" ) ).isValid() )
110 {
111 crs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context );
112 }
113 else
114 {
115 crs = mLayers.at( 0 )->crs();
116 }
117
118 QgsRectangle bbox;
119 if ( parameters.value( QStringLiteral( "EXTENT" ) ).isValid() )
120 {
121 bbox = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, crs );
122 }
123 else
124 {
125 bbox = QgsProcessingUtils::combineLayerExtents( mLayers, crs, context );
126 }
127
128 double minCellSize = 1e9;
129
130 QVector<QgsRasterCalculatorEntry> entries;
131 for ( QgsMapLayer *layer : mLayers )
132 {
133 QgsRasterLayer *rLayer = qobject_cast<QgsRasterLayer *>( layer );
134 if ( !rLayer )
135 {
136 continue;
137 }
138
139 const int nBands = rLayer->dataProvider()->bandCount();
140 for ( int i = 0; i < nBands; ++i )
141 {
143 entry.ref = QStringLiteral( "%1@%2" ).arg( rLayer->name() ).arg( i + 1 );
144 entry.raster = rLayer;
145 entry.bandNumber = i + 1;
146 entries << entry;
147 }
148
149 QgsRectangle ext = rLayer->extent();
150 if ( rLayer->crs() != crs )
151 {
152 QgsCoordinateTransform ct( rLayer->crs(), crs, context.transformContext() );
153 ext = ct.transformBoundingBox( ext );
154 }
155
156 double cellSize = ( ext.xMaximum() - ext.xMinimum() ) / rLayer->width();
157 if ( cellSize < minCellSize )
158 {
159 minCellSize = cellSize;
160 }
161 }
162
163 double cellSize = parameterAsDouble( parameters, QStringLiteral( "CELL_SIZE" ), context );
164 if ( cellSize == 0 )
165 {
166 cellSize = minCellSize;
167 }
168
169 const QString expression = parameterAsExpression( parameters, QStringLiteral( "EXPRESSION" ), context );
170 const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
171 const QFileInfo fi( outputFile );
172 const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
173
174 double width = std::round( ( bbox.xMaximum() - bbox.xMinimum() ) / cellSize );
175 double height = std::round( ( bbox.yMaximum() - bbox.yMinimum() ) / cellSize );
176
177 QgsRasterCalculator calc( expression, outputFile, outputFormat, bbox, crs, width, height, entries, context.transformContext() );
178 QgsRasterCalculator::Result result = calc.processCalculation( feedback );
179 qDeleteAll( mLayers );
180 mLayers.clear();
181 switch ( result )
182 {
184 throw QgsProcessingException( QObject::tr( "Error creating output file." ) );
186 throw QgsProcessingException( QObject::tr( "Error reading input layer." ) );
188 throw QgsProcessingException( QObject::tr( "Error parsing formula." ) );
190 throw QgsProcessingException( QObject::tr( "Error allocating memory for result." ) );
192 throw QgsProcessingException( QObject::tr( "Invalid band number for input." ) );
194 throw QgsProcessingException( QObject::tr( "Error occurred while performing calculation." ) );
195 default:
196 break;
197 }
198
199 QVariantMap outputs;
200 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
201 return outputs;
202}
203
204Qgis::ProcessingAlgorithmFlags QgsRasterCalculatorModelerAlgorithm::flags() const
205{
207}
208
209QString QgsRasterCalculatorModelerAlgorithm::name() const
210{
211 return QStringLiteral( "modelerrastercalc" );
212}
213
214QString QgsRasterCalculatorModelerAlgorithm::displayName() const
215{
216 return QObject::tr( "Raster calculator" );
217}
218
219QStringList QgsRasterCalculatorModelerAlgorithm::tags() const
220{
221 return QObject::tr( "raster,calculator" ).split( ',' );
222}
223
224QString QgsRasterCalculatorModelerAlgorithm::group() const
225{
226 return QObject::tr( "Raster analysis" );
227}
228
229QString QgsRasterCalculatorModelerAlgorithm::groupId() const
230{
231 return QStringLiteral( "rasteranalysis" );
232}
233
234QgsRasterCalculatorModelerAlgorithm *QgsRasterCalculatorModelerAlgorithm::createInstance() const
235{
236 return new QgsRasterCalculatorModelerAlgorithm();
237}
238
239QVariantMap QgsRasterCalculatorModelerAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
240{
241 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
242 {
243 layer->moveToThread( QThread::currentThread() );
244 }
245
247 if ( parameters.value( QStringLiteral( "CRS" ) ).isValid() )
248 {
249 crs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context );
250 }
251 else
252 {
253 crs = mLayers.at( 0 )->crs();
254 }
255
256 QgsRectangle bbox;
257 if ( parameters.value( QStringLiteral( "EXTENT" ) ).isValid() )
258 {
259 bbox = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, crs );
260 }
261 else
262 {
263 bbox = QgsProcessingUtils::combineLayerExtents( mLayers, crs, context );
264 }
265
266 double minCellSize = 1e9;
267
268 QVector<QgsRasterCalculatorEntry> entries;
269 int n = 0;
270 for ( QgsMapLayer *layer : mLayers )
271 {
272 QgsRasterLayer *rLayer = qobject_cast<QgsRasterLayer *>( layer );
273 if ( !rLayer )
274 {
275 continue;
276 }
277
278 n++;
279 const int nBands = rLayer->dataProvider()->bandCount();
280 for ( int i = 0; i < nBands; ++i )
281 {
283 entry.ref = QStringLiteral( "%1@%2" ).arg( indexToName( n ) ).arg( i + 1 );
284 entry.raster = rLayer;
285 entry.bandNumber = i + 1;
286 entries << entry;
287 }
288
289 QgsRectangle ext = rLayer->extent();
290 if ( rLayer->crs() != crs )
291 {
292 QgsCoordinateTransform ct( rLayer->crs(), crs, context.transformContext() );
293 ext = ct.transformBoundingBox( ext );
294 }
295
296 double cellSize = ( ext.xMaximum() - ext.xMinimum() ) / rLayer->width();
297 if ( cellSize < minCellSize )
298 {
299 minCellSize = cellSize;
300 }
301 }
302
303 double cellSize = parameterAsDouble( parameters, QStringLiteral( "CELL_SIZE" ), context );
304 if ( cellSize == 0 )
305 {
306 cellSize = minCellSize;
307 }
308
309 const QString expression = parameterAsExpression( parameters, QStringLiteral( "EXPRESSION" ), context );
310 const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
311 const QFileInfo fi( outputFile );
312 const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
313
314 double width = std::round( ( bbox.xMaximum() - bbox.xMinimum() ) / cellSize );
315 double height = std::round( ( bbox.yMaximum() - bbox.yMinimum() ) / cellSize );
316
317 QgsRasterCalculator calc( expression, outputFile, outputFormat, bbox, crs, width, height, entries, context.transformContext() );
318 QgsRasterCalculator::Result result = calc.processCalculation( feedback );
319 qDeleteAll( mLayers );
320 mLayers.clear();
321 switch ( result )
322 {
324 throw QgsProcessingException( QObject::tr( "Error creating output file." ) );
326 throw QgsProcessingException( QObject::tr( "Error reading input layer." ) );
328 throw QgsProcessingException( QObject::tr( "Error parsing formula." ) );
330 throw QgsProcessingException( QObject::tr( "Error allocating memory for result." ) );
332 throw QgsProcessingException( QObject::tr( "Invalid band number for input." ) );
334 throw QgsProcessingException( QObject::tr( "Error occurred while performing calculation." ) );
335 default:
336 break;
337 }
338
339 QVariantMap outputs;
340 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
341 return outputs;
342}
343
344QString QgsRasterCalculatorModelerAlgorithm::indexToName( int index ) const
345{
346 QString name;
347 int div = index;
348 int mod = 0;
349
350 while ( div > 0 )
351 {
352 mod = ( div - 1 ) % 26;
353 name = static_cast<char>( 65 + mod ) + name;
354 div = ( int ) ( ( div - mod ) / 26 );
355 }
356 return name;
357}
358
@ RasterCalculator
Raster calculator expression.
QFlags< ProcessingAlgorithmFlag > ProcessingAlgorithmFlags
Flags indicating how and when an algorithm operates and should be exposed to users.
Definition qgis.h:3410
@ HideFromToolbox
Algorithm should be hidden from the toolbox.
@ HideFromModeler
Algorithm should be hidden from the modeler.
This class represents a coordinate reference system (CRS).
Class for doing transforms between two map coordinate systems.
Base class for all map layer types.
Definition qgsmaplayer.h:76
QString name
Definition qgsmaplayer.h:80
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:83
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