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