QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
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 #include "qgsrasterprojector.h"
20 #include "qgsrasterfilewriter.h"
21 #include "qgsrasteranalysisutils.h"
22 
24 
25 QString QgsCellStatisticsAlgorithm::displayName() const
26 {
27  return QObject::tr( "Cell statistics" );
28 }
29 
30 QString QgsCellStatisticsAlgorithm::name() const
31 {
32  return QObject::tr( "cellstatistics" );
33 }
34 
35 QStringList QgsCellStatisticsAlgorithm::tags() const
36 {
37  return QObject::tr( "cell,pixel,statistic,count,mean,sum,majority,minority,variance,variety,range,median,minimum,maximum" ).split( ',' );
38 }
39 
40 QString QgsCellStatisticsAlgorithm::group() const
41 {
42  return QObject::tr( "Raster analysis" );
43 }
44 
45 QString QgsCellStatisticsAlgorithm::groupId() const
46 {
47  return QStringLiteral( "rasteranalysis" );
48 }
49 
50 QString QgsCellStatisticsAlgorithm::shortHelpString() const
51 {
52  return QObject::tr( "The Cell statistics algorithm computes a value for each cell of the "
53  "output raster. At each cell location, "
54  "the output value is defined as a function of all overlaid cell values of the "
55  "input rasters.\n\n"
56  "The output raster's extent and resolution is defined by a reference "
57  "raster. The following functions can be applied on the input "
58  "raster cells per output raster cell location:\n"
59  "<ul> "
60  " <li>Sum</li>"
61  " <li>Count</li>"
62  " <li>Mean</li>"
63  " <li>Median</li>"
64  " <li>Standard deviation</li>"
65  " <li>Variance</li>"
66  " <li>Minimum</li>"
67  " <li>Maximum</li>"
68  " <li>Minority (least frequent value)</li>"
69  " <li>Majority (most frequent value)</li>"
70  " <li>Range (max-min)</li>"
71  " <li>Variety (count of unique values)</li>"
72  "</ul> "
73  "Input raster layers that do not match the cell size of the reference raster layer will be "
74  "resampled using nearest neighbor resampling. The output raster data type will be set to "
75  "the most complex data type present in the input datasets except when using the functions "
76  "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"
77  "<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"
78  "<i>Calculation details - Count:</i> Count will always result in the number of cells without NoData values at the current cell location.\n"
79  "<i>Calculation details - Median:</i> If the number of input layers is even, the median will be calculated as the "
80  "arithmetic mean of the two middle values of the ordered cell input values.\n"
81  "<i>Calculation details - Minority/Majority:</i> If no unique minority or majority could be found, the result is NoData, except all "
82  "input cell values are equal." );
83 }
84 
85 QgsCellStatisticsAlgorithm *QgsCellStatisticsAlgorithm::createInstance() const
86 {
87  return new QgsCellStatisticsAlgorithm();
88 }
89 
90 void QgsCellStatisticsAlgorithm::initAlgorithm( const QVariantMap & )
91 {
92  addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "INPUT" ),
93  QObject::tr( "Input layers" ), QgsProcessing::TypeRaster ) );
94 
95  QStringList statistics = QStringList();
96  statistics << QObject::tr( "Sum" )
97  << QObject::tr( "Count" )
98  << QObject::tr( "Mean" )
99  << QObject::tr( "Median" )
100  << QObject::tr( "Standard deviation" )
101  << QObject::tr( "Variance" )
102  << QObject::tr( "Minimum" )
103  << QObject::tr( "Maximum" )
104  << QObject::tr( "Minority" )
105  << QObject::tr( "Majority" )
106  << QObject::tr( "Range" )
107  << QObject::tr( "Variety" );
108 
109  addParameter( new QgsProcessingParameterEnum( QStringLiteral( "STATISTIC" ), QObject::tr( "Statistic" ), statistics, false, 0, false ) );
110  addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "IGNORE_NODATA" ), QObject::tr( "Ignore NoData values" ), true ) );
111 
112  addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "REFERENCE_LAYER" ), QObject::tr( "Reference layer" ) ) );
113 
114  std::unique_ptr< QgsProcessingParameterNumber > output_nodata_parameter = qgis::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "OUTPUT_NODATA_VALUE" ), QObject::tr( "Output NoData value" ), QgsProcessingParameterNumber::Double, -9999, true );
115  output_nodata_parameter->setFlags( output_nodata_parameter->flags() | QgsProcessingParameterDefinition::FlagAdvanced );
116  addParameter( output_nodata_parameter.release() );
117 
118  addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ),
119  QObject::tr( "Output layer" ) ) );
120 
121  addOutput( new QgsProcessingOutputString( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ) ) );
122  addOutput( new QgsProcessingOutputString( QStringLiteral( "CRS_AUTHID" ), QObject::tr( "CRS authority identifier" ) ) );
123  addOutput( new QgsProcessingOutputNumber( QStringLiteral( "WIDTH_IN_PIXELS" ), QObject::tr( "Width in pixels" ) ) );
124  addOutput( new QgsProcessingOutputNumber( QStringLiteral( "HEIGHT_IN_PIXELS" ), QObject::tr( "Height in pixels" ) ) );
125  addOutput( new QgsProcessingOutputNumber( QStringLiteral( "TOTAL_PIXEL_COUNT" ), QObject::tr( "Total pixel count" ) ) );
126 }
127 
128 bool QgsCellStatisticsAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
129 {
130  QgsRasterLayer *referenceLayer = parameterAsRasterLayer( parameters, QStringLiteral( "REFERENCE_LAYER" ), context );
131  if ( !referenceLayer )
132  throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "REFERENCE_LAYER" ) ) );
133 
134  mIgnoreNoData = parameterAsBool( parameters, QStringLiteral( "IGNORE_NODATA" ), context );
135  mNoDataValue = parameterAsDouble( parameters, QStringLiteral( "OUTPUT_NODATA_VALUE" ), context );
136  mCrs = referenceLayer->crs();
137  mRasterUnitsPerPixelX = referenceLayer->rasterUnitsPerPixelX();
138  mRasterUnitsPerPixelY = referenceLayer->rasterUnitsPerPixelY();
139  mLayerWidth = referenceLayer->width();
140  mLayerHeight = referenceLayer->height();
141  mExtent = referenceLayer->extent();
142 
143  const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "INPUT" ), context );
144  QList< QgsRasterLayer * > rasterLayers;
145  rasterLayers.reserve( layers.count() );
146  for ( QgsMapLayer *l : layers )
147  {
148  if ( feedback->isCanceled() )
149  break; //in case some slow data sources are loaded
150 
151  if ( l->type() == QgsMapLayerType::RasterLayer )
152  {
153  QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( l );
154  QgsRasterAnalysisUtils::RasterLogicInput input;
155  const int band = 1; //could be made dynamic
156  input.hasNoDataValue = layer->dataProvider()->sourceHasNoDataValue( band );
157  input.sourceDataProvider.reset( layer->dataProvider()->clone() );
158  input.interface = input.sourceDataProvider.get();
159  // add projector if necessary
160  if ( layer->crs() != mCrs )
161  {
162  input.projector = qgis::make_unique< QgsRasterProjector >();
163  input.projector->setInput( input.sourceDataProvider.get() );
164  input.projector->setCrs( layer->crs(), mCrs, context.transformContext() );
165  input.interface = input.projector.get();
166  }
167  mInputs.emplace_back( std::move( input ) );
168  }
169  }
170 
171  return true;
172 }
173 
174 QVariantMap QgsCellStatisticsAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
175 {
176  //obtain statistic method
177  QgsRasterAnalysisUtils::CellValueStatisticMethods method = static_cast<QgsRasterAnalysisUtils::CellValueStatisticMethods>( parameterAsEnum( parameters, QStringLiteral( "STATISTIC" ), context ) );
178 
179  //determine output raster data type
180  //initially raster data type to most primitive data type that is possible
181  mDataType = Qgis::Byte;
182  for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : mInputs )
183  {
184  for ( int band : i.bands )
185  {
186  Qgis::DataType inputDataType = i.interface->dataType( band );
187  if ( static_cast<int>( mDataType ) < static_cast<int>( inputDataType ) )
188  mDataType = inputDataType; //if raster data type is more potent, set it as new data type
189  }
190  }
191 
192  //force data types on specific functions if input data types don't match
193  if ( method == QgsRasterAnalysisUtils::Mean || method == QgsRasterAnalysisUtils::StandardDeviation || method == QgsRasterAnalysisUtils::Variance )
194  {
195  if ( static_cast<int>( mDataType ) < 6 )
196  mDataType = Qgis::Float32; //force float on mean and stddev if all inputs are integer
197  }
198  else if ( method == QgsRasterAnalysisUtils::Count || method == QgsRasterAnalysisUtils::Variety ) //count, variety
199  {
200  if ( static_cast<int>( mDataType ) > 5 ) //if is floating point type
201  mDataType = Qgis::Int32; //force integer on variety if all inputs are float or complex
202  }
203 
204  const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
205  QFileInfo fi( outputFile );
206  const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
207 
208  std::unique_ptr< QgsRasterFileWriter > writer = qgis::make_unique< QgsRasterFileWriter >( outputFile );
209  writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
210  writer->setOutputFormat( outputFormat );
211  std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( mDataType, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
212  if ( !provider )
213  throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
214  if ( !provider->isValid() )
215  throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
216 
217  provider->setNoDataValue( 1, mNoDataValue );
218  qgssize layerSize = static_cast< qgssize >( mLayerWidth ) * static_cast< qgssize >( mLayerHeight );
219 
222  int nbBlocksWidth = static_cast< int>( std::ceil( 1.0 * mLayerWidth / maxWidth ) );
223  int nbBlocksHeight = static_cast< int >( std::ceil( 1.0 * mLayerHeight / maxHeight ) );
224  int nbBlocks = nbBlocksWidth * nbBlocksHeight;
225  provider->setEditable( true );
226  QgsRasterIterator outputIter( provider.get() );
227  outputIter.startRasterRead( 1, mLayerWidth, mLayerHeight, mExtent );
228 
229  int iterLeft = 0;
230  int iterTop = 0;
231  int iterCols = 0;
232  int iterRows = 0;
233  QgsRectangle blockExtent;
234  std::unique_ptr< QgsRasterBlock > outputBlock;
235  while ( outputIter.readNextRasterPart( 1, iterCols, iterRows, outputBlock, iterLeft, iterTop, &blockExtent ) )
236  {
237  std::vector< std::unique_ptr< QgsRasterBlock > > inputBlocks;
238  for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : mInputs )
239  {
240  if ( feedback->isCanceled() )
241  break; //in case some slow data sources are loaded
242  for ( int band : i.bands )
243  {
244  if ( feedback->isCanceled() )
245  break; //in case some slow data sources are loaded
246  std::unique_ptr< QgsRasterBlock > b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
247  inputBlocks.emplace_back( std::move( b ) );
248  }
249  }
250 
251  feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
252  for ( int row = 0; row < iterRows; row++ )
253  {
254  if ( feedback->isCanceled() )
255  break;
256 
257  for ( int col = 0; col < iterCols; col++ )
258  {
259  double result = 0;
260  bool noDataInStack = false;
261  std::vector<double> cellValues = QgsRasterAnalysisUtils::getCellValuesFromBlockStack( inputBlocks, row, col, noDataInStack );
262  int cellValueStackSize = cellValues.size();
263 
264  if ( noDataInStack && !mIgnoreNoData )
265  {
266  //output cell will always be NoData if NoData occurs in cellValueStack and NoData is not ignored
267  //this saves unnecessary iterations on the cellValueStack
268  if ( method == QgsRasterAnalysisUtils::Count )
269  outputBlock->setValue( row, col, cellValueStackSize );
270  else
271  {
272  outputBlock->setValue( row, col, mNoDataValue );
273  }
274  }
275  else if ( !noDataInStack || mIgnoreNoData )
276  {
277  switch ( method )
278  {
279  case QgsRasterAnalysisUtils::Sum:
280  result = std::accumulate( cellValues.begin(), cellValues.end(), 0.0 );
281  break;
282  case QgsRasterAnalysisUtils::Count:
283  result = cellValueStackSize;
284  break;
285  case QgsRasterAnalysisUtils::Mean:
286  result = QgsRasterAnalysisUtils::meanFromCellValues( cellValues, cellValueStackSize );
287  break;
288  case QgsRasterAnalysisUtils::Median:
289  result = QgsRasterAnalysisUtils::medianFromCellValues( cellValues, cellValueStackSize );
290  break;
291  case QgsRasterAnalysisUtils::StandardDeviation:
292  result = QgsRasterAnalysisUtils::stddevFromCellValues( cellValues, cellValueStackSize );
293  break;
294  case QgsRasterAnalysisUtils::Variance:
295  result = QgsRasterAnalysisUtils::varianceFromCellValues( cellValues, cellValueStackSize );
296  break;
297  case QgsRasterAnalysisUtils::Minimum:
298  result = QgsRasterAnalysisUtils::minimumFromCellValues( cellValues );
299  break;
300  case QgsRasterAnalysisUtils::Maximum:
301  result = QgsRasterAnalysisUtils::maximumFromCellValues( cellValues );
302  break;
303  case QgsRasterAnalysisUtils::Minority:
304  result = QgsRasterAnalysisUtils::minorityFromCellValues( cellValues, mNoDataValue, cellValueStackSize );
305  break;
306  case QgsRasterAnalysisUtils::Majority:
307  result = QgsRasterAnalysisUtils::majorityFromCellValues( cellValues, mNoDataValue, cellValueStackSize );
308  break;
309  case QgsRasterAnalysisUtils::Range:
310  result = QgsRasterAnalysisUtils::rangeFromCellValues( cellValues );
311  break;
312  case QgsRasterAnalysisUtils::Variety:
313  result = QgsRasterAnalysisUtils::varietyFromCellValues( cellValues );
314  break;
315  }
316  outputBlock->setValue( row, col, result );
317  }
318  }
319  }
320  provider->writeBlock( outputBlock.get(), 1, iterLeft, iterTop );
321  }
322  provider->setEditable( false );
323 
324  QVariantMap outputs;
325  outputs.insert( QStringLiteral( "EXTENT" ), mExtent.toString() );
326  outputs.insert( QStringLiteral( "CRS_AUTHID" ), mCrs.authid() );
327  outputs.insert( QStringLiteral( "WIDTH_IN_PIXELS" ), mLayerWidth );
328  outputs.insert( QStringLiteral( "HEIGHT_IN_PIXELS" ), mLayerHeight );
329  outputs.insert( QStringLiteral( "TOTAL_PIXEL_COUNT" ), layerSize );
330  outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
331 
332  return outputs;
333 }
334 
335 
336 
338 
339 
QgsMapLayer::crs
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:89
QgsFeedback::setProgress
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition: qgsfeedback.h:62
qgsrasterprojector.h
Qgis::Float32
@ Float32
Thirty two bit floating point (float)
Definition: qgis.h:109
QgsProcessingParameterNumber::Double
@ Double
Double/float values.
Definition: qgsprocessingparameters.h:1967
Qgis::DataType
DataType
Raster data types.
Definition: qgis.h:102
QgsProcessingFeedback
Base class for providing feedback from a processing algorithm.
Definition: qgsprocessingfeedback.h:38
QgsRasterFileWriter::driverForExtension
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
Definition: qgsrasterfilewriter.cpp:1063
QgsProcessingParameterRasterDestination
A raster layer destination parameter, for specifying the destination path for a raster layer created ...
Definition: qgsprocessingparameters.h:3066
QgsProcessingParameterDefinition::FlagAdvanced
@ FlagAdvanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition: qgsprocessingparameters.h:423
qgsrasteranalysisutils.h
QgsProcessingParameterMultipleLayers
A parameter for processing algorithms which accepts multiple map layers.
Definition: qgsprocessingparameters.h:1878
QgsRectangle
A rectangle specified with double values.
Definition: qgsrectangle.h:42
QgsProcessingOutputNumber
A numeric output for processing algorithms.
Definition: qgsprocessingoutputs.h:295
qgsrasterfilewriter.h
QgsRasterDataProvider::clone
QgsRasterDataProvider * clone() const override=0
Clone itself, create deep copy.
QgsRasterDataProvider::sourceHasNoDataValue
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
Definition: qgsrasterdataprovider.h:246
qgsalgorithmcellstatistics.h
QgsRasterLayer::width
int width() const
Returns the width of the (unclipped) raster.
Definition: qgsrasterlayer.cpp:2448
QgsProcessingContext
Contains information about the context in which a processing algorithm is executed.
Definition: qgsprocessingcontext.h:44
QgsRasterLayer::height
int height() const
Returns the height of the (unclipped) raster.
Definition: qgsrasterlayer.cpp:2454
QgsMapLayer::extent
virtual QgsRectangle extent() const
Returns the extent of the layer.
Definition: qgsmaplayer.cpp:197
QgsProcessing::TypeRaster
@ TypeRaster
Raster layers.
Definition: qgsprocessing.h:51
QgsMapLayerType::RasterLayer
@ RasterLayer
QgsRasterIterator::DEFAULT_MAXIMUM_TILE_HEIGHT
static const int DEFAULT_MAXIMUM_TILE_HEIGHT
Default maximum tile height.
Definition: qgsrasteriterator.h:151
QgsRasterLayer::dataProvider
QgsRasterDataProvider * dataProvider() override
Returns the source data provider.
Definition: qgsrasterlayer.cpp:234
QgsProcessingParameterRasterLayer
A raster layer parameter for processing algorithms.
Definition: qgsprocessingparameters.h:2223
QgsProcessingOutputString
A string output for processing algorithms.
Definition: qgsprocessingoutputs.h:317
QgsProcessingContext::transformContext
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
Definition: qgsprocessingcontext.h:149
QgsRasterLayer
Represents a raster layer.
Definition: qgsrasterlayer.h:71
QgsRasterIterator
Iterator for sequentially processing raster cells.
Definition: qgsrasteriterator.h:35
QgsRasterLayer::rasterUnitsPerPixelY
double rasterUnitsPerPixelY() const
Returns the number of raster units per each raster pixel in Y axis.
Definition: qgsrasterlayer.cpp:583
QgsFeedback::isCanceled
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition: qgsfeedback.h:53
QgsProcessingParameterBoolean
A boolean parameter for processing algorithms.
Definition: qgsprocessingparameters.h:1507
QgsMapLayer
Base class for all map layer types.
Definition: qgsmaplayer.h:83
QgsRasterIterator::DEFAULT_MAXIMUM_TILE_WIDTH
static const int DEFAULT_MAXIMUM_TILE_WIDTH
Default maximum tile width.
Definition: qgsrasteriterator.h:148
QgsErrorMessage::Text
@ Text
Definition: qgserror.h:38
Qgis::Int32
@ Int32
Thirty two bit signed integer (qint32)
Definition: qgis.h:108
QgsProcessingParameterEnum
An enum based parameter for processing algorithms, allowing for selection from predefined values.
Definition: qgsprocessingparameters.h:2256
Qgis::Byte
@ Byte
Eight bit unsigned integer (quint8)
Definition: qgis.h:104
QgsProcessingException
Custom exception class for processing related exceptions.
Definition: qgsexception.h:83
QgsRasterLayer::rasterUnitsPerPixelX
double rasterUnitsPerPixelX() const
Returns the number of raster units per each raster pixel in X axis.
Definition: qgsrasterlayer.cpp:567
qgssize
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:768