QGIS API Documentation 3.99.0-Master (752b475928d)
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 QString outputFormat = parameterAsOutputRasterFormat( parameters, QStringLiteral( "OUTPUT" ), context );
184
185 double width = std::round( ( bbox.xMaximum() - bbox.xMinimum() ) / cellSize );
186 double height = std::round( ( bbox.yMaximum() - bbox.yMinimum() ) / cellSize );
187
188 QgsRasterCalculator calc( expression, outputFile, outputFormat, bbox, crs, width, height, entries, context.transformContext() );
189 calc.setCreationOptions( creationOptions.split( '|', Qt::SplitBehaviorFlags::SkipEmptyParts ) );
190 QgsRasterCalculator::Result result = calc.processCalculation( feedback );
191 qDeleteAll( mLayers );
192 mLayers.clear();
193 switch ( result )
194 {
196 throw QgsProcessingException( QObject::tr( "Error creating output file." ) );
198 throw QgsProcessingException( QObject::tr( "Error reading input layer." ) );
200 throw QgsProcessingException( QObject::tr( "Error parsing formula." ) );
202 throw QgsProcessingException( QObject::tr( "Error allocating memory for result." ) );
204 throw QgsProcessingException( QObject::tr( "Invalid band number for input." ) );
206 throw QgsProcessingException( QObject::tr( "Error occurred while performing calculation." ) );
207 default:
208 break;
209 }
210
211 QVariantMap outputs;
212 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
213 return outputs;
214}
215
216Qgis::ProcessingAlgorithmFlags QgsRasterCalculatorModelerAlgorithm::flags() const
217{
219}
220
221QString QgsRasterCalculatorModelerAlgorithm::name() const
222{
223 return QStringLiteral( "modelerrastercalc" );
224}
225
226QString QgsRasterCalculatorModelerAlgorithm::displayName() const
227{
228 return QObject::tr( "Raster calculator" );
229}
230
231QStringList QgsRasterCalculatorModelerAlgorithm::tags() const
232{
233 return QObject::tr( "raster,calculator" ).split( ',' );
234}
235
236QString QgsRasterCalculatorModelerAlgorithm::group() const
237{
238 return QObject::tr( "Raster analysis" );
239}
240
241QString QgsRasterCalculatorModelerAlgorithm::groupId() const
242{
243 return QStringLiteral( "rasteranalysis" );
244}
245
246QgsRasterCalculatorModelerAlgorithm *QgsRasterCalculatorModelerAlgorithm::createInstance() const
247{
248 return new QgsRasterCalculatorModelerAlgorithm();
249}
250
251QVariantMap QgsRasterCalculatorModelerAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
252{
253 for ( QgsMapLayer *layer : std::as_const( mLayers ) )
254 {
255 layer->moveToThread( QThread::currentThread() );
256 }
257
259 if ( parameters.value( QStringLiteral( "CRS" ) ).isValid() )
260 {
261 crs = parameterAsCrs( parameters, QStringLiteral( "CRS" ), context );
262 }
263 else
264 {
265 crs = mLayers.at( 0 )->crs();
266 }
267
268 QgsRectangle bbox;
269 if ( parameters.value( QStringLiteral( "EXTENT" ) ).isValid() )
270 {
271 bbox = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context, crs );
272 }
273 else
274 {
275 bbox = QgsProcessingUtils::combineLayerExtents( mLayers, crs, context );
276 }
277
278 double minCellSize = 1e9;
279
280 QVector<QgsRasterCalculatorEntry> entries;
281 int n = 0;
282 for ( QgsMapLayer *layer : mLayers )
283 {
284 QgsRasterLayer *rLayer = qobject_cast<QgsRasterLayer *>( layer );
285 if ( !rLayer )
286 {
287 continue;
288 }
289
290 n++;
291 const int nBands = rLayer->dataProvider()->bandCount();
292 for ( int i = 0; i < nBands; ++i )
293 {
295 entry.ref = QStringLiteral( "%1@%2" ).arg( indexToName( n ) ).arg( i + 1 );
296 entry.raster = rLayer;
297 entry.bandNumber = i + 1;
298 entries << entry;
299 }
300
301 QgsRectangle ext = rLayer->extent();
302 if ( rLayer->crs() != crs )
303 {
304 QgsCoordinateTransform ct( rLayer->crs(), crs, context.transformContext() );
305 ext = ct.transformBoundingBox( ext );
306 }
307
308 double cellSize = ( ext.xMaximum() - ext.xMinimum() ) / rLayer->width();
309 if ( cellSize < minCellSize )
310 {
311 minCellSize = cellSize;
312 }
313 }
314
315 double cellSize = parameterAsDouble( parameters, QStringLiteral( "CELL_SIZE" ), context );
316 if ( cellSize == 0 )
317 {
318 cellSize = minCellSize;
319 }
320
321 const QString expression = parameterAsExpression( parameters, QStringLiteral( "EXPRESSION" ), context );
322 const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
323 const QString outputFormat = parameterAsOutputRasterFormat( parameters, QStringLiteral( "OUTPUT" ), context );
324
325 double width = std::round( ( bbox.xMaximum() - bbox.xMinimum() ) / cellSize );
326 double height = std::round( ( bbox.yMaximum() - bbox.yMinimum() ) / cellSize );
327
328 QgsRasterCalculator calc( expression, outputFile, outputFormat, bbox, crs, width, height, entries, context.transformContext() );
329 QgsRasterCalculator::Result result = calc.processCalculation( feedback );
330 qDeleteAll( mLayers );
331 mLayers.clear();
332 switch ( result )
333 {
335 throw QgsProcessingException( QObject::tr( "Error creating output file." ) );
337 throw QgsProcessingException( QObject::tr( "Error reading input layer." ) );
339 throw QgsProcessingException( QObject::tr( "Error parsing formula." ) );
341 throw QgsProcessingException( QObject::tr( "Error allocating memory for result." ) );
343 throw QgsProcessingException( QObject::tr( "Invalid band number for input." ) );
345 throw QgsProcessingException( QObject::tr( "Error occurred while performing calculation." ) );
346 default:
347 break;
348 }
349
350 QVariantMap outputs;
351 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
352 return outputs;
353}
354
355QString QgsRasterCalculatorModelerAlgorithm::indexToName( int index ) const
356{
357 QString name;
358 int div = index;
359 int mod = 0;
360
361 while ( div > 0 )
362 {
363 mod = ( div - 1 ) % 26;
364 name = static_cast<char>( 65 + mod ) + name;
365 div = ( int ) ( ( div - mod ) / 26 );
366 }
367 return name;
368}
369
@ 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.
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