QGIS API Documentation 3.99.0-Master (752b475928d)
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 QString outputFormat = parameterAsOutputRasterFormat( parameters, QStringLiteral( "OUTPUT" ), context );
140
141 auto writer = std::make_unique<QgsRasterFileWriter>( outputFile );
142 writer->setOutputProviderKey( QStringLiteral( "gdal" ) );
143 if ( !creationOptions.isEmpty() )
144 {
145 writer->setCreationOptions( creationOptions.split( '|' ) );
146 }
147 writer->setOutputFormat( outputFormat );
148 std::unique_ptr<QgsRasterDataProvider> provider( writer->createOneBandRaster( mDataType, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
149 if ( !provider )
150 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
151 if ( !provider->isValid() )
152 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, provider->error().message( QgsErrorMessage::Text ) ) );
153
154 provider->setNoDataValue( 1, mNoDataValue );
155 qgssize noDataCount = 0;
156 qgssize trueCount = 0;
157 qgssize falseCount = 0;
158 const qgssize layerSize = static_cast<qgssize>( mLayerWidth ) * static_cast<qgssize>( mLayerHeight );
159
160 QgsRasterAnalysisUtils::applyRasterLogicOperator( mInputs, provider.get(), mNoDataValue, mTreatNodataAsFalse, mLayerWidth, mLayerHeight, mExtent, feedback, mExtractValFunc, noDataCount, trueCount, falseCount );
161
162 QVariantMap outputs;
163 outputs.insert( QStringLiteral( "EXTENT" ), mExtent.toString() );
164 outputs.insert( QStringLiteral( "CRS_AUTHID" ), mCrs.authid() );
165 outputs.insert( QStringLiteral( "WIDTH_IN_PIXELS" ), mLayerWidth );
166 outputs.insert( QStringLiteral( "HEIGHT_IN_PIXELS" ), mLayerHeight );
167 outputs.insert( QStringLiteral( "TOTAL_PIXEL_COUNT" ), layerSize );
168 outputs.insert( QStringLiteral( "NODATA_PIXEL_COUNT" ), noDataCount );
169 outputs.insert( QStringLiteral( "TRUE_PIXEL_COUNT" ), trueCount );
170 outputs.insert( QStringLiteral( "FALSE_PIXEL_COUNT" ), falseCount );
171 outputs.insert( QStringLiteral( "OUTPUT" ), outputFile );
172
173 return outputs;
174}
175
176
177//
178// QgsRasterLogicalOrAlgorithm
179//
180
181QgsRasterLogicalOrAlgorithm::QgsRasterLogicalOrAlgorithm()
182{
183 mExtractValFunc = []( const std::vector<std::unique_ptr<QgsRasterBlock>> &inputs, bool &res, bool &resIsNoData, int row, int column, bool treatNoDataAsFalse ) {
184 res = false;
185 resIsNoData = false;
186 bool isNoData = false;
187 for ( auto &block : inputs )
188 {
189 double value = 0;
190 if ( !block || !block->isValid() )
191 {
192 if ( treatNoDataAsFalse )
193 continue;
194 else
195 {
196 resIsNoData = true;
197 break;
198 }
199 }
200 else
201 {
202 value = block->valueAndNoData( row, column, isNoData );
203 if ( isNoData && !treatNoDataAsFalse )
204 {
205 resIsNoData = true;
206 break;
207 }
208 else if ( !qgsDoubleNear( value, 0.0 ) && !isNoData )
209 {
210 res = true;
211 if ( treatNoDataAsFalse ) // otherwise we need to check all remaining rasters for nodata
212 break;
213 }
214 }
215 }
216 };
217}
218
219QString QgsRasterLogicalOrAlgorithm::name() const
220{
221 return QStringLiteral( "rasterlogicalor" );
222}
223
224QString QgsRasterLogicalOrAlgorithm::displayName() const
225{
226 return QObject::tr( "Raster boolean OR" );
227}
228
229
230QString QgsRasterLogicalOrAlgorithm::shortDescription() const
231{
232 return QObject::tr( "Calculates the boolean OR for a set of input raster layers." );
233}
234
235QString QgsRasterLogicalOrAlgorithm::shortHelpString() const
236{
237 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, "
238 "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"
239 "The reference layer parameter specifies an existing raster layer to use as a reference when creating the output raster. The output raster "
240 "will have the same extent, CRS, and pixel dimensions as this layer.\n\n"
241 "By default, a NoData pixel in ANY of the input layers will result in a NoData pixel in the output raster. If the "
242 "'Treat NoData values as false' option is checked, then NoData inputs will be treated the same as a 0 input value." );
243}
244
245QgsRasterLogicalOrAlgorithm *QgsRasterLogicalOrAlgorithm::createInstance() const
246{
247 return new QgsRasterLogicalOrAlgorithm();
248}
249
250//
251// QgsRasterLogicalAndAlgorithm
252//
253
254QgsRasterLogicalAndAlgorithm::QgsRasterLogicalAndAlgorithm()
255{
256 mExtractValFunc = []( const std::vector<std::unique_ptr<QgsRasterBlock>> &inputs, bool &res, bool &resIsNoData, int row, int column, bool treatNoDataAsFalse ) {
257 res = true;
258 resIsNoData = false;
259 bool isNoData = false;
260 for ( auto &block : inputs )
261 {
262 double value = 0;
263 if ( !block || !block->isValid() )
264 {
265 if ( treatNoDataAsFalse )
266 {
267 res = false;
268 break;
269 }
270 else
271 {
272 resIsNoData = true;
273 break;
274 }
275 }
276 else
277 {
278 value = block->valueAndNoData( row, column, isNoData );
279 if ( isNoData && !treatNoDataAsFalse )
280 {
281 resIsNoData = true;
282 break;
283 }
284 else if ( qgsDoubleNear( value, 0.0 ) || isNoData )
285 {
286 res = false;
287 if ( treatNoDataAsFalse ) // otherwise we need to check remaining rasters for nodata
288 break;
289 }
290 }
291 }
292 };
293}
294
295QString QgsRasterLogicalAndAlgorithm::name() const
296{
297 return QStringLiteral( "rasterbooleanand" );
298}
299
300QString QgsRasterLogicalAndAlgorithm::displayName() const
301{
302 return QObject::tr( "Raster boolean AND" );
303}
304
305
306QString QgsRasterLogicalAndAlgorithm::shortDescription() const
307{
308 return QObject::tr( "Calculates the boolean AND for a set of input raster layers." );
309}
310
311QString QgsRasterLogicalAndAlgorithm::shortHelpString() const
312{
313 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, "
314 "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"
315 "The reference layer parameter specifies an existing raster layer to use as a reference when creating the output raster. The output raster "
316 "will have the same extent, CRS, and pixel dimensions as this layer.\n\n"
317 "By default, a NoData pixel in ANY of the input layers will result in a NoData pixel in the output raster. If the "
318 "'Treat NoData values as false' option is checked, then NoData inputs will be treated the same as a 0 input value." );
319}
320
321QgsRasterLogicalAndAlgorithm *QgsRasterLogicalAndAlgorithm::createInstance() const
322{
323 return new QgsRasterLogicalAndAlgorithm();
324}
325
@ 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.
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