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