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