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