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