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