QGIS API Documentation 3.99.0-Master (357b655ed83)
Loading...
Searching...
No Matches
qgsalgorithmcellstatistics.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmcellstatistics.cpp
3 ---------------------
4 begin : May 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
31QString QgsCellStatisticsAlgorithmBase::group() const
32{
33 return QObject::tr( "Raster analysis" );
34}
35
36QString QgsCellStatisticsAlgorithmBase::groupId() const
37{
38 return u"rasteranalysis"_s;
39}
40
41void QgsCellStatisticsAlgorithmBase::initAlgorithm( const QVariantMap & )
42{
43 addParameter( new QgsProcessingParameterMultipleLayers( u"INPUT"_s, QObject::tr( "Input layers" ), Qgis::ProcessingSourceType::Raster ) );
44
45 addSpecificAlgorithmParams();
46
47 addParameter( new QgsProcessingParameterBoolean( u"IGNORE_NODATA"_s, QObject::tr( "Ignore NoData values" ), true ) );
48
49 addParameter( new QgsProcessingParameterRasterLayer( u"REFERENCE_LAYER"_s, QObject::tr( "Reference layer" ) ) );
50
51 auto output_nodata_parameter = std::make_unique<QgsProcessingParameterNumber>( u"OUTPUT_NODATA_VALUE"_s, QObject::tr( "Output NoData value" ), Qgis::ProcessingNumberParameterType::Double, -9999, false );
52 output_nodata_parameter->setFlags( output_nodata_parameter->flags() | Qgis::ProcessingParameterFlag::Advanced );
53 addParameter( output_nodata_parameter.release() );
54
55 // backwards compatibility parameter
56 // TODO QGIS 5: remove parameter and related logic
57 auto createOptsParam = std::make_unique<QgsProcessingParameterString>( u"CREATE_OPTIONS"_s, QObject::tr( "Creation options" ), QVariant(), false, true );
58 createOptsParam->setMetadata( QVariantMap( { { u"widget_wrapper"_s, QVariantMap( { { u"widget_type"_s, u"rasteroptions"_s } } ) } } ) );
59 createOptsParam->setFlags( createOptsParam->flags() | Qgis::ProcessingParameterFlag::Hidden );
60 addParameter( createOptsParam.release() );
61
62 auto creationOptsParam = std::make_unique<QgsProcessingParameterString>( u"CREATION_OPTIONS"_s, QObject::tr( "Creation options" ), QVariant(), false, true );
63 creationOptsParam->setMetadata( QVariantMap( { { u"widget_wrapper"_s, QVariantMap( { { u"widget_type"_s, u"rasteroptions"_s } } ) } } ) );
64 creationOptsParam->setFlags( creationOptsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
65 addParameter( creationOptsParam.release() );
66
67 addParameter( new QgsProcessingParameterRasterDestination( u"OUTPUT"_s, QObject::tr( "Output layer" ) ) );
68
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 QgsCellStatisticsAlgorithmBase::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 mCrs = referenceLayer->crs();
85 mRasterUnitsPerPixelX = referenceLayer->rasterUnitsPerPixelX();
86 mRasterUnitsPerPixelY = referenceLayer->rasterUnitsPerPixelY();
87 mLayerWidth = referenceLayer->width();
88 mLayerHeight = referenceLayer->height();
89 mExtent = referenceLayer->extent();
90
91 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, u"INPUT"_s, context );
92 QList<QgsRasterLayer *> rasterLayers;
93 rasterLayers.reserve( layers.count() );
94 for ( QgsMapLayer *l : layers )
95 {
96 if ( feedback->isCanceled() )
97 break; //in case some slow data sources are loaded
98
99 if ( l->type() == Qgis::LayerType::Raster )
100 {
101 QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( l );
102 QgsRasterAnalysisUtils::RasterLogicInput input;
103 const int band = 1; //could be made dynamic
104 input.hasNoDataValue = layer->dataProvider()->sourceHasNoDataValue( band );
105 input.sourceDataProvider.reset( layer->dataProvider()->clone() );
106 input.interface = input.sourceDataProvider.get();
107 // add projector if necessary
108 if ( layer->crs() != mCrs )
109 {
110 input.projector = std::make_unique<QgsRasterProjector>();
111 input.projector->setInput( input.sourceDataProvider.get() );
112 input.projector->setCrs( layer->crs(), mCrs, context.transformContext() );
113 input.interface = input.projector.get();
114 }
115 mInputs.emplace_back( std::move( input ) );
116 }
117 }
118
119 //determine output raster data type
120 //initially raster data type to most primitive data type that is possible
121 mDataType = Qgis::DataType::Byte;
122 for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : std::as_const( mInputs ) )
123 {
124 for ( int band : i.bands )
125 {
126 Qgis::DataType inputDataType = i.interface->dataType( band );
127 if ( static_cast<int>( mDataType ) < static_cast<int>( inputDataType ) )
128 mDataType = inputDataType; //if raster data type is more potent, set it as new data type
129 }
130 }
131
132 prepareSpecificAlgorithmParameters( parameters, context, feedback );
133
134 return true;
135}
136
137
138QVariantMap QgsCellStatisticsAlgorithmBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
139{
140 QString creationOptions = parameterAsString( parameters, u"CREATION_OPTIONS"_s, context ).trimmed();
141 // handle backwards compatibility parameter CREATE_OPTIONS
142 const QString optionsString = parameterAsString( parameters, u"CREATE_OPTIONS"_s, context );
143 if ( !optionsString.isEmpty() )
144 creationOptions = optionsString;
145
146 const QString outputFile = parameterAsOutputLayer( parameters, u"OUTPUT"_s, context );
147 const QString outputFormat = parameterAsOutputRasterFormat( parameters, u"OUTPUT"_s, context );
148
149 auto writer = std::make_unique<QgsRasterFileWriter>( outputFile );
150 writer->setOutputProviderKey( u"gdal"_s );
151 if ( !creationOptions.isEmpty() )
152 {
153 writer->setCreationOptions( creationOptions.split( '|' ) );
154 }
155 writer->setOutputFormat( outputFormat );
156 mOutputRasterDataProvider.reset( writer->createOneBandRaster( mDataType, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
157 if ( !mOutputRasterDataProvider )
158 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
159 if ( !mOutputRasterDataProvider->isValid() )
160 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, mOutputRasterDataProvider->error().message( QgsErrorMessage::Text ) ) );
161
162 mOutputRasterDataProvider->setNoDataValue( 1, mNoDataValue );
163 qgssize layerSize = static_cast<qgssize>( mLayerWidth ) * static_cast<qgssize>( mLayerHeight );
164
165 const bool hasReportsDuringClose = mOutputRasterDataProvider->hasReportsDuringClose();
166 mMaxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
167
168 //call child statistics method
169 processRasterStack( feedback );
170
171 if ( feedback && hasReportsDuringClose )
172 {
173 std::unique_ptr<QgsFeedback> scaledFeedback( QgsFeedback::createScaledFeedback( feedback, mMaxProgressDuringBlockWriting, 100.0 ) );
174 if ( !mOutputRasterDataProvider->closeWithProgress( scaledFeedback.get() ) )
175 {
176 if ( feedback->isCanceled() )
177 return {};
178 throw QgsProcessingException( QObject::tr( "Could not write raster dataset" ) );
179 }
180 }
181
182 mOutputRasterDataProvider.reset();
183
184 QVariantMap outputs;
185 outputs.insert( u"EXTENT"_s, mExtent.toString() );
186 outputs.insert( u"CRS_AUTHID"_s, mCrs.authid() );
187 outputs.insert( u"WIDTH_IN_PIXELS"_s, mLayerWidth );
188 outputs.insert( u"HEIGHT_IN_PIXELS"_s, mLayerHeight );
189 outputs.insert( u"TOTAL_PIXEL_COUNT"_s, layerSize );
190 outputs.insert( u"OUTPUT"_s, outputFile );
191
192 return outputs;
193}
194
195
196//
197//QgsCellStatisticsAlgorithm
198//
199QString QgsCellStatisticsAlgorithm::displayName() const
200{
201 return QObject::tr( "Cell statistics" );
202}
203
204QString QgsCellStatisticsAlgorithm::name() const
205{
206 return u"cellstatistics"_s;
207}
208
209QStringList QgsCellStatisticsAlgorithm::tags() const
210{
211 return QObject::tr( "cell,pixel,statistic,count,mean,sum,majority,minority,variance,variety,range,median,minimum,maximum" ).split( ',' );
212}
213
214QString QgsCellStatisticsAlgorithm::shortHelpString() const
215{
216 return QObject::tr( "The Cell statistics algorithm computes a value for each cell of the "
217 "output raster. At each cell location, "
218 "the output value is defined as a function of all overlaid cell values of the "
219 "input rasters.\n\n"
220 "The output raster's extent and resolution is defined by a reference "
221 "raster. The following functions can be applied on the input "
222 "raster cells per output raster cell location:\n"
223 "<ul> "
224 " <li>Sum</li>"
225 " <li>Count</li>"
226 " <li>Mean</li>"
227 " <li>Median</li>"
228 " <li>Standard deviation</li>"
229 " <li>Variance</li>"
230 " <li>Minimum</li>"
231 " <li>Maximum</li>"
232 " <li>Minority (least frequent value)</li>"
233 " <li>Majority (most frequent value)</li>"
234 " <li>Range (max-min)</li>"
235 " <li>Variety (count of unique values)</li>"
236 "</ul> "
237 "Input raster layers that do not match the cell size of the reference raster layer will be "
238 "resampled using nearest neighbor resampling. The output raster data type will be set to "
239 "the most complex data type present in the input datasets except when using the functions "
240 "Mean, Standard deviation and Variance (data type is always Float32/Float64 depending on input float type) or Count and Variety (data type is always Int32).\n"
241 "<i>Calculation details - general:</i> NoData values in any of the input layers will result in a NoData cell output if the Ignore NoData parameter is not set.\n"
242 "<i>Calculation details - Count:</i> Count will always result in the number of cells without NoData values at the current cell location.\n"
243 "<i>Calculation details - Median:</i> If the number of input layers is even, the median will be calculated as the "
244 "arithmetic mean of the two middle values of the ordered cell input values. In this case the output data type is Float32.\n"
245 "<i>Calculation details - Minority/Majority:</i> If no unique minority or majority could be found, the result is NoData, except all "
246 "input cell values are equal." );
247}
248
249QString QgsCellStatisticsAlgorithm::shortDescription() const
250{
251 return QObject::tr( "Generates a raster whose cell values are computed from overlaid cell values of the input rasters." );
252}
253
254QgsCellStatisticsAlgorithm *QgsCellStatisticsAlgorithm::createInstance() const
255{
256 return new QgsCellStatisticsAlgorithm();
257}
258
259void QgsCellStatisticsAlgorithm::addSpecificAlgorithmParams()
260{
261 QStringList statistics = QStringList();
262 statistics << QObject::tr( "Sum" )
263 << QObject::tr( "Count" )
264 << QObject::tr( "Mean" )
265 << QObject::tr( "Median" )
266 << QObject::tr( "Standard deviation" )
267 << QObject::tr( "Variance" )
268 << QObject::tr( "Minimum" )
269 << QObject::tr( "Maximum" )
270 << QObject::tr( "Minority" )
271 << QObject::tr( "Majority" )
272 << QObject::tr( "Range" )
273 << QObject::tr( "Variety" );
274
275 addParameter( new QgsProcessingParameterEnum( u"STATISTIC"_s, QObject::tr( "Statistic" ), statistics, false, 0, false ) );
276}
277
278bool QgsCellStatisticsAlgorithm::prepareSpecificAlgorithmParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
279{
280 Q_UNUSED( feedback )
281 //obtain statistic method
282 mMethod = static_cast<QgsRasterAnalysisUtils::CellValueStatisticMethods>( parameterAsEnum( parameters, u"STATISTIC"_s, context ) );
283
284 //force data types on specific functions in the cellstatistics alg if input data types don't match
285 if (
286 mMethod == QgsRasterAnalysisUtils::Mean || mMethod == QgsRasterAnalysisUtils::StandardDeviation || mMethod == QgsRasterAnalysisUtils::Variance || ( mMethod == QgsRasterAnalysisUtils::Median && ( mInputs.size() % 2 == 0 ) )
287 )
288 {
289 if ( static_cast<int>( mDataType ) < 6 )
290 mDataType = Qgis::DataType::Float32; //force float on mean, stddev and median with equal number of input layers if all inputs are integer
291 }
292 else if ( mMethod == QgsRasterAnalysisUtils::Count || mMethod == QgsRasterAnalysisUtils::Variety ) //count, variety
293 {
294 if ( static_cast<int>( mDataType ) > 5 ) //if is floating point type
295 mDataType = Qgis::DataType::Int32; //force integer on variety if all inputs are float or complex
296 }
297 return true;
298}
299
300void QgsCellStatisticsAlgorithm::processRasterStack( QgsProcessingFeedback *feedback )
301{
302 mOutputRasterDataProvider->setEditable( true );
303 QgsRasterIterator outputIter( mOutputRasterDataProvider.get() );
304 outputIter.startRasterRead( 1, mLayerWidth, mLayerHeight, mExtent );
305
306 int iterLeft = 0;
307 int iterTop = 0;
308 int iterCols = 0;
309 int iterRows = 0;
310 QgsRectangle blockExtent;
311 std::unique_ptr<QgsRasterBlock> outputBlock;
312 while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
313 {
314 std::vector<std::unique_ptr<QgsRasterBlock>> inputBlocks;
315 for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : std::as_const( mInputs ) )
316 {
317 if ( feedback->isCanceled() )
318 break; //in case some slow data sources are loaded
319 for ( int band : i.bands )
320 {
321 if ( feedback->isCanceled() )
322 break; //in case some slow data sources are loaded
323 std::unique_ptr<QgsRasterBlock> b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
324 inputBlocks.emplace_back( std::move( b ) );
325 }
326 }
327
328 feedback->setProgress( mMaxProgressDuringBlockWriting * outputIter.progress( 1 ) );
329 for ( int row = 0; row < iterRows; row++ )
330 {
331 if ( feedback->isCanceled() )
332 break;
333
334 for ( int col = 0; col < iterCols; col++ )
335 {
336 double result = 0;
337 bool noDataInStack = false;
338 std::vector<double> cellValues = QgsRasterAnalysisUtils::getCellValuesFromBlockStack( inputBlocks, row, col, noDataInStack );
339 int cellValueStackSize = cellValues.size();
340
341 if ( noDataInStack && !mIgnoreNoData )
342 {
343 //output cell will always be NoData if NoData occurs in cellValueStack and NoData is not ignored
344 //this saves unnecessary iterations on the cellValueStack
345 if ( mMethod == QgsRasterAnalysisUtils::Count )
346 outputBlock->setValue( row, col, cellValueStackSize );
347 else
348 {
349 outputBlock->setValue( row, col, mNoDataValue );
350 }
351 }
352 else if ( !noDataInStack || ( mIgnoreNoData && cellValueStackSize > 0 ) )
353 {
354 switch ( mMethod )
355 {
356 case QgsRasterAnalysisUtils::Sum:
357 result = std::accumulate( cellValues.begin(), cellValues.end(), 0.0 );
358 break;
359 case QgsRasterAnalysisUtils::Count:
360 result = cellValueStackSize;
361 break;
362 case QgsRasterAnalysisUtils::Mean:
363 result = QgsRasterAnalysisUtils::meanFromCellValues( cellValues, cellValueStackSize );
364 break;
365 case QgsRasterAnalysisUtils::Median:
366 result = QgsRasterAnalysisUtils::medianFromCellValues( cellValues, cellValueStackSize );
367 break;
368 case QgsRasterAnalysisUtils::StandardDeviation:
369 result = QgsRasterAnalysisUtils::stddevFromCellValues( cellValues, cellValueStackSize );
370 break;
371 case QgsRasterAnalysisUtils::Variance:
372 result = QgsRasterAnalysisUtils::varianceFromCellValues( cellValues, cellValueStackSize );
373 break;
374 case QgsRasterAnalysisUtils::Minimum:
375 result = QgsRasterAnalysisUtils::minimumFromCellValues( cellValues );
376 break;
377 case QgsRasterAnalysisUtils::Maximum:
378 result = QgsRasterAnalysisUtils::maximumFromCellValues( cellValues );
379 break;
380 case QgsRasterAnalysisUtils::Minority:
381 result = QgsRasterAnalysisUtils::minorityFromCellValues( cellValues, mNoDataValue, cellValueStackSize );
382 break;
383 case QgsRasterAnalysisUtils::Majority:
384 result = QgsRasterAnalysisUtils::majorityFromCellValues( cellValues, mNoDataValue, cellValueStackSize );
385 break;
386 case QgsRasterAnalysisUtils::Range:
387 result = QgsRasterAnalysisUtils::rangeFromCellValues( cellValues );
388 break;
389 case QgsRasterAnalysisUtils::Variety:
390 result = QgsRasterAnalysisUtils::varietyFromCellValues( cellValues );
391 break;
392 }
393 outputBlock->setValue( row, col, result );
394 }
395 else
396 {
397 //result is NoData if cellValueStack contains no valid values, eg. all cellValues are NoData
398 outputBlock->setValue( row, col, mNoDataValue );
399 }
400 }
401 }
402 if ( !mOutputRasterDataProvider->writeBlock( outputBlock.get(), 1, iterLeft, iterTop ) )
403 {
404 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( mOutputRasterDataProvider->error().summary() ) );
405 }
406 }
407 mOutputRasterDataProvider->setEditable( false );
408}
409
410//
411//QgsCellStatisticsPercentileAlgorithm
412//
413QString QgsCellStatisticsPercentileAlgorithm::displayName() const
414{
415 return QObject::tr( "Cell stack percentile" );
416}
417
418QString QgsCellStatisticsPercentileAlgorithm::name() const
419{
420 return u"cellstackpercentile"_s;
421}
422
423QStringList QgsCellStatisticsPercentileAlgorithm::tags() const
424{
425 return QObject::tr( "cell,pixel,statistic,percentile,quantile,quartile" ).split( ',' );
426}
427
428QString QgsCellStatisticsPercentileAlgorithm::shortHelpString() const
429{
430 return QObject::tr( "This algorithm generates a raster containing the cell-wise percentile value of a stack of input rasters. "
431 "The percentile to return is determined by the percentile input value (ranges between 0 and 1). "
432 "At each cell location, the specified percentile is obtained using the respective value from "
433 "the stack of all overlaid and sorted cell values of the input rasters.\n\n"
434 "There are three methods for percentile calculation:"
435 "<ul> "
436 " <li>Nearest rank</li>"
437 " <li>Inclusive linear interpolation (PERCENTILE.INC)</li>"
438 " <li>Exclusive linear interpolation (PERCENTILE.EXC)</li>"
439 "</ul> "
440 "While the output value can stay the same for the nearest rank method (obtains the value that is nearest to the "
441 "specified percentile), the linear interpolation method return unique values for different percentiles. Both interpolation "
442 "methods follow their counterpart methods implemented by LibreOffice or Microsoft Excel. \n\n"
443 "The output raster's extent and resolution is defined by a reference "
444 "raster. If the input raster layers that do not match the cell size of the reference raster layer will be "
445 "resampled using nearest neighbor resampling. NoData values in any of the input layers will result in a NoData cell output if the Ignore NoData parameter is not set. "
446 "The output raster data type will be set to the most complex data type present in the input datasets. " );
447}
448
449QString QgsCellStatisticsPercentileAlgorithm::shortDescription() const
450{
451 return QObject::tr( "Generates a raster containing the cell-wise percentile value of a stack of input rasters." );
452}
453
454QgsCellStatisticsPercentileAlgorithm *QgsCellStatisticsPercentileAlgorithm::createInstance() const
455{
456 return new QgsCellStatisticsPercentileAlgorithm();
457}
458
459void QgsCellStatisticsPercentileAlgorithm::addSpecificAlgorithmParams()
460{
461 addParameter( new QgsProcessingParameterEnum( u"METHOD"_s, QObject::tr( "Method" ), QStringList() << QObject::tr( "Nearest rank" ) << QObject::tr( "Inclusive linear interpolation (PERCENTILE.INC)" ) << QObject::tr( "Exclusive linear interpolation (PERCENTILE.EXC)" ), false, 0, false ) );
462 addParameter( new QgsProcessingParameterNumber( u"PERCENTILE"_s, QObject::tr( "Percentile" ), Qgis::ProcessingNumberParameterType::Double, 0.25, false, 0.0, 1.0 ) );
463}
464
465bool QgsCellStatisticsPercentileAlgorithm::prepareSpecificAlgorithmParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
466{
467 Q_UNUSED( feedback )
468 mMethod = static_cast<QgsRasterAnalysisUtils::CellValuePercentileMethods>( parameterAsEnum( parameters, u"METHOD"_s, context ) );
469 mPercentile = parameterAsDouble( parameters, u"PERCENTILE"_s, context );
470
471 //default percentile output data type to float32 raster if interpolation method is chosen
472 //otherwise use the most potent data type in the input raster stack (see prepareAlgorithm() in base class)
473 if ( mMethod != QgsRasterAnalysisUtils::CellValuePercentileMethods::NearestRankPercentile && static_cast<int>( mDataType ) < 6 )
474 mDataType = Qgis::DataType::Float32;
475
476 return true;
477}
478
479void QgsCellStatisticsPercentileAlgorithm::processRasterStack( QgsProcessingFeedback *feedback )
480{
481 mOutputRasterDataProvider->setEditable( true );
482 QgsRasterIterator outputIter( mOutputRasterDataProvider.get() );
483 outputIter.startRasterRead( 1, mLayerWidth, mLayerHeight, mExtent );
484
485 int iterLeft = 0;
486 int iterTop = 0;
487 int iterCols = 0;
488 int iterRows = 0;
489 QgsRectangle blockExtent;
490 std::unique_ptr<QgsRasterBlock> outputBlock;
491 while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
492 {
493 std::vector<std::unique_ptr<QgsRasterBlock>> inputBlocks;
494 for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : std::as_const( mInputs ) )
495 {
496 if ( feedback->isCanceled() )
497 break; //in case some slow data sources are loaded
498 for ( int band : i.bands )
499 {
500 if ( feedback->isCanceled() )
501 break; //in case some slow data sources are loaded
502 std::unique_ptr<QgsRasterBlock> b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
503 inputBlocks.emplace_back( std::move( b ) );
504 }
505 }
506
507 feedback->setProgress( mMaxProgressDuringBlockWriting * outputIter.progress( 1 ) );
508 for ( int row = 0; row < iterRows; row++ )
509 {
510 if ( feedback->isCanceled() )
511 break;
512
513 for ( int col = 0; col < iterCols; col++ )
514 {
515 double result = 0;
516 bool noDataInStack = false;
517 std::vector<double> cellValues = QgsRasterAnalysisUtils::getCellValuesFromBlockStack( inputBlocks, row, col, noDataInStack );
518 int cellValueStackSize = cellValues.size();
519
520 if ( noDataInStack && !mIgnoreNoData )
521 {
522 outputBlock->setValue( row, col, mNoDataValue );
523 }
524 else if ( !noDataInStack || ( mIgnoreNoData && cellValueStackSize > 0 ) )
525 {
526 switch ( mMethod )
527 {
528 case QgsRasterAnalysisUtils::NearestRankPercentile:
529 result = QgsRasterAnalysisUtils::nearestRankPercentile( cellValues, cellValueStackSize, mPercentile );
530 break;
531 case QgsRasterAnalysisUtils::InterpolatedPercentileInc:
532 result = QgsRasterAnalysisUtils::interpolatedPercentileInc( cellValues, cellValueStackSize, mPercentile );
533 break;
534 case QgsRasterAnalysisUtils::InterpolatedPercentileExc:
535 result = QgsRasterAnalysisUtils::interpolatedPercentileExc( cellValues, cellValueStackSize, mPercentile, mNoDataValue );
536 break;
537 }
538 outputBlock->setValue( row, col, result );
539 }
540 else
541 {
542 //result is NoData if cellValueStack contains no valid values, eg. all cellValues are NoData
543 outputBlock->setValue( row, col, mNoDataValue );
544 }
545 }
546 }
547 if ( !mOutputRasterDataProvider->writeBlock( outputBlock.get(), 1, iterLeft, iterTop ) )
548 {
549 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( mOutputRasterDataProvider->error().summary() ) );
550 }
551 }
552 mOutputRasterDataProvider->setEditable( false );
553}
554
555//
556//QgsCellStatisticsPercentRankFromValueAlgorithm
557//
558QString QgsCellStatisticsPercentRankFromValueAlgorithm::displayName() const
559{
560 return QObject::tr( "Cell stack percent rank from value" );
561}
562
563QString QgsCellStatisticsPercentRankFromValueAlgorithm::name() const
564{
565 return u"cellstackpercentrankfromvalue"_s;
566}
567
568QStringList QgsCellStatisticsPercentRankFromValueAlgorithm::tags() const
569{
570 return QObject::tr( "cell,pixel,statistic,percentrank,rank,percent,value" ).split( ',' );
571}
572
573QString QgsCellStatisticsPercentRankFromValueAlgorithm::shortHelpString() const
574{
575 return QObject::tr( "This algorithm generates a raster containing the cell-wise percent rank value of a stack of input rasters based on a single input value.\n\n"
576 "At each cell location, the specified value is ranked among the respective values in the stack of all overlaid and sorted cell values from the input rasters. "
577 "For values outside of the stack value distribution, the algorithm returns NoData because the value cannot be ranked among the cell values.\n\n"
578 "There are two methods for percentile calculation:"
579 "<ul> "
580 " <li>Inclusive linearly interpolated percent rank (PERCENTRANK.INC)</li>"
581 " <li>Exclusive linearly interpolated percent rank (PERCENTRANK.EXC)</li>"
582 "</ul> "
583 "The linear interpolation method return the unique percent rank for different values. Both interpolation "
584 "methods follow their counterpart methods implemented by LibreOffice or Microsoft Excel. \n\n"
585 "The output raster's extent and resolution is defined by a reference "
586 "raster. If the input raster layers that do not match the cell size of the reference raster layer will be "
587 "resampled using nearest neighbor resampling. NoData values in any of the input layers will result in a NoData cell output if the Ignore NoData parameter is not set. "
588 "The output raster data type will always be Float32." );
589}
590
591QString QgsCellStatisticsPercentRankFromValueAlgorithm::shortDescription() const
592{
593 return QObject::tr( "Generates a raster containing the cell-wise percent rank value of a stack of input rasters based on a single input value." );
594}
595
596QgsCellStatisticsPercentRankFromValueAlgorithm *QgsCellStatisticsPercentRankFromValueAlgorithm::createInstance() const
597{
598 return new QgsCellStatisticsPercentRankFromValueAlgorithm();
599}
600
601void QgsCellStatisticsPercentRankFromValueAlgorithm::addSpecificAlgorithmParams()
602{
603 addParameter( new QgsProcessingParameterEnum( u"METHOD"_s, QObject::tr( "Method" ), QStringList() << QObject::tr( "Inclusive linear interpolation (PERCENTRANK.INC)" ) << QObject::tr( "Exclusive linear interpolation (PERCENTRANK.EXC)" ), false, 0, false ) );
604 addParameter( new QgsProcessingParameterNumber( u"VALUE"_s, QObject::tr( "Value" ), Qgis::ProcessingNumberParameterType::Double, 10, false ) );
605}
606
607bool QgsCellStatisticsPercentRankFromValueAlgorithm::prepareSpecificAlgorithmParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
608{
609 Q_UNUSED( feedback )
610 mMethod = static_cast<QgsRasterAnalysisUtils::CellValuePercentRankMethods>( parameterAsEnum( parameters, u"METHOD"_s, context ) );
611 mValue = parameterAsDouble( parameters, u"VALUE"_s, context );
612
613 //output data type always defaults to Float32 because result only ranges between 0 and 1
614 mDataType = Qgis::DataType::Float32;
615 return true;
616}
617
618void QgsCellStatisticsPercentRankFromValueAlgorithm::processRasterStack( QgsProcessingFeedback *feedback )
619{
620 mOutputRasterDataProvider->setEditable( true );
621 QgsRasterIterator outputIter( mOutputRasterDataProvider.get() );
622 outputIter.startRasterRead( 1, mLayerWidth, mLayerHeight, mExtent );
623
624 int iterLeft = 0;
625 int iterTop = 0;
626 int iterCols = 0;
627 int iterRows = 0;
628 QgsRectangle blockExtent;
629 std::unique_ptr<QgsRasterBlock> outputBlock;
630 while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
631 {
632 std::vector<std::unique_ptr<QgsRasterBlock>> inputBlocks;
633 for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : std::as_const( mInputs ) )
634 {
635 if ( feedback->isCanceled() )
636 break; //in case some slow data sources are loaded
637 for ( int band : i.bands )
638 {
639 if ( feedback->isCanceled() )
640 break; //in case some slow data sources are loaded
641 std::unique_ptr<QgsRasterBlock> b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
642 inputBlocks.emplace_back( std::move( b ) );
643 }
644 }
645
646 feedback->setProgress( mMaxProgressDuringBlockWriting * outputIter.progress( 1 ) );
647 for ( int row = 0; row < iterRows; row++ )
648 {
649 if ( feedback->isCanceled() )
650 break;
651
652 for ( int col = 0; col < iterCols; col++ )
653 {
654 double result = 0;
655 bool noDataInStack = false;
656 std::vector<double> cellValues = QgsRasterAnalysisUtils::getCellValuesFromBlockStack( inputBlocks, row, col, noDataInStack );
657 int cellValueStackSize = cellValues.size();
658
659 if ( noDataInStack && !mIgnoreNoData )
660 {
661 outputBlock->setValue( row, col, mNoDataValue );
662 }
663 else if ( !noDataInStack || ( mIgnoreNoData && cellValueStackSize > 0 ) )
664 {
665 switch ( mMethod )
666 {
667 case QgsRasterAnalysisUtils::InterpolatedPercentRankInc:
668 result = QgsRasterAnalysisUtils::interpolatedPercentRankInc( cellValues, cellValueStackSize, mValue, mNoDataValue );
669 break;
670 case QgsRasterAnalysisUtils::InterpolatedPercentRankExc:
671 result = QgsRasterAnalysisUtils::interpolatedPercentRankExc( cellValues, cellValueStackSize, mValue, mNoDataValue );
672 break;
673 }
674 outputBlock->setValue( row, col, result );
675 }
676 else
677 {
678 //result is NoData if cellValueStack contains no valid values, eg. all cellValues are NoData
679 outputBlock->setValue( row, col, mNoDataValue );
680 }
681 }
682 }
683 if ( !mOutputRasterDataProvider->writeBlock( outputBlock.get(), 1, iterLeft, iterTop ) )
684 {
685 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( mOutputRasterDataProvider->error().summary() ) );
686 }
687 }
688 mOutputRasterDataProvider->setEditable( false );
689}
690
691
692//
693//QgsCellStatisticsPercentRankFromRasterAlgorithm
694//
695QString QgsCellStatisticsPercentRankFromRasterAlgorithm::displayName() const
696{
697 return QObject::tr( "Cell stack percentrank from raster layer" );
698}
699
700QString QgsCellStatisticsPercentRankFromRasterAlgorithm::name() const
701{
702 return u"cellstackpercentrankfromrasterlayer"_s;
703}
704
705QStringList QgsCellStatisticsPercentRankFromRasterAlgorithm::tags() const
706{
707 return QObject::tr( "cell,pixel,statistic,percentrank,rank,percent,value,raster" ).split( ',' );
708}
709
710QString QgsCellStatisticsPercentRankFromRasterAlgorithm::shortHelpString() const
711{
712 return QObject::tr( "This algorithm generates a raster containing the cell-wise percent rank value of a stack of input rasters "
713 "based on an input value raster.\n\n"
714 "At each cell location, the current value of the value raster is used ranked among the respective values in the stack of all overlaid and sorted cell values of the input rasters. "
715 "For values outside of the the stack value distribution, the algorithm returns NoData because the value cannot be ranked among the cell values.\n\n"
716 "There are two methods for percentile calculation:"
717 "<ul> "
718 " <li>Inclusive linearly interpolated percent rank (PERCENTRANK.INC)</li>"
719 " <li>Exclusive linearly interpolated percent rank (PERCENTRANK.EXC)</li>"
720 "</ul> "
721 "The linear interpolation method return the unique percent rank for different values. Both interpolation "
722 "methods follow their counterpart methods implemented by LibreOffice or Microsoft Excel. \n\n"
723 "The output raster's extent and resolution is defined by a reference "
724 "raster. If the input raster layers that do not match the cell size of the reference raster layer will be "
725 "resampled using nearest neighbor resampling. NoData values in any of the input layers will result in a NoData cell output if the Ignore NoData parameter is not set. "
726 "The output raster data type will always be Float32." );
727}
728
729QString QgsCellStatisticsPercentRankFromRasterAlgorithm::shortDescription() const
730{
731 return QObject::tr( "Generates a raster containing the cell-wise percent rank value of a stack of input rasters based on an input value raster." );
732}
733
734QgsCellStatisticsPercentRankFromRasterAlgorithm *QgsCellStatisticsPercentRankFromRasterAlgorithm::createInstance() const
735{
736 return new QgsCellStatisticsPercentRankFromRasterAlgorithm();
737}
738
739void QgsCellStatisticsPercentRankFromRasterAlgorithm::addSpecificAlgorithmParams()
740{
741 addParameter( new QgsProcessingParameterRasterLayer( u"INPUT_VALUE_RASTER"_s, QObject::tr( "Value raster layer" ) ) );
742 addParameter( new QgsProcessingParameterBand( u"VALUE_RASTER_BAND"_s, QObject::tr( "Value raster band" ), 1, u"VALUE_LAYER"_s ) );
743 addParameter( new QgsProcessingParameterEnum( u"METHOD"_s, QObject::tr( "Method" ), QStringList() << QObject::tr( "Inclusive linear interpolation (PERCENTRANK.INC)" ) << QObject::tr( "Exclusive linear interpolation (PERCENTRANK.EXC)" ), false, 0, false ) );
744}
745
746bool QgsCellStatisticsPercentRankFromRasterAlgorithm::prepareSpecificAlgorithmParameters( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
747{
748 Q_UNUSED( feedback )
749 mMethod = static_cast<QgsRasterAnalysisUtils::CellValuePercentRankMethods>( parameterAsEnum( parameters, u"METHOD"_s, context ) );
750
751 QgsRasterLayer *inputValueRaster = parameterAsRasterLayer( parameters, u"INPUT_VALUE_RASTER"_s, context );
752 if ( !inputValueRaster )
753 throw QgsProcessingException( invalidRasterError( parameters, u"INPUT_VALUE_RASTER"_s ) );
754
755 mValueRasterInterface.reset( inputValueRaster->dataProvider()->clone() );
756
757 mValueRasterBand = parameterAsInt( parameters, u"VALUE_RASTER_BAND"_s, context );
758
759 //output data type always defaults to Float32 because result only ranges between 0 and 1
760 mDataType = Qgis::DataType::Float32;
761 return true;
762}
763
764void QgsCellStatisticsPercentRankFromRasterAlgorithm::processRasterStack( QgsProcessingFeedback *feedback )
765{
766 mOutputRasterDataProvider->setEditable( true );
767 QgsRasterIterator outputIter( mOutputRasterDataProvider.get() );
768 outputIter.startRasterRead( 1, mLayerWidth, mLayerHeight, mExtent );
769
770 int iterLeft = 0;
771 int iterTop = 0;
772 int iterCols = 0;
773 int iterRows = 0;
774 QgsRectangle blockExtent;
775 std::unique_ptr<QgsRasterBlock> outputBlock;
776 while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
777 {
778 std::unique_ptr<QgsRasterBlock> valueBlock( mValueRasterInterface->block( mValueRasterBand, blockExtent, iterCols, iterRows ) );
779
780 std::vector<std::unique_ptr<QgsRasterBlock>> inputBlocks;
781 for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : std::as_const( mInputs ) )
782 {
783 if ( feedback->isCanceled() )
784 break; //in case some slow data sources are loaded
785 for ( int band : i.bands )
786 {
787 if ( feedback->isCanceled() )
788 break; //in case some slow data sources are loaded
789 std::unique_ptr<QgsRasterBlock> b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
790 inputBlocks.emplace_back( std::move( b ) );
791 }
792 }
793
794 feedback->setProgress( mMaxProgressDuringBlockWriting * outputIter.progress( 1 ) );
795 for ( int row = 0; row < iterRows; row++ )
796 {
797 if ( feedback->isCanceled() )
798 break;
799
800 for ( int col = 0; col < iterCols; col++ )
801 {
802 bool percentRankValueIsNoData = false;
803 double percentRankValue = valueBlock->valueAndNoData( row, col, percentRankValueIsNoData );
804
805 double result = 0;
806 bool noDataInStack = false;
807 std::vector<double> cellValues = QgsRasterAnalysisUtils::getCellValuesFromBlockStack( inputBlocks, row, col, noDataInStack );
808 int cellValueStackSize = cellValues.size();
809
810 if ( noDataInStack && !mIgnoreNoData && !percentRankValueIsNoData )
811 {
812 outputBlock->setValue( row, col, mNoDataValue );
813 }
814 else if ( !noDataInStack || ( !percentRankValueIsNoData && mIgnoreNoData && cellValueStackSize > 0 ) )
815 {
816 switch ( mMethod )
817 {
818 case QgsRasterAnalysisUtils::InterpolatedPercentRankInc:
819 result = QgsRasterAnalysisUtils::interpolatedPercentRankInc( cellValues, cellValueStackSize, percentRankValue, mNoDataValue );
820 break;
821 case QgsRasterAnalysisUtils::InterpolatedPercentRankExc:
822 result = QgsRasterAnalysisUtils::interpolatedPercentRankExc( cellValues, cellValueStackSize, percentRankValue, mNoDataValue );
823 break;
824 }
825 outputBlock->setValue( row, col, result );
826 }
827 else
828 {
829 //result is NoData if cellValueStack contains no valid values, eg. all cellValues are NoData or percentRankValue is NoData
830 outputBlock->setValue( row, col, mNoDataValue );
831 }
832 }
833 }
834 if ( !mOutputRasterDataProvider->writeBlock( outputBlock.get(), 1, iterLeft, iterTop ) )
835 {
836 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( mOutputRasterDataProvider->error().summary() ) );
837 }
838 }
839 mOutputRasterDataProvider->setEditable( false );
840}
841
@ Raster
Raster layers.
Definition qgis.h:3608
DataType
Raster data types.
Definition qgis.h:379
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:387
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:381
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:386
@ Raster
Raster layer.
Definition qgis.h:195
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3835
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3834
@ Double
Double/float values.
Definition qgis.h:3875
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:55
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:63
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 raster band parameter for Processing algorithms.
A boolean parameter for processing algorithms.
An enum based parameter for processing algorithms, allowing for selection from predefined values.
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 ...
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:7458