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