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