QGIS API Documentation  3.22.4-Białowieża (ce8e65e95e)
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"
21 #include "qgsrasteranalysisutils.h"
22 #include <algorithm>
23 
25 
26 
27 QStringList QgsRasterBooleanLogicAlgorithmBase::tags() const
28 {
29  return QObject::tr( "logical,boolean" ).split( ',' );
30 }
31 
32 QString QgsRasterBooleanLogicAlgorithmBase::group() const
33 {
34  return QObject::tr( "Raster analysis" );
35 }
36 
37 QString QgsRasterBooleanLogicAlgorithmBase::groupId() const
38 {
39  return QStringLiteral( "rasteranalysis" );
40 }
41 
42 void 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 );
56  typeChoice->setFlags( QgsProcessingParameterDefinition::FlagAdvanced );
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 
72 bool 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() == QgsMapLayerType::RasterLayer )
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 
116 QVariantMap 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 
159 QgsRasterLogicalOrAlgorithm::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 
198 QString QgsRasterLogicalOrAlgorithm::name() const
199 {
200  return QStringLiteral( "rasterlogicalor" );
201 }
202 
203 QString QgsRasterLogicalOrAlgorithm::displayName() const
204 {
205  return QObject::tr( "Raster boolean OR" );
206 }
207 
208 
209 QString QgsRasterLogicalOrAlgorithm::shortDescription() const
210 {
211  return QObject::tr( "Calculates the boolean OR for a set of input raster layers" );
212 }
213 
214 QString 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 
224 QgsRasterLogicalOrAlgorithm *QgsRasterLogicalOrAlgorithm::createInstance() const
225 {
226  return new QgsRasterLogicalOrAlgorithm();
227 }
228 
229 //
230 // QgsRasterLogicalAndAlgorithm
231 //
232 
233 QgsRasterLogicalAndAlgorithm::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 
275 QString QgsRasterLogicalAndAlgorithm::name() const
276 {
277  return QStringLiteral( "rasterbooleanand" );
278 }
279 
280 QString QgsRasterLogicalAndAlgorithm::displayName() const
281 {
282  return QObject::tr( "Raster boolean AND" );
283 }
284 
285 
286 QString QgsRasterLogicalAndAlgorithm::shortDescription() const
287 {
288  return QObject::tr( "Calculates the boolean AND for a set of input raster layers" );
289 }
290 
291 QString 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 
301 QgsRasterLogicalAndAlgorithm *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:52
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
QgsRasterDataProvider * clone() const override=0
Clone itself, create deep copy.
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:1713
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:1246