QGIS API Documentation 4.1.0-Master (5bf3c20f3c9)
Loading...
Searching...
No Matches
qgsalgorithmrasterfeaturepreservingsmoothing.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsalgorithmrasterfeaturepreservingsmoothing.cpp
3 ---------------------
4 begin : December 2025
5 copyright : (C) 2025 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
20#include "qgsrasterfilewriter.h"
21
22#include <QString>
23
24using namespace Qt::StringLiterals;
25
27
28struct Vector2D
29{
30 double x = 0;
31 double y = 0;
32
33 Vector2D( double x = 0, double y = 0 )
34 : x( x )
35 , y( y )
36 {}
37
38 double angleBetweenCos( const Vector2D &other ) const
39 {
40 const double dot = x * other.x + y * other.y + 1.0;
41 const double magSelf = ( x * x + y * y + 1.0 );
42 const double magOther = ( other.x * other.x + other.y * other.y + 1.0 );
43 return dot / std::sqrt( magSelf * magOther );
44 }
45};
46
47QString QgsRasterFeaturePreservingSmoothingAlgorithm::name() const
48{
49 return u"rasterfeaturepreservingsmoothing"_s;
50}
51
52QString QgsRasterFeaturePreservingSmoothingAlgorithm::displayName() const
53{
54 return QObject::tr( "Feature preserving DEM smoothing" );
55}
56
57QStringList QgsRasterFeaturePreservingSmoothingAlgorithm::tags() const
58{
59 return QObject::tr( "smooth,filter,denoise,fpdems,blur" ).split( ',' );
60}
61
62QString QgsRasterFeaturePreservingSmoothingAlgorithm::group() const
63{
64 return QObject::tr( "Raster analysis" );
65}
66
67QString QgsRasterFeaturePreservingSmoothingAlgorithm::groupId() const
68{
69 return u"rasteranalysis"_s;
70}
71
72QString QgsRasterFeaturePreservingSmoothingAlgorithm::shortHelpString() const
73{
74 return QObject::tr(
75 "This algorithm applies the Feature-Preserving DEM Smoothing (FPDEMS) method, as described by Lindsay et al. (2019).\n\n"
76 "It is effective at removing surface roughness from Digital Elevation Models (DEMs) without significantly altering sharp features such as breaks-in-slope, stream banks, or terrace scarps. "
77 "This makes it superior to standard low-pass filters (e.g., mean, median, Gaussian) or resampling, which often blur distinct topographic features.\n\n"
78 "The algorithm works in three steps:\n"
79 "1. Calculating surface normal 3D vectors for each grid cell.\n"
80 "2. Smoothing the normal vector field using a filter that applies more weight to neighbors with similar surface normals (preserving edges).\n"
81 "3. Iteratively updating the elevations in the DEM to match the smoothed normal field.\n\n"
82 "References: Lindsay, John et al (2019): LiDAR DEM Smoothing and the Preservation of Drainage Features. Remote Sensing, Vol. 11, Issue 16, https://doi.org/10.3390/rs11161926"
83 );
84}
85
86QString QgsRasterFeaturePreservingSmoothingAlgorithm::shortDescription() const
87{
88 return QObject::tr( "Smooths a DEM while preserving topographic features." );
89}
90
91void QgsRasterFeaturePreservingSmoothingAlgorithm::initAlgorithm( const QVariantMap & )
92{
93 addParameter( new QgsProcessingParameterRasterLayer( u"INPUT"_s, QObject::tr( "Input layer" ) ) );
94
95 addParameter( new QgsProcessingParameterBand( u"BAND"_s, QObject::tr( "Band number" ), 1, u"INPUT"_s ) );
96
97 auto radiusParam = std::make_unique<QgsProcessingParameterNumber>( u"RADIUS"_s, QObject::tr( "Filter radius (pixels)" ), Qgis::ProcessingNumberParameterType::Integer, 5, false, 1, 50 );
98 radiusParam->setHelp( QObject::tr( "Radius of the filter kernel. A radius of 5 results in an 11x11 kernel." ) );
99 addParameter( radiusParam.release() );
100
101 auto thresholdParam
102 = std::make_unique<QgsProcessingParameterNumber>( u"THRESHOLD"_s, QObject::tr( "Normal difference threshold (degrees)" ), Qgis::ProcessingNumberParameterType::Double, 15.0, false, 0.0, 90.0 );
103 thresholdParam->setHelp(
104 QObject::tr(
105 "Maximum angular difference (in degrees) between the normal vector of the center cell and a neighbor for the neighbor to be included in the filter. Higher values result in more neighbors being "
106 "included, producing smoother surfaces. A range of 10-20 degrees is typically optimal."
107 )
108 );
109 addParameter( thresholdParam.release() );
110
111 auto iterParam = std::make_unique<QgsProcessingParameterNumber>( u"ITERATIONS"_s, QObject::tr( "Elevation update iterations" ), Qgis::ProcessingNumberParameterType::Integer, 3, false, 1, 50 );
112 iterParam->setHelp( QObject::tr( "Number of times the smoothing process (elevation update) is repeated. Increasing this value from the default of 3 will result in significantly greater smoothing." ) );
113 addParameter( iterParam.release() );
114
115 auto maxDiffParam
116 = std::make_unique<QgsProcessingParameterNumber>( u"MAX_ELEVATION_CHANGE"_s, QObject::tr( "Maximum elevation change" ), Qgis::ProcessingNumberParameterType::Double, QVariant(), true, 0.0 );
117 maxDiffParam->setHelp(
118 QObject::tr( "The allowed maximum height change of any cell in one iteration. If the calculated change exceeds this value, the elevation remains unchanged. This prevents excessive deviation from the original surface." )
119 );
120 addParameter( maxDiffParam.release() );
121
122 auto zFactorParam = std::make_unique<QgsProcessingParameterNumber>( u"Z_FACTOR"_s, QObject::tr( "Z factor" ), Qgis::ProcessingNumberParameterType::Double, 1.0, false, 0.00000001 );
123 zFactorParam->setHelp( QObject::tr( "Multiplication factor to convert vertical Z units to horizontal XY units." ) );
124 zFactorParam->setFlags( zFactorParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
125 zFactorParam->setMetadata( { QVariantMap( { { u"widget_wrapper"_s, QVariantMap( { { u"decimals"_s, 12 } } ) } } ) } );
126 addParameter( zFactorParam.release() );
127
128 auto creationOptsParam = std::make_unique<QgsProcessingParameterString>( u"CREATION_OPTIONS"_s, QObject::tr( "Creation options" ), QVariant(), false, true );
129 creationOptsParam->setMetadata( QVariantMap( { { u"widget_wrapper"_s, QVariantMap( { { u"widget_type"_s, u"rasteroptions"_s } } ) } } ) );
130 creationOptsParam->setFlags( creationOptsParam->flags() | Qgis::ProcessingParameterFlag::Advanced );
131 addParameter( creationOptsParam.release() );
132
133 auto outputLayerParam = std::make_unique<QgsProcessingParameterRasterDestination>( u"OUTPUT"_s, QObject::tr( "Output layer" ) );
134 addParameter( outputLayerParam.release() );
135}
136
137QgsRasterFeaturePreservingSmoothingAlgorithm *QgsRasterFeaturePreservingSmoothingAlgorithm::createInstance() const
138{
139 return new QgsRasterFeaturePreservingSmoothingAlgorithm();
140}
141
142bool QgsRasterFeaturePreservingSmoothingAlgorithm::prepareAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback * )
143{
144 QgsRasterLayer *layer = parameterAsRasterLayer( parameters, u"INPUT"_s, context );
145 if ( !layer )
146 throw QgsProcessingException( invalidRasterError( parameters, u"INPUT"_s ) );
147
148 mBand = parameterAsInt( parameters, u"BAND"_s, context );
149 if ( mBand < 1 || mBand > layer->bandCount() )
150 throw QgsProcessingException( QObject::tr( "Invalid band number for BAND (%1): Valid values for input raster are 1 to %2" ).arg( mBand ).arg( layer->bandCount() ) );
151
152 mInterface.reset( layer->dataProvider()->clone() );
153 mLayerWidth = layer->width();
154 mLayerHeight = layer->height();
155 mExtent = layer->extent();
156 mCrs = layer->crs();
157 mRasterUnitsPerPixelX = layer->rasterUnitsPerPixelX();
158 mRasterUnitsPerPixelY = layer->rasterUnitsPerPixelY();
159 mDataType = layer->dataProvider()->dataType( mBand );
160 mNoData = layer->dataProvider()->sourceNoDataValue( mBand );
161 return true;
162}
163
164QVariantMap QgsRasterFeaturePreservingSmoothingAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
165{
166 const int radius = parameterAsInt( parameters, u"RADIUS"_s, context );
167
168 const double thresholdDegrees = parameterAsDouble( parameters, u"THRESHOLD"_s, context );
169 const double cosThreshold = std::cos( thresholdDegrees * M_PI / 180.0 );
170
171 const int iterations = parameterAsInt( parameters, u"ITERATIONS"_s, context );
172 const bool hasMaxElevChange = parameters.value( u"MAX_ELEVATION_CHANGE"_s ).isValid();
173 const double maxElevChange = hasMaxElevChange ? parameterAsDouble( parameters, u"MAX_ELEVATION_CHANGE"_s, context ) : 0;
174 const double zFactor = parameterAsDouble( parameters, u"Z_FACTOR"_s, context );
175
176 const double oneOver8ResX = 1 / ( 8.0 * mRasterUnitsPerPixelX );
177 const double oneOver8ResY = 1 / ( 8.0 * mRasterUnitsPerPixelY );
178
179 const QString creationOptions = parameterAsString( parameters, u"CREATION_OPTIONS"_s, context ).trimmed();
180 const QString outputFile = parameterAsOutputLayer( parameters, u"OUTPUT"_s, context );
181 const QString outputFormat = parameterAsOutputRasterFormat( parameters, u"OUTPUT"_s, context );
182
183 auto outputWriter = std::make_unique<QgsRasterFileWriter>( outputFile );
184 outputWriter->setOutputProviderKey( u"gdal"_s );
185 if ( !creationOptions.isEmpty() )
186 {
187 outputWriter->setCreationOptions( creationOptions.split( '|' ) );
188 }
189 outputWriter->setOutputFormat( outputFormat );
190
191 std::unique_ptr<QgsRasterDataProvider> destProvider( outputWriter->createOneBandRaster( mDataType, mLayerWidth, mLayerHeight, mExtent, mCrs ) );
192 if ( !destProvider )
193 throw QgsProcessingException( QObject::tr( "Could not create raster output: %1" ).arg( outputFile ) );
194 if ( !destProvider->isValid() )
195 throw QgsProcessingException( QObject::tr( "Could not create raster output %1: %2" ).arg( outputFile, destProvider->error().message( QgsErrorMessage::Text ) ) );
196
197 destProvider->setNoDataValue( 1, mNoData );
198 destProvider->setEditable( true );
199
200 // block iteration padding must be large enough to cover all 3 steps, i.e. 1px (for normal calculation) + radius in pixels (for smoothing) + 1px for each iteration
201 const int blockPadding = 1 + radius + iterations;
202 QgsRasterIterator iter( mInterface.get(), blockPadding );
203 iter.startRasterRead( mBand, mLayerWidth, mLayerHeight, mExtent );
204
205 int iterCols = 0;
206 int iterRows = 0;
207 int iterLeft = 0;
208 int iterTop = 0;
209 int tileCols = 0;
210 int tileRows = 0;
211 int tileLeft = 0;
212 int tileTop = 0;
213 QgsRectangle blockExtent;
214
215 std::unique_ptr<QgsRasterBlock> inputBlock;
216
217 // buffers are re-used across different tiles to minimize allocations.
218 // Resize them in advance to the largest possible required sizes.
219 const size_t maxBufferSize = static_cast<std::size_t>( iter.maximumTileWidth() + 2 * blockPadding ) * static_cast< std::size_t >( iter.maximumTileHeight() + 2 * blockPadding );
220 // pixel normals, using SCALED z values (accounting for z-factor)
221 std::vector<Vector2D> normalBuffer( maxBufferSize );
222 std::vector<Vector2D> smoothedNormalBuffer( maxBufferSize );
223 std::vector<double> zBuffer( maxBufferSize );
224 std::vector<qint8> noDataBuffer( maxBufferSize );
225
226 bool isNoData = false;
227
228 const bool hasReportsDuringClose = destProvider->hasReportsDuringClose();
229 const double maxProgressDuringBlockWriting = hasReportsDuringClose ? 50.0 : 100.0;
230
231 while ( iter.readNextRasterPart( mBand, iterCols, iterRows, inputBlock, iterLeft, iterTop, &blockExtent, &tileCols, &tileRows, &tileLeft, &tileTop ) )
232 {
233 if ( feedback->isCanceled() )
234 break;
235 feedback->setProgress( maxProgressDuringBlockWriting * iter.progress( mBand, 0 ) );
236 feedback->setProgressText( QObject::tr( "Calculating surface normals" ) );
237
238 // copy raster values and NoData to buffers once in advance -- we will be retrieving
239 // each individual value many times during surface normal calculation, so the cost
240 // of this upfront step pays off...
241 double *zBufferData = zBuffer.data();
242 qint8 *noDataBufferData = noDataBuffer.data();
243 for ( int r = 0; r < iterRows; ++r )
244 {
245 for ( int c = 0; c < iterCols; ++c )
246 {
247 *zBufferData++ = inputBlock->valueAndNoData( r, c, isNoData ) * zFactor;
248 *noDataBufferData++ = isNoData ? 1 : 0;
249 }
250 }
251
252 // step 1: calculate surface normals
253 // we follow Lindsay's rust implementation of FPDEMS, and consider out-of-range pixels and no-data pixels
254 // as the center pixel value when calculating the normal over the 3x3 matrix window.
255
256 // if neighbor is out of range or is NoData, follow Lindsay and use center z
257 auto getZ = [iterRows, iterCols, &noDataBuffer, &zBuffer]( int row, int col, double z ) -> double {
258 if ( row < 0 || row >= iterRows || col < 0 || col >= iterCols )
259 return z;
260 std::size_t idx = static_cast<std::size_t>( row ) * iterCols + col;
261 if ( noDataBuffer[idx] )
262 return z;
263 return zBuffer[idx];
264 };
265
266 for ( int r = 0; r < iterRows; ++r )
267 {
268 if ( feedback->isCanceled() )
269 break;
270
271 feedback->setProgress( maxProgressDuringBlockWriting * iter.progress( mBand, r / static_cast< double >( iterRows ) / 3.0 ) );
272
273 for ( int c = 0; c < iterCols; ++c )
274 {
275 const std::size_t idx = static_cast<std::size_t>( r ) * iterCols + c;
276 if ( noDataBuffer[idx] )
277 {
278 normalBuffer[idx] = Vector2D( 0, 0 );
279 continue;
280 }
281
282 const double z = zBuffer[idx];
283 const double z1 = getZ( r - 1, c - 1, z );
284 const double z2 = getZ( r - 1, c, z );
285 const double z3 = getZ( r - 1, c + 1, z );
286 const double z4 = getZ( r, c - 1, z );
287 const double z6 = getZ( r, c + 1, z );
288 const double z7 = getZ( r + 1, c - 1, z );
289 const double z8 = getZ( r + 1, c, z );
290 const double z9 = getZ( r + 1, c + 1, z );
291
292 // Horn 1981, adjusting for raster units per pixel
293 const double dx = ( ( z3 - z1 + 2 * ( z6 - z4 ) + z9 - z7 ) * oneOver8ResX );
294 const double dy = ( ( z7 - z1 + 2 * ( z8 - z2 ) + z9 - z3 ) * oneOver8ResY );
295 normalBuffer[idx] = Vector2D( -dx, dy );
296 }
297 }
298 if ( feedback->isCanceled() )
299 break;
300
301 // step 2: smooth normals
302 feedback->setProgressText( QObject::tr( "Smoothing surface normals" ) );
303 for ( int r = 0; r < iterRows; ++r )
304 {
305 if ( feedback->isCanceled() )
306 break;
307
308 feedback->setProgress( maxProgressDuringBlockWriting * iter.progress( mBand, 1.0 / 3.0 + r / static_cast< double >( iterRows ) / 3.0 ) );
309
310 for ( int c = 0; c < iterCols; ++c )
311 {
312 const std::size_t idx = static_cast<std::size_t>( r ) * iterCols + c;
313 if ( noDataBuffer[idx] )
314 continue;
315
316 const Vector2D &centerNormal = normalBuffer[idx];
317 double summedWeights = 0.0;
318 double summedX = 0;
319 double summedY = 0;
320
321 for ( int kernelY = -radius; kernelY <= radius; ++kernelY )
322 {
323 for ( int kernelX = -radius; kernelX <= radius; ++kernelX )
324 {
325 const int pixelRow = r + kernelY;
326 const int pixelCol = c + kernelX;
327
328 // skip pixels outside range, nodata pixels
329 if ( pixelRow < 0 || pixelRow >= iterRows || pixelCol < 0 || pixelCol >= iterCols )
330 continue;
331 const std::size_t pixelIdx = static_cast<std::size_t>( pixelRow ) * iterCols + pixelCol;
332 if ( noDataBuffer[pixelIdx] )
333 continue;
334
335 const Vector2D &neighNormal = normalBuffer[pixelIdx];
336
337 const double cosAngle = centerNormal.angleBetweenCos( neighNormal );
338 if ( cosAngle > cosThreshold )
339 {
340 const double w = ( cosAngle - cosThreshold ) * ( cosAngle - cosThreshold );
341 summedWeights += w;
342 summedX += w * neighNormal.x;
343 summedY += w * neighNormal.y;
344 }
345 }
346 }
347 if ( !qgsDoubleNear( summedWeights, 0.0 ) )
348 {
349 summedX /= summedWeights;
350 summedY /= summedWeights;
351 }
352 smoothedNormalBuffer[idx] = Vector2D( summedX, summedY );
353 }
354 }
355 if ( feedback->isCanceled() )
356 break;
357
358 // step 3: update elevation
359 // pixel offsets
360 const int dx[8] = { 1, 1, 1, 0, -1, -1, -1, 0 };
361 const int dy[8] = { -1, 0, 1, 1, 1, 0, -1, -1 };
362
363 // world offsets (accounting for units per pixel)
364 const double worldX[8] = { -mRasterUnitsPerPixelX, -mRasterUnitsPerPixelX, -mRasterUnitsPerPixelX, 0.0, mRasterUnitsPerPixelX, mRasterUnitsPerPixelX, mRasterUnitsPerPixelX, 0.0 };
365 const double worldY[8] = { -mRasterUnitsPerPixelY, 0.0, mRasterUnitsPerPixelY, mRasterUnitsPerPixelY, mRasterUnitsPerPixelY, 0.0, -mRasterUnitsPerPixelY, -mRasterUnitsPerPixelY };
366
367 for ( int iteration = 0; iteration < iterations; ++iteration )
368 {
369 feedback->setProgressText( QObject::tr( "Updating elevation (iteration %1/%2)" ).arg( iteration + 1 ).arg( iterations ) );
370 if ( feedback->isCanceled() )
371 break;
372
373 for ( int r = 0; r < iterRows; ++r )
374 {
375 feedback->setProgress(
376 maxProgressDuringBlockWriting * iter.progress( mBand, 2.0 / 3.0 + ( ( static_cast< double >( iteration ) / iterations ) + ( r / static_cast< double >( iterRows ) ) / iterations ) / 3.0 )
377 );
378 for ( int c = 0; c < iterCols; ++c )
379 {
380 const std::size_t idx = static_cast<std::size_t>( r ) * iterCols + c;
381 if ( noDataBuffer[idx] )
382 continue;
383
384 const double originalZ = inputBlock->value( r, c );
385
386 const Vector2D &centerNormal = smoothedNormalBuffer[idx];
387 double sumWeights = 0.0;
388 double sumZ = 0.0;
389 for ( int n = 0; n < 8; ++n )
390 {
391 const int pixelX = c + dx[n];
392 const int pixelY = r + dy[n];
393 if ( pixelX < 0 || pixelX >= iterCols || pixelY < 0 || pixelY >= iterRows )
394 continue;
395
396 const std::size_t nIdx = static_cast<std::size_t>( pixelY ) * iterCols + pixelX;
397 if ( noDataBuffer[nIdx] )
398 continue;
399
400 const double pixelZ = zBuffer[nIdx];
401
402 const Vector2D &neighNormal = smoothedNormalBuffer[nIdx];
403 const double cosAngle = centerNormal.angleBetweenCos( neighNormal );
404 if ( cosAngle > cosThreshold )
405 {
406 const double w = ( cosAngle - cosThreshold ) * ( cosAngle - cosThreshold );
407 sumWeights += w;
408 sumZ += -( neighNormal.x * worldX[n] + neighNormal.y * worldY[n] - pixelZ ) * w;
409 }
410 }
411
412 if ( sumWeights > 0.0 )
413 {
414 const double newZScaled = ( sumZ / sumWeights );
415 const double newZUnscaled = newZScaled / zFactor;
416 if ( !hasMaxElevChange || std::abs( newZUnscaled - originalZ ) <= maxElevChange )
417 {
418 zBuffer[idx] = newZScaled;
419 }
420 else
421 {
422 zBuffer[idx] = originalZ * zFactor;
423 }
424 }
425 else
426 {
427 zBuffer[idx] = originalZ * zFactor;
428 }
429 }
430 }
431 }
432
433 const int blockOffsetX = tileLeft - iterLeft;
434 const int blockOffsetY = tileTop - iterTop;
435
436 auto outputBlock = std::make_unique<QgsRasterBlock>( mDataType, tileCols, tileRows );
437
438 for ( int r = 0; r < tileRows; ++r )
439 {
440 for ( int c = 0; c < tileCols; ++c )
441 {
442 const int pixelRow = r + blockOffsetY;
443 const int pixelCol = c + blockOffsetX;
444
445 const std::size_t idx = static_cast<std::size_t>( pixelRow ) * iterCols + pixelCol;
446 if ( noDataBuffer[idx] )
447 outputBlock->setValue( r, c, mNoData );
448 else
449 outputBlock->setValue( r, c, zBuffer[idx] / zFactor );
450 }
451 }
452
453 if ( !destProvider->writeBlock( outputBlock.get(), 1, tileLeft, tileTop ) )
454 {
455 throw QgsProcessingException( QObject::tr( "Could not write raster block: %1" ).arg( destProvider->error().summary() ) );
456 }
457 }
458 destProvider->setEditable( false );
459
460 if ( feedback && hasReportsDuringClose )
461 {
462 std::unique_ptr<QgsFeedback> scaledFeedback( QgsFeedback::createScaledFeedback( feedback, maxProgressDuringBlockWriting, 100.0 ) );
463 if ( !destProvider->closeWithProgress( scaledFeedback.get() ) )
464 {
465 if ( feedback->isCanceled() )
466 return {};
467 throw QgsProcessingException( QObject::tr( "Could not write raster dataset" ) );
468 }
469 }
470
471 QVariantMap outputs;
472 outputs.insert( u"OUTPUT"_s, outputFile );
473 return outputs;
474}
475
476
@ Advanced
Parameter is an advanced parameter which should be hidden from users by default.
Definition qgis.h:3880
@ Double
Double/float values.
Definition qgis.h:3921
bool isCanceled() const
Tells whether the operation has been canceled already.
Definition qgsfeedback.h:56
void setProgress(double progress)
Sets the current progress for the feedback object.
Definition qgsfeedback.h:65
static std::unique_ptr< QgsFeedback > createScaledFeedback(QgsFeedback *parentFeedback, double startPercentage, double endPercentage)
Returns a feedback object whose [0, 100] progression range will be mapped to parentFeedback [startPer...
virtual QgsRectangle extent() const
Returns the extent of the layer.
QgsCoordinateReferenceSystem crs
Definition qgsmaplayer.h:90
Contains information about the context in which a processing algorithm is executed.
Custom exception class for processing related exceptions.
Base class for providing feedback from a processing algorithm.
virtual void setProgressText(const QString &text)
Sets a progress report text string.
A raster band parameter for Processing algorithms.
A raster layer parameter for processing algorithms.
QgsRasterDataProvider * clone() const override=0
Clone itself, create deep copy.
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
Qgis::DataType dataType(int bandNo) const override=0
Returns data type for the band specified by number.
Iterator for sequentially processing raster cells.
Represents a raster layer.
int height() const
Returns the height of the (unclipped) raster.
int bandCount() const
Returns the number of bands in this layer.
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.
A rectangle specified with double values.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into c
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:6975