QGIS API Documentation 4.3.0-Master (9b7fa678901)
Loading...
Searching...
No Matches
qgsalgorithmrasterstackposition.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrasterstackposition.cpp
3 ---------------------
4 begin : July 2020
5 copyright : (C) 2020 by Clemens Raffler
6 email : clemens dot raffler 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
21#include "qgsrasterfilewriter.h"
22#include "qgsrasterprojector.h"
23
24#include <QString>
25
26using namespace Qt::StringLiterals;
27
29
30//
31//QgsRasterFrequencyByComparisonOperatorBase
32//
33
34QString QgsRasterStackPositionAlgorithmBase::group() const
35{
36 return QObject::tr( "Raster analysis" );
37}
38
39QString QgsRasterStackPositionAlgorithmBase::groupId() const
40{
41 return u"rasteranalysis"_s;
42}
43
44void QgsRasterStackPositionAlgorithmBase::initAlgorithm( const QVariantMap & )
45{
46 addParameter( new QgsProcessingParameterMultipleLayers( u"INPUT_RASTERS"_s, QObject::tr( "Input raster layers" ), Qgis::ProcessingSourceType::Raster ) );
47
48 addParameter( new QgsProcessingParameterRasterLayer( u"REFERENCE_LAYER"_s, QObject::tr( "Reference layer" ) ) );
49
50 addParameter( new QgsProcessingParameterBoolean( u"IGNORE_NODATA"_s, QObject::tr( "Ignore NoData values" ), false ) );
51
52 auto output_nodata_parameter = std::make_unique<QgsProcessingParameterNumber>( u"OUTPUT_NODATA_VALUE"_s, QObject::tr( "Output NoData value" ), Qgis::ProcessingNumberParameterType::Double, -9999, true );
53 output_nodata_parameter->setFlags( output_nodata_parameter->flags() | Qgis::ProcessingParameterFlag::Advanced );
54 addParameter( output_nodata_parameter.release() );
55
56 // backwards compatibility parameter
57 // TODO QGIS 5: remove parameter and related logic
58 auto createOptsParam = std::make_unique<QgsProcessingParameterString>( u"CREATE_OPTIONS"_s, QObject::tr( "Creation options" ), QVariant(), false, true );
59 createOptsParam->setMetadata( QVariantMap( { { u"widget_wrapper"_s, QVariantMap( { { u"widget_type"_s, u"rasteroptions"_s } } ) } } ) );
60 createOptsParam->setFlags( createOptsParam->flags() | Qgis::ProcessingParameterFlag::Hidden );
61 addParameter( createOptsParam.release() );
62
63 auto creationOptsParam = std::make_unique<QgsProcessingParameterString>( u"CREATION_OPTIONS"_s, QObject::tr( "Creation options" ), QVariant(), false, true );
64 creationOptsParam->setMetadata( QVariantMap( { { u"widget_wrapper"_s, QVariantMap( { { u"widget_type"_s, u"rasteroptions"_s } } ) } } ) );
65 creationOptsParam->setFlags( creationOptsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
66 addParameter( creationOptsParam.release() );
67
68 addParameter( new QgsProcessingParameterRasterDestination( u"OUTPUT"_s, QObject::tr( "Output layer" ) ) );
69 addOutput( new QgsProcessingOutputString( u"EXTENT"_s, QObject::tr( "Extent" ) ) );
70 addOutput( new QgsProcessingOutputString( u"CRS_AUTHID"_s, QObject::tr( "CRS authority identifier" ) ) );
71 addOutput( new QgsProcessingOutputNumber( u"WIDTH_IN_PIXELS"_s, QObject::tr( "Width in pixels" ) ) );
72 addOutput( new QgsProcessingOutputNumber( u"HEIGHT_IN_PIXELS"_s, QObject::tr( "Height in pixels" ) ) );
73 addOutput( new QgsProcessingOutputNumber( u"TOTAL_PIXEL_COUNT"_s, QObject::tr( "Total pixel count" ) ) );
74}
75
76bool QgsRasterStackPositionAlgorithmBase::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
77{
78 QgsRasterLayer *referenceLayer = parameterAsRasterLayer( parameters, u"REFERENCE_LAYER"_s, context );
79 if ( !referenceLayer )
80 throw QgsProcessingException( invalidRasterError( parameters, u"REFERENCE_LAYER"_s ) );
81
82 mIgnoreNoData = parameterAsBool( parameters, u"IGNORE_NODATA"_s, context );
83 mNoDataValue = parameterAsDouble( parameters, u"OUTPUT_NODATA_VALUE"_s, context );
84
85 mCrs = referenceLayer->crs();
86 mRasterUnitsPerPixelX = referenceLayer->rasterUnitsPerPixelX();
87 mRasterUnitsPerPixelY = referenceLayer->rasterUnitsPerPixelY();
88 mLayerWidth = referenceLayer->width();
89 mLayerHeight = referenceLayer->height();
90 mExtent = referenceLayer->extent();
91
92 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, u"INPUT_RASTERS"_s, context );
93 QList<QgsRasterLayer *> rasterLayers;
94 rasterLayers.reserve( layers.count() );
95 for ( QgsMapLayer *l : layers )
96 {
97 if ( feedback->isCanceled() )
98 break; //in case some slow data sources are loaded
99
100 if ( l->type() == Qgis::LayerType::Raster )
101 {
102 QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( l );
103 QgsRasterAnalysisUtils::RasterLogicInput input;
104 const int band = 1; //could be made dynamic
105 input.hasNoDataValue = layer->dataProvider()->sourceHasNoDataValue( band );
106 input.sourceDataProvider.reset( layer->dataProvider()->clone() );
107 input.interface = input.sourceDataProvider.get();
108 // add projector if necessary
109 if ( layer->crs() != mCrs )
110 {
111 input.projector = std::make_unique<QgsRasterProjector>();
112 input.projector->setInput( input.sourceDataProvider.get() );
113 input.projector->setCrs( layer->crs(), mCrs, context.transformContext() );
114 input.interface = input.projector.get();
115 }
116 mInputs.emplace_back( std::move( input ) );
117 }
118 }
119
120 return true;
121}
122
123QVariantMap QgsRasterStackPositionAlgorithmBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
124{
125 QString creationOptions = parameterAsString( parameters, u"CREATION_OPTIONS"_s, context ).trimmed();
126 // handle backwards compatibility parameter CREATE_OPTIONS
127 const QString optionsString = parameterAsString( parameters, u"CREATE_OPTIONS"_s, context );
128 if ( !optionsString.isEmpty() )
129 creationOptions = optionsString;
130
131 const QString outputFile = parameterAsOutputLayer( parameters, u"OUTPUT"_s, context );
132 const QString outputFormat = parameterAsOutputRasterFormat( parameters, u"OUTPUT"_s, context );
133
134 auto writer = std::make_unique<QgsRasterFileWriter>( outputFile );
135 writer->setOutputProviderKey( u"gdal"_s );
136 if ( !creationOptions.isEmpty() )
137 {
138 writer->setCreationOptions( creationOptions.split( '|' ) );
139 }
140 writer->setOutputFormat( outputFormat );
141 std::unique_ptr<QgsRasterDataProvider> provider( writer->createOneBandRaster( Qgis::DataType::Int32, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
142 if ( !provider )
143 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
144 if ( !provider->isValid() )
145 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
146
147 provider->setNoDataValue( 1, mNoDataValue );
148 const qgssize layerSize = static_cast<qgssize>( mLayerWidth ) * static_cast<qgssize>( mLayerHeight );
149
150 provider->setEditable( true );
151
152 QgsRasterIterator iter( provider.get() );
153 iter.startRasterRead( 1, mLayerWidth, mLayerHeight, mExtent );
154 int iterLeft = 0;
155 int iterTop = 0;
156 int iterCols = 0;
157 int iterRows = 0;
158 QgsRectangle blockExtent;
159
160 const bool hasReportsDuringClose = provider->hasReportsDuringClose();
161 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
162
163 std::unique_ptr<QgsRasterBlock> outputBlock;
164 while ( iter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
165 {
166 std::vector<std::unique_ptr<QgsRasterBlock>> inputBlocks;
167 for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : mInputs )
168 {
169 if ( feedback->isCanceled() )
170 break; //in case some slow data sources are loaded
171 for ( const int band : i.bands )
172 {
173 if ( feedback->isCanceled() )
174 break; //in case some slow data sources are loaded
175 std::unique_ptr<QgsRasterBlock> b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
176 inputBlocks.emplace_back( std::move( b ) );
177 }
178 }
179
180 feedback->setProgress( maxProgressDuringBlockWriting * iter.progress( 1 ) );
181 for ( int row = 0; row < iterRows; row++ )
182 {
183 if ( feedback->isCanceled() )
184 break;
185
186 for ( int col = 0; col < iterCols; col++ )
187 {
188 bool noDataInStack = false;
189
190 if ( !inputBlocks.empty() )
191 {
192 const int position = findPosition( inputBlocks, row, col, noDataInStack );
193
194 if ( position == -1 || ( noDataInStack && !mIgnoreNoData ) )
195 {
196 //output cell will always be NoData if NoData occurs the current raster cell
197 //of the input blocks and NoData is not ignored
198 //this saves unnecessary iterations on the cellValueStack
199 outputBlock->setValue( row, col, mNoDataValue );
200 }
201 else
202 {
203 outputBlock->setValue( row, col, position );
204 }
205 }
206 else
207 {
208 outputBlock->setValue( row, col, mNoDataValue );
209 }
210 }
211 }
212 if ( !provider->writeBlock( outputBlock.get(), 1, iterLeft, iterTop ) )
213 {
214 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( provider->error().summary() ) );
215 }
216 }
217 provider->setEditable( false );
218
219 if ( feedback && hasReportsDuringClose )
220 {
221 std::unique_ptr<QgsFeedback> scaledFeedback( QgsFeedback::createScaledFeedback( feedback, maxProgressDuringBlockWriting, 100.0 ) );
222 if ( !provider->closeWithProgress( scaledFeedback.get() ) )
223 {
224 if ( feedback->isCanceled() )
225 return {};
226 throw QgsProcessingException( QObject::tr( "Could not write raster dataset" ) );
227 }
228 }
229
230 QVariantMap outputs;
231 outputs.insert( u"EXTENT"_s, mExtent.toString() );
232 outputs.insert( u"CRS_AUTHID"_s, mCrs.authid() );
233 outputs.insert( u"WIDTH_IN_PIXELS"_s, mLayerWidth );
234 outputs.insert( u"HEIGHT_IN_PIXELS"_s, mLayerHeight );
235 outputs.insert( u"TOTAL_PIXEL_COUNT"_s, layerSize );
236 outputs.insert( u"OUTPUT"_s, outputFile );
237
238 return outputs;
239}
240
241//
242// QgsRasterStackLowestPositionAlgorithm
243//
244QString QgsRasterStackLowestPositionAlgorithm::displayName() const
245{
246 return QObject::tr( "Lowest position in raster stack" );
247}
248
249QString QgsRasterStackLowestPositionAlgorithm::name() const
250{
251 return u"lowestpositioninrasterstack"_s;
252}
253
254QStringList QgsRasterStackLowestPositionAlgorithm::tags() const
255{
256 return QObject::tr( "cell,lowest,position,pixel,stack" ).split( ',' );
257}
258
259QString QgsRasterStackLowestPositionAlgorithm::shortDescription() const
260{
261 return QObject::tr(
262 "Evaluates on a cell-by-cell basis the position "
263 "of the raster with the lowest value in a stack of rasters."
264 );
265}
266
267QString QgsRasterStackLowestPositionAlgorithm::shortHelpString() const
268{
269 return QObject::tr(
270 "This algorithm evaluates on a cell-by-cell basis the position "
271 "of the raster with the lowest value in a stack of rasters. Position counts start "
272 "with 1 and range to the total number of input rasters. The order of the input "
273 "rasters is relevant for the algorithm. If multiple rasters feature the lowest value, "
274 "the first raster will be used for the position value.\n "
275 "If multiband rasters are used in the data raster stack, the algorithm will always "
276 "perform the analysis on the first band of the rasters - use GDAL to use other bands in the analysis. "
277 "Any NoData cells in the raster layer stack will result in a NoData cell "
278 "in the output raster unless the \"ignore NoData\" parameter is checked. "
279 "The output NoData value can be set manually. The output rasters extent and resolution "
280 "is defined by a reference raster layer and is always of int32 type."
281 );
282}
283
284QgsRasterStackLowestPositionAlgorithm *QgsRasterStackLowestPositionAlgorithm::createInstance() const
285{
286 return new QgsRasterStackLowestPositionAlgorithm();
287}
288
289int QgsRasterStackLowestPositionAlgorithm::findPosition( std::vector<std::unique_ptr<QgsRasterBlock>> &inputBlocks, int &row, int &col, bool &noDataInRasterBlockStack )
290{
291 QGS_MARK_ALGORITHM_SOURCE
292
293 int lowestPosition = 0;
294
295 //auxiliary variables
296 const int inputBlocksCount = inputBlocks.size();
297 int currentPosition = 0;
298 int noDataCount = 0;
299 double firstValue = mNoDataValue;
300 bool firstValueIsNoData = true;
301
302 while ( firstValueIsNoData && ( currentPosition < inputBlocksCount ) )
303 {
304 //check if all blocks are nodata/invalid
305 std::unique_ptr<QgsRasterBlock> &firstBlock = inputBlocks.at( currentPosition );
306 firstValue = firstBlock->valueAndNoData( row, col, firstValueIsNoData );
307
308 if ( !firstBlock->isValid() || firstValueIsNoData )
309 {
310 noDataInRasterBlockStack = true;
311 noDataCount++;
312 }
313 else
314 {
315 lowestPosition = currentPosition;
316 }
317 currentPosition++;
318 }
319
320 if ( noDataCount == inputBlocksCount )
321 {
322 noDataInRasterBlockStack = true;
323 return -1; //all blocks are NoData
324 }
325 else
326 {
327 //scan for the lowest value
328 while ( currentPosition < inputBlocksCount )
329 {
330 std::unique_ptr<QgsRasterBlock> &currentBlock = inputBlocks.at( currentPosition );
331
332 bool currentValueIsNoData = false;
333 const double currentValue = currentBlock->valueAndNoData( row, col, currentValueIsNoData );
334
335 if ( !currentBlock->isValid() || currentValueIsNoData )
336 {
337 noDataInRasterBlockStack = true;
338 noDataCount++;
339 }
340 else
341 {
342 if ( currentValue < firstValue )
343 {
344 firstValue = currentValue;
345 lowestPosition = currentPosition;
346 }
347 }
348 currentPosition++;
349 }
350 }
351 //the ArcGIS implementation uses 1 for first position value instead of 0 as in standard c++
352 return ++lowestPosition; //therefore ++
353}
354
355//
356// QgsRasterStackHighestPositionAlgorithmAlgorithm
357//
358
359QString QgsRasterStackHighestPositionAlgorithm::displayName() const
360{
361 return QObject::tr( "Highest position in raster stack" );
362}
363
364QString QgsRasterStackHighestPositionAlgorithm::name() const
365{
366 return u"highestpositioninrasterstack"_s;
367}
368
369QStringList QgsRasterStackHighestPositionAlgorithm::tags() const
370{
371 return QObject::tr( "cell,highest,position,pixel,stack" ).split( ',' );
372}
373
374QString QgsRasterStackHighestPositionAlgorithm::shortDescription() const
375{
376 return QObject::tr(
377 "Evaluates on a cell-by-cell basis the position "
378 "of the raster with the highest value in a stack of rasters."
379 );
380}
381
382QString QgsRasterStackHighestPositionAlgorithm::shortHelpString() const
383{
384 return QObject::tr(
385 "This algorithm evaluates on a cell-by-cell basis the position "
386 "of the raster with the highest value in a stack of rasters. Position counts start "
387 "with 1 and range to the total number of input rasters. The order of the input "
388 "rasters is relevant for the algorithm. If multiple rasters feature the highest value, "
389 "the first raster will be used for the position value.\n "
390 "If multiband rasters are used in the data raster stack, the algorithm will always "
391 "perform the analysis on the first band of the rasters - use GDAL to use other bands in the analysis. "
392 "Any NoData cells in the raster layer stack will result in a NoData cell "
393 "in the output raster unless the \"ignore NoData\" parameter is checked. "
394 "The output NoData value can be set manually. The output rasters extent and resolution "
395 "is defined by a reference raster layer and is always of int32 type."
396 );
397}
398
399QgsRasterStackHighestPositionAlgorithm *QgsRasterStackHighestPositionAlgorithm::createInstance() const
400{
401 return new QgsRasterStackHighestPositionAlgorithm();
402}
403
404int QgsRasterStackHighestPositionAlgorithm::findPosition( std::vector<std::unique_ptr<QgsRasterBlock>> &inputBlocks, int &row, int &col, bool &noDataInRasterBlockStack )
405{
406 QGS_MARK_ALGORITHM_SOURCE
407
408 int highestPosition = 0;
409
410 //auxiliary variables
411 const int inputBlocksCount = inputBlocks.size();
412 int currentPosition = 0;
413 int noDataCount = 0;
414 double firstValue = mNoDataValue;
415 bool firstValueIsNoData = true;
416
417 while ( firstValueIsNoData && ( currentPosition < inputBlocksCount ) )
418 {
419 //check if all blocks are nodata/invalid
420 std::unique_ptr<QgsRasterBlock> &firstBlock = inputBlocks.at( currentPosition );
421 firstValue = firstBlock->valueAndNoData( row, col, firstValueIsNoData );
422
423 if ( !firstBlock->isValid() || firstValueIsNoData )
424 {
425 noDataInRasterBlockStack = true;
426 noDataCount++;
427 }
428 else
429 {
430 highestPosition = currentPosition;
431 }
432
433 currentPosition++;
434 }
435
436 if ( noDataCount == inputBlocksCount )
437 {
438 noDataInRasterBlockStack = true;
439 return -1; //all blocks are NoData
440 }
441 else
442 {
443 //scan for the lowest value
444 while ( currentPosition < inputBlocksCount )
445 {
446 std::unique_ptr<QgsRasterBlock> &currentBlock = inputBlocks.at( currentPosition );
447
448 bool currentValueIsNoData = false;
449 const double currentValue = currentBlock->valueAndNoData( row, col, currentValueIsNoData );
450
451 if ( !currentBlock->isValid() || currentValueIsNoData )
452 {
453 noDataInRasterBlockStack = true;
454 noDataCount++;
455 }
456 else
457 {
458 if ( currentValue > firstValue )
459 {
460 firstValue = currentValue;
461 highestPosition = currentPosition;
462 }
463 }
464 currentPosition++;
465 }
466 }
467 //the ArcGIS implementation uses 1 for first position value instead of 0 as in standard c++
468 return ++highestPosition; //therefore ++
469}
470
@ Raster
Raster layers.
Definition qgis.h:3775
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:420
@ Raster
Raster layer.
Definition qgis.h:208
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:4027
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:4026
@ Double
Double/float values.
Definition qgis.h:4067
@ Text
Plain text format.
Definition qgserror.h:40
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
static std::unique_ptr< QgsFeedback > createScaledFeedback(QgsFeedback *parentFeedback, double startPercentage, double endPercentage)
Returns a feedback object whose [0, 100] progression range will be mapped to parentFeedback [startPer...
Base class for all map layer types.
Definition qgsmaplayer.h:83
virtual Q_INVOKABLE QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
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.
A numeric output for processing algorithms.
A string output for processing algorithms.
A boolean 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 ...
A raster layer parameter for processing algorithms.
QgsRasterDataProvider * clone() const override=0
Clone itself, create deep copy.
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
Iterator for sequentially processing raster cells.
Represents a raster layer.
int height() const
Returns the height of the (unclipped) raster.
double rasterUnitsPerPixelX() const
Returns the number of raster units per each raster pixel in X axis.
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
double rasterUnitsPerPixelY() const
Returns the number of raster units per each raster pixel in Y axis.
int width() const
Returns the width of the (unclipped) raster.
A rectangle specified with double values.
unsigned long long qgssize
Qgssize is used instead of size_t, because size_t is stdlib type, unknown by SIP, and it would be har...
Definition qgis.h:8342