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