QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
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::
165 applyRasterLogicOperator( mInputs, std::move( provider ), mNoDataValue, mTreatNodataAsFalse, mLayerWidth, mLayerHeight, mExtent, feedback, mExtractValFunc, noDataCount, trueCount, falseCount );
166
167 QVariantMap outputs;
168 outputs.insert( u"EXTENT"_s, mExtent.toString() );
169 outputs.insert( u"CRS_AUTHID"_s, mCrs.authid() );
170 outputs.insert( u"WIDTH_IN_PIXELS"_s, mLayerWidth );
171 outputs.insert( u"HEIGHT_IN_PIXELS"_s, mLayerHeight );
172 outputs.insert( u"TOTAL_PIXEL_COUNT"_s, layerSize );
173 outputs.insert( u"NODATA_PIXEL_COUNT"_s, noDataCount );
174 outputs.insert( u"TRUE_PIXEL_COUNT"_s, trueCount );
175 outputs.insert( u"FALSE_PIXEL_COUNT"_s, falseCount );
176 outputs.insert( u"OUTPUT"_s, outputFile );
177
178 return outputs;
179}
180
181
182//
183// QgsRasterLogicalOrAlgorithm
184//
185
186QgsRasterLogicalOrAlgorithm::QgsRasterLogicalOrAlgorithm()
187{
188 mExtractValFunc = []( const std::vector<std::unique_ptr<QgsRasterBlock>> &inputs, bool &res, bool &resIsNoData, int row, int column, bool treatNoDataAsFalse ) {
189 res = false;
190 resIsNoData = false;
191 bool isNoData = false;
192 for ( auto &block : inputs )
193 {
194 double value = 0;
195 if ( !block || !block->isValid() )
196 {
197 if ( treatNoDataAsFalse )
198 continue;
199 else
200 {
201 resIsNoData = true;
202 break;
203 }
204 }
205 else
206 {
207 value = block->valueAndNoData( row, column, isNoData );
208 if ( isNoData && !treatNoDataAsFalse )
209 {
210 resIsNoData = true;
211 break;
212 }
213 else if ( !qgsDoubleNear( value, 0.0 ) && !isNoData )
214 {
215 res = true;
216 if ( treatNoDataAsFalse ) // otherwise we need to check all remaining rasters for nodata
217 break;
218 }
219 }
220 }
221 };
222}
223
224QString QgsRasterLogicalOrAlgorithm::name() const
225{
226 return u"rasterlogicalor"_s;
227}
228
229QString QgsRasterLogicalOrAlgorithm::displayName() const
230{
231 return QObject::tr( "Raster boolean OR" );
232}
233
234
235QString QgsRasterLogicalOrAlgorithm::shortDescription() const
236{
237 return QObject::tr( "Calculates the boolean OR for a set of input raster layers." );
238}
239
240QString QgsRasterLogicalOrAlgorithm::shortHelpString() const
241{
242 return QObject::tr(
243 "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, "
244 "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"
245 "The reference layer parameter specifies an existing raster layer to use as a reference when creating the output raster. The output raster "
246 "will have the same extent, CRS, and pixel dimensions as this layer.\n\n"
247 "By default, a NoData pixel in ANY of the input layers will result in a NoData pixel in the output raster. If the "
248 "'Treat NoData values as false' option is checked, then NoData inputs will be treated the same as a 0 input value."
249 );
250}
251
252QgsRasterLogicalOrAlgorithm *QgsRasterLogicalOrAlgorithm::createInstance() const
253{
254 return new QgsRasterLogicalOrAlgorithm();
255}
256
257//
258// QgsRasterLogicalAndAlgorithm
259//
260
261QgsRasterLogicalAndAlgorithm::QgsRasterLogicalAndAlgorithm()
262{
263 mExtractValFunc = []( const std::vector<std::unique_ptr<QgsRasterBlock>> &inputs, bool &res, bool &resIsNoData, int row, int column, bool treatNoDataAsFalse ) {
264 res = true;
265 resIsNoData = false;
266 bool isNoData = false;
267 for ( auto &block : inputs )
268 {
269 double value = 0;
270 if ( !block || !block->isValid() )
271 {
272 if ( treatNoDataAsFalse )
273 {
274 res = false;
275 break;
276 }
277 else
278 {
279 resIsNoData = true;
280 break;
281 }
282 }
283 else
284 {
285 value = block->valueAndNoData( row, column, isNoData );
286 if ( isNoData && !treatNoDataAsFalse )
287 {
288 resIsNoData = true;
289 break;
290 }
291 else if ( qgsDoubleNear( value, 0.0 ) || isNoData )
292 {
293 res = false;
294 if ( treatNoDataAsFalse ) // otherwise we need to check remaining rasters for nodata
295 break;
296 }
297 }
298 }
299 };
300}
301
302QString QgsRasterLogicalAndAlgorithm::name() const
303{
304 return u"rasterbooleanand"_s;
305}
306
307QString QgsRasterLogicalAndAlgorithm::displayName() const
308{
309 return QObject::tr( "Raster boolean AND" );
310}
311
312
313QString QgsRasterLogicalAndAlgorithm::shortDescription() const
314{
315 return QObject::tr( "Calculates the boolean AND for a set of input raster layers." );
316}
317
318QString QgsRasterLogicalAndAlgorithm::shortHelpString() const
319{
320 return QObject::tr(
321 "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, "
322 "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"
323 "The reference layer parameter specifies an existing raster layer to use as a reference when creating the output raster. The output raster "
324 "will have the same extent, CRS, and pixel dimensions as this layer.\n\n"
325 "By default, a NoData pixel in ANY of the input layers will result in a NoData pixel in the output raster. If the "
326 "'Treat NoData values as false' option is checked, then NoData inputs will be treated the same as a 0 input value."
327 );
328}
329
330QgsRasterLogicalAndAlgorithm *QgsRasterLogicalAndAlgorithm::createInstance() const
331{
332 return new QgsRasterLogicalAndAlgorithm();
333}
334
@ Raster
Raster layers.
Definition qgis.h:3651
@ Float32
Thirty two bit floating point (float).
Definition qgis.h:401
@ Int8
Eight bit signed integer (qint8) (added in QGIS 3.30).
Definition qgis.h:396
@ Raster
Raster layer.
Definition qgis.h:208
@ Hidden
Parameter is hidden and should not be shown to users.
Definition qgis.h:3881
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3880
@ Double
Double/float values.
Definition qgis.h:3921
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:7485
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975