QGIS API Documentation  3.8.0-Zanzibar (11aff65)
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 = qgis::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::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 = qgis::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  QFileInfo fi( outputFile );
120  const QString outputFormat = QgsRasterFileWriter::driverForExtension( fi.suffix() );
121 
122  std::unique_ptr< QgsRasterFileWriter > writer = qgis::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  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 
A boolean parameter for processing algorithms.
int width() const
Returns the width of the (unclipped) raster.
A parameter for processing algorithms which accepts multiple map layers.
Base class for all map layer types.
Definition: qgsmaplayer.h:78
Base class for providing feedback from a processing algorithm.
Parameter is an advanced parameter which should be hidden from users by default.
This class provides qgis with the ability to render raster datasets onto the mapcanvas.
double rasterUnitsPerPixelX() const
Returns the number of raster units per each raster pixel in X axis.
QgsRasterInterface * clone() const override=0
Clone itself, create deep copy.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference)
Definition: qgis.h:265
double rasterUnitsPerPixelY() const
Returns the number of raster units per each raster pixel in Y axis.
A numeric output for processing algorithms.
int height() const
Returns the height of the (unclipped) raster.
Thirty two bit floating point (float)
Definition: qgis.h:87
virtual QgsRectangle extent() const
Returns the extent of the layer.
A raster layer destination parameter, for specifying the destination path for a raster layer created ...
static QString driverForExtension(const QString &extension)
Returns the GDAL driver name for a specified file extension.
A string output for processing algorithms.
QgsRasterDataProvider * dataProvider() override
Returns the layer&#39;s data provider, it may be nullptr.
A raster layer parameter for processing algorithms.
Custom exception class for processing related exceptions.
Definition: qgsexception.h:82
QgsCoordinateTransformContext transformContext() const
Returns the coordinate transform context.
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
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:596
Contains information about the context in which a processing algorithm is executed.
QgsCoordinateReferenceSystem crs
Definition: qgsmaplayer.h:85