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