QGIS API Documentation 3.37.0-Master (fdefdf9c27f)
qgsalgorithmrasterlogicalop.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmrasterlogicalop.cpp
3 ---------------------
4 begin : March 2019
5 copyright : (C) 2019 by Nyall Dawson
6 email : nyall dot dawson 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
18#include "gdal.h"
20#include "qgsrasterprojector.h"
21#include "qgsrasterfilewriter.h"
23#include <algorithm>
24
26
27
28QStringList QgsRasterBooleanLogicAlgorithmBase::tags() const
29{
30 return QObject::tr( "logical,boolean" ).split( ',' );
31}
32
33QString QgsRasterBooleanLogicAlgorithmBase::group() const
34{
35 return QObject::tr( "Raster analysis" );
36}
37
38QString QgsRasterBooleanLogicAlgorithmBase::groupId() const
39{
40 return QStringLiteral( "rasteranalysis" );
41}
42
43void QgsRasterBooleanLogicAlgorithmBase::initAlgorithm( const QVariantMap & )
44{
45 addParameter( new QgsProcessingParameterMultipleLayers( QStringLiteral( "INPUT" ),
46 QObject::tr( "Input layers" ), Qgis::ProcessingSourceType::Raster ) );
47
48 addParameter( new QgsProcessingParameterRasterLayer( QStringLiteral( "REF_LAYER" ), QObject::tr( "Reference layer" ) ) );
49 addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "NODATA_AS_FALSE" ), QObject::tr( "Treat NoData values as false" ), false ) );
50
51 std::unique_ptr< QgsProcessingParameterNumber > noDataValueParam = std::make_unique< QgsProcessingParameterNumber >( QStringLiteral( "NO_DATA" ),
52 QObject::tr( "Output NoData value" ), Qgis::ProcessingNumberParameterType::Double, -9999 );
53 noDataValueParam->setFlags( Qgis::ProcessingParameterFlag::Advanced );
54 addParameter( noDataValueParam.release() );
55
56 std::unique_ptr< QgsProcessingParameterDefinition > typeChoice = QgsRasterAnalysisUtils::createRasterTypeParameter( QStringLiteral( "DATA_TYPE" ), QObject::tr( "Output data type" ), Qgis::DataType::Float32 );
57 typeChoice->setFlags( Qgis::ProcessingParameterFlag::Advanced );
58 addParameter( typeChoice.release() );
59
60 addParameter( new QgsProcessingParameterRasterDestination( QStringLiteral( "OUTPUT" ),
61 QObject::tr( "Output layer" ) ) );
62
63 addOutput( new QgsProcessingOutputString( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ) ) );
64 addOutput( new QgsProcessingOutputString( QStringLiteral( "CRS_AUTHID" ), QObject::tr( "CRS authority identifier" ) ) );
65 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "WIDTH_IN_PIXELS" ), QObject::tr( "Width in pixels" ) ) );
66 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "HEIGHT_IN_PIXELS" ), QObject::tr( "Height in pixels" ) ) );
67 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "TOTAL_PIXEL_COUNT" ), QObject::tr( "Total pixel count" ) ) );
68 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "NODATA_PIXEL_COUNT" ), QObject::tr( "NoData pixel count" ) ) );
69 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "TRUE_PIXEL_COUNT" ), QObject::tr( "True pixel count" ) ) );
70 addOutput( new QgsProcessingOutputNumber( QStringLiteral( "FALSE_PIXEL_COUNT" ), QObject::tr( "False pixel count" ) ) );
71}
72
73bool QgsRasterBooleanLogicAlgorithmBase::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
74{
75 QgsRasterLayer *referenceLayer = parameterAsRasterLayer( parameters, QStringLiteral( "REF_LAYER" ), context );
76 if ( !referenceLayer )
77 throw QgsProcessingException( invalidRasterError( parameters, QStringLiteral( "REF_LAYER" ) ) );
78 mCrs = referenceLayer->crs();
79 mRasterUnitsPerPixelX = referenceLayer->rasterUnitsPerPixelX();
80 mRasterUnitsPerPixelY = referenceLayer->rasterUnitsPerPixelY();
81 mLayerWidth = referenceLayer->width();
82 mLayerHeight = referenceLayer->height();
83 mExtent = referenceLayer->extent();
84 mNoDataValue = parameterAsDouble( parameters, QStringLiteral( "NO_DATA" ), context );
85 mDataType = QgsRasterAnalysisUtils::rasterTypeChoiceToDataType( parameterAsEnum( parameters, QStringLiteral( "DATA_TYPE" ), context ) );
86 if ( mDataType == Qgis::DataType::Int8 && atoi( GDALVersionInfo( "VERSION_NUM" ) ) < GDAL_COMPUTE_VERSION( 3, 7, 0 ) )
87 throw QgsProcessingException( QObject::tr( "Int8 data type requires GDAL version 3.7 or later" ) );
88
89 mTreatNodataAsFalse = parameterAsBoolean( parameters, QStringLiteral( "NODATA_AS_FALSE" ), context );
90
91 const QList< QgsMapLayer * > layers = parameterAsLayerList( parameters, QStringLiteral( "INPUT" ), context );
92 QList< QgsRasterLayer * > rasterLayers;
93 rasterLayers.reserve( layers.count() );
94 for ( QgsMapLayer *l : layers )
95 {
96 if ( l->type() == Qgis::LayerType::Raster )
97 {
98 QgsRasterLayer *layer = qobject_cast< QgsRasterLayer * >( l );
99 QgsRasterAnalysisUtils::RasterLogicInput input;
100 const int band = 1; // hardcoded for now - needs a way to supply this in the processing gui
101 input.hasNoDataValue = layer->dataProvider()->sourceHasNoDataValue( band );
102 input.sourceDataProvider.reset( layer->dataProvider()->clone() );
103 input.interface = input.sourceDataProvider.get();
104 // add projector if necessary
105 if ( layer->crs() != mCrs )
106 {
107 input.projector = std::make_unique< QgsRasterProjector >();
108 input.projector->setInput( input.sourceDataProvider.get() );
109 input.projector->setCrs( layer->crs(), mCrs, context.transformContext() );
110 input.interface = input.projector.get();
111 }
112 mInputs.emplace_back( std::move( input ) );
113 }
114 }
115
116 return true;
117}
118
119QVariantMap QgsRasterBooleanLogicAlgorithmBase::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
120{
121 const QString outputFile = parameterAsOutputLayer( parameters, QStringLiteral( "OUTPUT" ), context );
122 const QFileInfo fi( outputFile );
123 const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
124
125 std::unique_ptr< QgsRasterFileWriter > writer = std::make_unique< QgsRasterFileWriter >( outputFile );
126 writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
127 writer->setOutputFormat( outputFormat );
128 std::unique_ptr<QgsRasterDataProvider > provider( writer->createOneBandRaster( mDataType, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
129 if ( !provider )
130 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
131 if ( !provider->isValid() )
132 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
133
134 provider->setNoDataValue( 1, mNoDataValue );
135 qgssize noDataCount = 0;
136 qgssize trueCount = 0;
137 qgssize falseCount = 0;
138 const qgssize layerSize = static_cast< qgssize >( mLayerWidth ) * static_cast< qgssize >( mLayerHeight );
139
140 QgsRasterAnalysisUtils::applyRasterLogicOperator( mInputs, provider.get(), mNoDataValue, mTreatNodataAsFalse, mLayerWidth, mLayerHeight,
141 mExtent, feedback, mExtractValFunc, noDataCount, trueCount, falseCount );
142
143 QVariantMap outputs;
144 outputs.insert( QStringLiteral( "EXTENT" ), mExtent.toString() );
145 outputs.insert( QStringLiteral( "CRS_AUTHID" ), mCrs.authid() );
146 outputs.insert( QStringLiteral( "WIDTH_IN_PIXELS" ), mLayerWidth );
147 outputs.insert( QStringLiteral( "HEIGHT_IN_PIXELS" ), mLayerHeight );
148 outputs.insert( QStringLiteral( "TOTAL_PIXEL_COUNT" ), layerSize );
149 outputs.insert( QStringLiteral( "NODATA_PIXEL_COUNT" ), noDataCount );
150 outputs.insert( QStringLiteral( "TRUE_PIXEL_COUNT" ), trueCount );
151 outputs.insert( QStringLiteral( "FALSE_PIXEL_COUNT" ), falseCount );
152 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
153
154 return outputs;
155}
156
157
158//
159// QgsRasterLogicalOrAlgorithm
160//
161
162QgsRasterLogicalOrAlgorithm::QgsRasterLogicalOrAlgorithm()
163{
164 mExtractValFunc = [ = ]( const std::vector< std::unique_ptr< QgsRasterBlock > > &inputs, bool & res, bool & resIsNoData, int row, int column, bool treatNoDataAsFalse )
165 {
166 res = false;
167 resIsNoData = false;
168 bool isNoData = false;
169 for ( auto &block : inputs )
170 {
171 double value = 0;
172 if ( !block || !block->isValid() )
173 {
174 if ( treatNoDataAsFalse )
175 continue;
176 else
177 {
178 resIsNoData = true;
179 break;
180 }
181 }
182 else
183 {
184 value = block->valueAndNoData( row, column, isNoData );
185 if ( isNoData && !treatNoDataAsFalse )
186 {
187 resIsNoData = true;
188 break;
189 }
190 else if ( !qgsDoubleNear( value, 0.0 ) && !isNoData )
191 {
192 res = true;
193 if ( treatNoDataAsFalse ) // otherwise we need to check all remaining rasters for nodata
194 break;
195 }
196 }
197 }
198 };
199}
200
201QString QgsRasterLogicalOrAlgorithm::name() const
202{
203 return QStringLiteral( "rasterlogicalor" );
204}
205
206QString QgsRasterLogicalOrAlgorithm::displayName() const
207{
208 return QObject::tr( "Raster boolean OR" );
209}
210
211
212QString QgsRasterLogicalOrAlgorithm::shortDescription() const
213{
214 return QObject::tr( "Calculates the boolean OR for a set of input raster layers" );
215}
216
217QString QgsRasterLogicalOrAlgorithm::shortHelpString() const
218{
219 return QObject::tr( "This algorithm calculates the boolean OR for a set of input rasters. If any of the input rasters have a non-zero value for a pixel, "
220 "that pixel will be set to 1 in the output raster. If all the input rasters have 0 values for the pixel it will be set to 0 in the output raster.\n\n"
221 "The reference layer parameter specifies an existing raster layer to use as a reference when creating the output raster. The output raster "
222 "will have the same extent, CRS, and pixel dimensions as this layer.\n\n"
223 "By default, a NoData pixel in ANY of the input layers will result in a NoData pixel in the output raster. If the "
224 "'Treat NoData values as false' option is checked, then NoData inputs will be treated the same as a 0 input value." );
225}
226
227QgsRasterLogicalOrAlgorithm *QgsRasterLogicalOrAlgorithm::createInstance() const
228{
229 return new QgsRasterLogicalOrAlgorithm();
230}
231
232//
233// QgsRasterLogicalAndAlgorithm
234//
235
236QgsRasterLogicalAndAlgorithm::QgsRasterLogicalAndAlgorithm()
237{
238 mExtractValFunc = [ = ]( const std::vector< std::unique_ptr< QgsRasterBlock > > &inputs, bool & res, bool & resIsNoData, int row, int column, bool treatNoDataAsFalse )
239 {
240 res = true;
241 resIsNoData = false;
242 bool isNoData = false;
243 for ( auto &block : inputs )
244 {
245 double value = 0;
246 if ( !block || !block->isValid() )
247 {
248 if ( treatNoDataAsFalse )
249 {
250 res = false;
251 break;
252 }
253 else
254 {
255 resIsNoData = true;
256 break;
257 }
258 }
259 else
260 {
261 value = block->valueAndNoData( row, column, isNoData );
262 if ( isNoData && !treatNoDataAsFalse )
263 {
264 resIsNoData = true;
265 break;
266 }
267 else if ( qgsDoubleNear( value, 0.0 ) || isNoData )
268 {
269 res = false;
270 if ( treatNoDataAsFalse ) // otherwise we need to check remaining rasters for nodata
271 break;
272 }
273 }
274 }
275 };
276}
277
278QString QgsRasterLogicalAndAlgorithm::name() const
279{
280 return QStringLiteral( "rasterbooleanand" );
281}
282
283QString QgsRasterLogicalAndAlgorithm::displayName() const
284{
285 return QObject::tr( "Raster boolean AND" );
286}
287
288
289QString QgsRasterLogicalAndAlgorithm::shortDescription() const
290{
291 return QObject::tr( "Calculates the boolean AND for a set of input raster layers" );
292}
293
294QString QgsRasterLogicalAndAlgorithm::shortHelpString() const
295{
296 return QObject::tr( "This algorithm calculates the boolean AND for a set of input rasters. If all of the input rasters have a non-zero value for a pixel, "
297 "that pixel will be set to 1 in the output raster. If any of the input rasters have 0 values for the pixel it will be set to 0 in the output raster.\n\n"
298 "The reference layer parameter specifies an existing raster layer to use as a reference when creating the output raster. The output raster "
299 "will have the same extent, CRS, and pixel dimensions as this layer.\n\n"
300 "By default, a NoData pixel in ANY of the input layers will result in a NoData pixel in the output raster. If the "
301 "'Treat NoData values as false' option is checked, then NoData inputs will be treated the same as a 0 input value." );
302}
303
304QgsRasterLogicalAndAlgorithm *QgsRasterLogicalAndAlgorithm::createInstance() const
305{
306 return new QgsRasterLogicalAndAlgorithm();
307}
308
310
311
312
@ Float32
Thirty two bit floating point (float)
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30)
@ Raster
Raster layer.
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Base class for all map layer types.
Definition: qgsmaplayer.h:75
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:81
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.
Definition: qgsexception.h:83
Base class for providing feedback from a processing algorithm.
A numeric output for processing algorithms.
A string output 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.
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.
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:5747
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:5207