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