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