QGIS API Documentation 3.99.0-Master (2fe06baccd8)
Loading...
Searching...
No Matches
qgsalgorithmrasterfrequencybycomparisonoperator.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmrasterfrequencybycomparisonoperator.cpp
3 ---------------------
4 begin : June 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
25
26//
27//QgsRasterFrequencyByComparisonOperatorBase
28//
29
30QString QgsRasterFrequencyByComparisonOperatorBase::group() const
31{
32 return QObject::tr( "Raster analysis" );
33}
34
35QString QgsRasterFrequencyByComparisonOperatorBase::groupId() const
36{
37 return QStringLiteral( "rasteranalysis" );
38}
39
40void QgsRasterFrequencyByComparisonOperatorBase::initAlgorithm( const QVariantMap & )
41{
42 addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "INPUT_VALUE_RASTER" ), QObject::tr( "Input value raster" ) ) );
43 addParameter( new QgsProcessingParameterBand( QStringLiteral( "INPUT_VALUE_RASTER_BAND" ), QObject::tr( "Value raster band" ), 1, QStringLiteral( "INPUT_VALUE_RASTER" ) ) );
44 addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "INPUT_RASTERS" ), QObject::tr( "Input raster layers" ), Qgis::ProcessingSourceType::Raster ) );
45 addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "IGNORE_NODATA" ), QObject::tr( "Ignore NoData values" ), false ) );
46
47 auto output_nodata_parameter = std::make_unique<QgsProcessingParameterNumber>( QStringLiteral( "OUTPUT_NODATA_VALUE" ), QObject::tr( "Output NoData value" ), Qgis::ProcessingNumberParameterType::Double, -9999, true );
48 output_nodata_parameter->setFlags( output_nodata_parameter->flags() | Qgis::ProcessingParameterFlag::Advanced );
49 addParameter( output_nodata_parameter.release() );
50
51 // backwards compatibility parameter
52 // TODO QGIS 4: remove parameter and related logic
53 auto createOptsParam = std::make_unique<QgsProcessingParameterString>( QStringLiteral( "CREATE_OPTIONS" ), QObject::tr( "Creation options" ), QVariant(), false, true );
54 createOptsParam->setMetadata( QVariantMap( { { QStringLiteral( "widget_wrapper" ), QVariantMap( { { QStringLiteral( "widget_type" ), QStringLiteral( "rasteroptions" ) } } ) } } ) );
55 createOptsParam->setFlags( createOptsParam->flags() | Qgis::ProcessingParameterFlag::Hidden );
56 addParameter( createOptsParam.release() );
57
58 auto creationOptsParam = std::make_unique<QgsProcessingParameterString>( QStringLiteral( "CREATION_OPTIONS" ), QObject::tr( "Creation options" ), QVariant(), false, true );
59 creationOptsParam->setMetadata( QVariantMap( { { QStringLiteral( "widget_wrapper" ), QVariantMap( { { QStringLiteral( "widget_type" ), QStringLiteral( "rasteroptions" ) } } ) } } ) );
60 creationOptsParam->setFlags( creationOptsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
61 addParameter( creationOptsParam.release() );
62
63 addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ), QObject::tr( "Output layer" ) ) );
64 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "OCCURRENCE_COUNT" ), QObject::tr( "Count of value occurrences" ) ) );
65 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "FOUND_LOCATIONS_COUNT" ), QObject::tr( "Count of cells with equal value occurrences" ) ) );
66 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "MEAN_FREQUENCY_PER_LOCATION" ), QObject::tr( "Mean frequency at valid cell locations" ) ) );
67 addOutput( new QgsProcessingOutputString( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ) ) );
68 addOutput( new QgsProcessingOutputString( QStringLiteral( "CRS_AUTHID" ), QObject::tr( "CRS authority identifier" ) ) );
69 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "WIDTH_IN_PIXELS" ), QObject::tr( "Width in pixels" ) ) );
70 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "HEIGHT_IN_PIXELS" ), QObject::tr( "Height in pixels" ) ) );
71 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "TOTAL_PIXEL_COUNT" ), QObject::tr( "Total pixel count" ) ) );
72}
73
74bool QgsRasterFrequencyByComparisonOperatorBase::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
75{
76 QgsRasterLayer *inputValueRaster = parameterAsRasterLayer( parameters, QStringLiteral( "INPUT_VALUE_RASTER" ), context );
77 if ( !inputValueRaster )
78 throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "INPUT_VALUE_RASTER" ) ) );
79
80 mInputValueRasterBand = parameterAsInt( parameters, QStringLiteral( "INPUT_VALUE_RASTER_BAND" ), context );
81 mIgnoreNoData = parameterAsBool( parameters, QStringLiteral( "IGNORE_NODATA" ), context );
82
83 mInputValueRasterInterface.reset( inputValueRaster->dataProvider()->clone() );
84 mNoDataValue = parameterAsDouble( parameters, QStringLiteral( "OUTPUT_NODATA_VALUE" ), context );
85 mCrs = inputValueRaster->crs();
86 mRasterUnitsPerPixelX = inputValueRaster->rasterUnitsPerPixelX();
87 mRasterUnitsPerPixelY = inputValueRaster->rasterUnitsPerPixelY();
88 mLayerWidth = inputValueRaster->width();
89 mLayerHeight = inputValueRaster->height();
90 mExtent = inputValueRaster->extent();
91
92 const QList<QgsMapLayer *> layers = parameterAsLayerList( parameters, QStringLiteral( "INPUT_RASTERS" ), context );
93 QList<QgsRasterLayer *> rasterLayers;
94 rasterLayers.reserve( layers.count() );
95 for ( QgsMapLayer *l : layers )
96 {
97 if ( feedback->isCanceled() )
98 break; //in case some slow data sources are loaded
99
100 if ( l->type() == Qgis::LayerType::Raster )
101 {
102 QgsRasterLayer *layer = qobject_cast<QgsRasterLayer *>( l );
103 QgsRasterAnalysisUtils::RasterLogicInput input;
104 const int band = 1; //could be made dynamic
105 input.hasNoDataValue = layer->dataProvider()->sourceHasNoDataValue( band );
106 input.sourceDataProvider.reset( layer->dataProvider()->clone() );
107 input.interface = input.sourceDataProvider.get();
108 // add projector if necessary
109 if ( layer->crs() != mCrs )
110 {
111 input.projector = std::make_unique<QgsRasterProjector>();
112 input.projector->setInput( input.sourceDataProvider.get() );
113 input.projector->setCrs( layer->crs(), mCrs, context.transformContext() );
114 input.interface = input.projector.get();
115 }
116 mInputs.emplace_back( std::move( input ) );
117 }
118 }
119
120 return true;
121}
122
123QVariantMap QgsRasterFrequencyByComparisonOperatorBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
124{
125 QString creationOptions = parameterAsString( parameters, QStringLiteral( "CREATION_OPTIONS" ), context ).trimmed();
126 // handle backwards compatibility parameter CREATE_OPTIONS
127 const QString optionsString = parameterAsString( parameters, QStringLiteral( "CREATE_OPTIONS" ), context );
128 if ( !optionsString.isEmpty() )
129 creationOptions = optionsString;
130
131 const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
132 const QFileInfo fi( outputFile );
133 const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
134
135 auto writer = std::make_unique<QgsRasterFileWriter>( outputFile );
136 writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
137 if ( !creationOptions.isEmpty() )
138 {
139 writer->setCreationOptions( creationOptions.split( '|' ) );
140 }
141 writer->setOutputFormat( outputFormat );
142 std::unique_ptr<QgsRasterDataProvider> provider( writer->createOneBandRaster( Qgis::DataType::Int32, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
143 if ( !provider )
144 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
145 if ( !provider->isValid() )
146 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
147
148 provider->setNoDataValue( 1, mNoDataValue );
149 const qgssize layerSize = static_cast<qgssize>( mLayerWidth ) * static_cast<qgssize>( mLayerHeight );
150
153 const int nbBlocksWidth = static_cast<int>( std::ceil( 1.0 * mLayerWidth / maxWidth ) );
154 const int nbBlocksHeight = static_cast<int>( std::ceil( 1.0 * mLayerHeight / maxHeight ) );
155 const int nbBlocks = nbBlocksWidth * nbBlocksHeight;
156 provider->setEditable( true );
157
158 QgsRasterIterator iter( mInputValueRasterInterface.get() );
159 iter.startRasterRead( mInputValueRasterBand, mLayerWidth, mLayerHeight, mExtent );
160 int iterLeft = 0;
161 int iterTop = 0;
162 int iterCols = 0;
163 int iterRows = 0;
164 QgsRectangle blockExtent;
165
166 unsigned long long occurrenceCount = 0;
167 unsigned long long noDataLocationsCount = 0;
168 std::unique_ptr<QgsRasterBlock> inputBlock;
169 while ( iter.readNextRasterPart( 1, iterCols, iterRows, inputBlock, iterLeft, iterTop, &blockExtent ) )
170 {
171 std::vector<std::unique_ptr<QgsRasterBlock>> inputBlocks;
172 for ( const QgsRasterAnalysisUtils::RasterLogicInput &i : mInputs )
173 {
174 if ( feedback->isCanceled() )
175 break; //in case some slow data sources are loaded
176 for ( const int band : i.bands )
177 {
178 if ( feedback->isCanceled() )
179 break; //in case some slow data sources are loaded
180 std::unique_ptr<QgsRasterBlock> b( i.interface->block( band, blockExtent, iterCols, iterRows ) );
181 inputBlocks.emplace_back( std::move( b ) );
182 }
183 }
184
185 auto outputBlock = std::make_unique<QgsRasterBlock>( Qgis::DataType::Int32, iterCols, iterRows );
186 feedback->setProgress( 100 * ( ( iterTop / maxHeight * nbBlocksWidth ) + iterLeft / maxWidth ) / nbBlocks );
187 for ( int row = 0; row < iterRows; row++ )
188 {
189 if ( feedback->isCanceled() )
190 break;
191
192 for ( int col = 0; col < iterCols; col++ )
193 {
194 bool valueRasterCellIsNoData = false;
195 const double value = inputBlock->valueAndNoData( row, col, valueRasterCellIsNoData );
196
197 if ( valueRasterCellIsNoData && !mIgnoreNoData )
198 {
199 //output cell will always be NoData if NoData occurs in valueRaster or cellValueStack and NoData is not ignored
200 //this saves unnecessary iterations on the cellValueStack
201 outputBlock->setValue( row, col, mNoDataValue );
202 noDataLocationsCount++;
203 }
204 else
205 {
206 bool noDataInStack = false;
207 const std::vector<double> cellValues = QgsRasterAnalysisUtils::getCellValuesFromBlockStack( inputBlocks, row, col, noDataInStack );
208
209 if ( noDataInStack && !mIgnoreNoData )
210 {
211 outputBlock->setValue( row, col, mNoDataValue );
212 noDataLocationsCount++;
213 }
214 else
215 {
216 const int frequency = applyComparisonOperator( value, cellValues );
217 outputBlock->setValue( row, col, frequency );
218 occurrenceCount += frequency;
219 }
220 }
221 }
222 }
223 if ( !provider->writeBlock( outputBlock.get(), 1, iterLeft, iterTop ) )
224 {
225 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( provider->error().summary() ) );
226 }
227 }
228 provider->setEditable( false );
229
230 const unsigned long long foundLocationsCount = layerSize - noDataLocationsCount;
231 const double meanEqualCountPerValidLocation = static_cast<double>( occurrenceCount ) / static_cast<double>( foundLocationsCount * mInputs.size() );
232
233 QVariantMap outputs;
234 outputs.insert( QStringLiteral( "OCCURRENCE_COUNT" ), occurrenceCount );
235 outputs.insert( QStringLiteral( "FOUND_LOCATIONS_COUNT" ), foundLocationsCount );
236 outputs.insert( QStringLiteral( "MEAN_FREQUENCY_PER_LOCATION" ), meanEqualCountPerValidLocation );
237 outputs.insert( QStringLiteral( "EXTENT" ), mExtent.toString() );
238 outputs.insert( QStringLiteral( "CRS_AUTHID" ), mCrs.authid() );
239 outputs.insert( QStringLiteral( "WIDTH_IN_PIXELS" ), mLayerWidth );
240 outputs.insert( QStringLiteral( "HEIGHT_IN_PIXELS" ), mLayerHeight );
241 outputs.insert( QStringLiteral( "TOTAL_PIXEL_COUNT" ), layerSize );
242 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
243
244 return outputs;
245}
246
247//
248// QgsRasterFrequencyByEqualOperatorAlgorithm
249//
250
251QString QgsRasterFrequencyByEqualOperatorAlgorithm::displayName() const
252{
253 return QObject::tr( "Equal to frequency" );
254}
255
256QString QgsRasterFrequencyByEqualOperatorAlgorithm::name() const
257{
258 return QStringLiteral( "equaltofrequency" );
259}
260
261QStringList QgsRasterFrequencyByEqualOperatorAlgorithm::tags() const
262{
263 return QObject::tr( "cell,equal,frequency,pixel,stack" ).split( ',' );
264}
265
266QString QgsRasterFrequencyByEqualOperatorAlgorithm::shortHelpString() const
267{
268 return QObject::tr( "This algorithm evaluates on a cell-by-cell basis the frequency "
269 "(number of times) the values of an input stack of rasters are equal "
270 "to the value of a value raster. \n "
271 "If multiband rasters are used in the data raster stack, the algorithm will always "
272 "perform the analysis on the first band of the rasters - use GDAL to use other bands in the analysis. "
273 "The input value layer serves as reference layer for the sample layers. "
274 "Any NoData cells in the value raster or the data layer stack will result in a NoData cell "
275 "in the output raster if the ignore NoData parameter is not checked. "
276 "The output NoData value can be set manually. The output rasters extent and resolution "
277 "is defined by the input raster layer and is always of int32 type." );
278}
279
280QString QgsRasterFrequencyByEqualOperatorAlgorithm::shortDescription() const
281{
282 return QObject::tr( "Evaluates on a cell-by-cell basis the frequency (number of times) "
283 "the values of an input stack of rasters are equal to the value of a value raster." );
284}
285
286QgsRasterFrequencyByEqualOperatorAlgorithm *QgsRasterFrequencyByEqualOperatorAlgorithm::createInstance() const
287{
288 return new QgsRasterFrequencyByEqualOperatorAlgorithm();
289}
290
291int QgsRasterFrequencyByEqualOperatorAlgorithm::applyComparisonOperator( double searchValue, std::vector<double> cellValueStack )
292{
293 return static_cast<int>( std::count( cellValueStack.begin(), cellValueStack.end(), searchValue ) );
294}
295
296//
297// QgsRasterFrequencyByGreaterThanOperatorAlgorithm
298//
299
300QString QgsRasterFrequencyByGreaterThanOperatorAlgorithm::displayName() const
301{
302 return QObject::tr( "Greater than frequency" );
303}
304
305QString QgsRasterFrequencyByGreaterThanOperatorAlgorithm::name() const
306{
307 return QStringLiteral( "greaterthanfrequency" );
308}
309
310QStringList QgsRasterFrequencyByGreaterThanOperatorAlgorithm::tags() const
311{
312 return QObject::tr( "cell,greater,frequency,pixel,stack" ).split( ',' );
313}
314
315QString QgsRasterFrequencyByGreaterThanOperatorAlgorithm::shortHelpString() const
316{
317 return QObject::tr( "This algorithm evaluates on a cell-by-cell basis the frequency "
318 "(number of times) the values of an input stack of rasters are greater than "
319 "the value of a value raster. \n "
320 "If multiband rasters are used in the data raster stack, the algorithm will always "
321 "perform the analysis on the first band of the rasters - use GDAL to use other bands in the analysis. "
322 "The input value layer serves as reference layer for the sample layers. "
323 "Any NoData cells in the value raster or the data layer stack will result in a NoData cell "
324 "in the output raster if the ignore NoData parameter is not checked. "
325 "The output NoData value can be set manually. The output rasters extent and resolution "
326 "is defined by the input raster layer and is always of int32 type." );
327}
328
329QString QgsRasterFrequencyByGreaterThanOperatorAlgorithm::shortDescription() const
330{
331 return QObject::tr( "Evaluates on a cell-by-cell basis the frequency (number of times) "
332 "the values of an input stack of rasters are greater than the value of a value raster." );
333}
334
335QgsRasterFrequencyByGreaterThanOperatorAlgorithm *QgsRasterFrequencyByGreaterThanOperatorAlgorithm::createInstance() const
336{
337 return new QgsRasterFrequencyByGreaterThanOperatorAlgorithm();
338}
339
340int QgsRasterFrequencyByGreaterThanOperatorAlgorithm::applyComparisonOperator( double searchValue, std::vector<double> cellValueStack )
341{
342 return static_cast<int>( std::count_if( cellValueStack.begin(), cellValueStack.end(), [&]( double const &stackValue ) { return stackValue > searchValue; } ) );
343}
344
345//
346// QgsRasterFrequencyByLessThanOperatorAlgorithm
347//
348
349QString QgsRasterFrequencyByLessThanOperatorAlgorithm::displayName() const
350{
351 return QObject::tr( "Less than frequency" );
352}
353
354QString QgsRasterFrequencyByLessThanOperatorAlgorithm::name() const
355{
356 return QStringLiteral( "lessthanfrequency" );
357}
358
359QStringList QgsRasterFrequencyByLessThanOperatorAlgorithm::tags() const
360{
361 return QObject::tr( "cell,less,lower,frequency,pixel,stack" ).split( ',' );
362}
363
364QString QgsRasterFrequencyByLessThanOperatorAlgorithm::shortHelpString() const
365{
366 return QObject::tr( "This algorithm evaluates on a cell-by-cell basis the frequency "
367 "(number of times) the values of an input stack of rasters are less than "
368 "the value of a value raster. \n "
369 "If multiband rasters are used in the data raster stack, the algorithm will always "
370 "perform the analysis on the first band of the rasters - use GDAL to use other bands in the analysis. "
371 "The input value layer serves as reference layer for the sample layers. "
372 "Any NoData cells in the value raster or the data layer stack will result in a NoData cell "
373 "in the output raster if the ignore NoData parameter is not checked. "
374 "The output NoData value can be set manually. The output rasters extent and resolution "
375 "is defined by the input raster layer and is always of int32 type." );
376}
377
378QString QgsRasterFrequencyByLessThanOperatorAlgorithm::shortDescription() const
379{
380 return QObject::tr( "Evaluates on a cell-by-cell basis the frequency (number of times) "
381 "the values of an input stack of rasters are less than the value of a value raster." );
382}
383
384QgsRasterFrequencyByLessThanOperatorAlgorithm *QgsRasterFrequencyByLessThanOperatorAlgorithm::createInstance() const
385{
386 return new QgsRasterFrequencyByLessThanOperatorAlgorithm();
387}
388
389int QgsRasterFrequencyByLessThanOperatorAlgorithm::applyComparisonOperator( double searchValue, std::vector<double> cellValueStack )
390{
391 return static_cast<int>( std::count_if( cellValueStack.begin(), cellValueStack.end(), [&]( double const &stackValue ) { return stackValue < searchValue; } ) );
392}
393
@ Raster
Raster layers.
Definition qgis.h:3537
@ Int32
Thirty two bit signed integer (qint32).
Definition qgis.h:379
@ Raster
Raster layer.
Definition qgis.h:192
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3764
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3763
@ Double
Double/float values.
Definition qgis.h:3804
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:53
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:61
Base class for all map layer types.
Definition qgsmaplayer.h:80
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:87
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.
A parameter for processing algorithms which accepts multiple map layers.
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.
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
Iterator for sequentially processing raster cells.
static const int DEFAULT_MAXIMUM_TILE_WIDTH
Default maximum tile width.
static const int DEFAULT_MAXIMUM_TILE_HEIGHT
Default maximum tile height.
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:7142