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