QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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 int lowestPosition = 0;
292
293 //auxiliary variables
294 const int inputBlocksCount = inputBlocks.size();
295 int currentPosition = 0;
296 int noDataCount = 0;
297 double firstValue = mNoDataValue;
298 bool firstValueIsNoData = true;
299
300 while ( firstValueIsNoData && ( currentPosition < inputBlocksCount ) )
301 {
302 //check if all blocks are nodata/invalid
303 std::unique_ptr<QgsRasterBlock> &firstBlock = inputBlocks.at( currentPosition );
304 firstValue = firstBlock->valueAndNoData( row, col, firstValueIsNoData );
305
306 if ( !firstBlock->isValid() || firstValueIsNoData )
307 {
308 noDataInRasterBlockStack = true;
309 noDataCount++;
310 }
311 else
312 {
313 lowestPosition = currentPosition;
314 }
315 currentPosition++;
316 }
317
318 if ( noDataCount == inputBlocksCount )
319 {
320 noDataInRasterBlockStack = true;
321 return -1; //all blocks are NoData
322 }
323 else
324 {
325 //scan for the lowest value
326 while ( currentPosition < inputBlocksCount )
327 {
328 std::unique_ptr<QgsRasterBlock> &currentBlock = inputBlocks.at( currentPosition );
329
330 bool currentValueIsNoData = false;
331 const double currentValue = currentBlock->valueAndNoData( row, col, currentValueIsNoData );
332
333 if ( !currentBlock->isValid() || currentValueIsNoData )
334 {
335 noDataInRasterBlockStack = true;
336 noDataCount++;
337 }
338 else
339 {
340 if ( currentValue < firstValue )
341 {
342 firstValue = currentValue;
343 lowestPosition = currentPosition;
344 }
345 }
346 currentPosition++;
347 }
348 }
349 //the ArcGIS implementation uses 1 for first position value instead of 0 as in standard c++
350 return ++lowestPosition; //therefore ++
351}
352
353//
354// QgsRasterStackHighestPositionAlgorithmAlgorithm
355//
356
357QString QgsRasterStackHighestPositionAlgorithm::displayName() const
358{
359 return QObject::tr( "Highest position in raster stack" );
360}
361
362QString QgsRasterStackHighestPositionAlgorithm::name() const
363{
364 return u"highestpositioninrasterstack"_s;
365}
366
367QStringList QgsRasterStackHighestPositionAlgorithm::tags() const
368{
369 return QObject::tr( "cell,highest,position,pixel,stack" ).split( ',' );
370}
371
372QString QgsRasterStackHighestPositionAlgorithm::shortDescription() const
373{
374 return QObject::tr(
375 "Evaluates on a cell-by-cell basis the position "
376 "of the raster with the highest value in a stack of rasters."
377 );
378}
379
380QString QgsRasterStackHighestPositionAlgorithm::shortHelpString() const
381{
382 return QObject::tr(
383 "This algorithm evaluates on a cell-by-cell basis the position "
384 "of the raster with the highest value in a stack of rasters. Position counts start "
385 "with 1 and range to the total number of input rasters. The order of the input "
386 "rasters is relevant for the algorithm. If multiple rasters feature the highest value, "
387 "the first raster will be used for the position value.\n "
388 "If multiband rasters are used in the data raster stack, the algorithm will always "
389 "perform the analysis on the first band of the rasters - use GDAL to use other bands in the analysis. "
390 "Any NoData cells in the raster layer stack will result in a NoData cell "
391 "in the output raster unless the \"ignore NoData\" parameter is checked. "
392 "The output NoData value can be set manually. The output rasters extent and resolution "
393 "is defined by a reference raster layer and is always of int32 type."
394 );
395}
396
397QgsRasterStackHighestPositionAlgorithm *QgsRasterStackHighestPositionAlgorithm::createInstance() const
398{
399 return new QgsRasterStackHighestPositionAlgorithm();
400}
401
402int QgsRasterStackHighestPositionAlgorithm::findPosition( std::vector<std::unique_ptr<QgsRasterBlock>> &inputBlocks, int &row, int &col, bool &noDataInRasterBlockStack )
403{
404 int highestPosition = 0;
405
406 //auxiliary variables
407 const int inputBlocksCount = inputBlocks.size();
408 int currentPosition = 0;
409 int noDataCount = 0;
410 double firstValue = mNoDataValue;
411 bool firstValueIsNoData = true;
412
413 while ( firstValueIsNoData && ( currentPosition < inputBlocksCount ) )
414 {
415 //check if all blocks are nodata/invalid
416 std::unique_ptr<QgsRasterBlock> &firstBlock = inputBlocks.at( currentPosition );
417 firstValue = firstBlock->valueAndNoData( row, col, firstValueIsNoData );
418
419 if ( !firstBlock->isValid() || firstValueIsNoData )
420 {
421 noDataInRasterBlockStack = true;
422 noDataCount++;
423 }
424 else
425 {
426 highestPosition = currentPosition;
427 }
428
429 currentPosition++;
430 }
431
432 if ( noDataCount == inputBlocksCount )
433 {
434 noDataInRasterBlockStack = true;
435 return -1; //all blocks are NoData
436 }
437 else
438 {
439 //scan for the lowest value
440 while ( currentPosition < inputBlocksCount )
441 {
442 std::unique_ptr<QgsRasterBlock> &currentBlock = inputBlocks.at( currentPosition );
443
444 bool currentValueIsNoData = false;
445 const double currentValue = currentBlock->valueAndNoData( row, col, currentValueIsNoData );
446
447 if ( !currentBlock->isValid() || currentValueIsNoData )
448 {
449 noDataInRasterBlockStack = true;
450 noDataCount++;
451 }
452 else
453 {
454 if ( currentValue > firstValue )
455 {
456 firstValue = currentValue;
457 highestPosition = currentPosition;
458 }
459 }
460 currentPosition++;
461 }
462 }
463 //the ArcGIS implementation uses 1 for first position value instead of 0 as in standard c++
464 return ++highestPosition; //therefore ++
465}
466
@ Raster
Raster layers.
Definition qgis.h:3651
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:400
@ Raster
Raster layer.
Definition qgis.h:208
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3881
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3880
@ Double
Double/float values.
Definition qgis.h:3921
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 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:7485