QGIS API Documentation 4.3.0-Master (7d9941090cd)
Loading...
Searching...
No Matches
qgsrelief.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsrelief.cpp - description
3 ---------------------------
4 begin : November 2011
5 copyright : (C) 2011 by Marco Hugentobler
6 email : marco dot hugentobler at sourcepole dot ch
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 "qgsrelief.h"
19
20#include <cfloat>
21#include <cpl_string.h>
22
23#include "qgis.h"
24#include "qgsaspectfilter.h"
25#include "qgsfeedback.h"
26#include "qgsgdalutils.h"
27#include "qgshillshadefilter.h"
28#include "qgslogger.h"
29#include "qgsrasterfilewriter.h"
30#include "qgsrasterlayer.h"
31#include "qgsslopefilter.h"
32
33#include <QColor>
34#include <QFile>
35#include <QString>
36#include <QTextStream>
37#include <QVector>
38
39using namespace Qt::StringLiterals;
40
41QgsRelief::QgsRelief( const QString &inputFile, const QString &outputFile, const QString &outputFormat )
42 : mInputFile( inputFile )
43 , mOutputFile( outputFile )
44 , mOutputFormat( outputFormat )
45 , mSlopeFilter( std::make_unique<QgsSlopeFilter>( inputFile, outputFile, outputFormat ) )
46 , mAspectFilter( std::make_unique<QgsAspectFilter>( inputFile, outputFile, outputFormat ) )
47 , mHillshadeFilter285( std::make_unique<QgsHillshadeFilter>( inputFile, outputFile, outputFormat, 285, 30 ) )
48 , mHillshadeFilter300( std::make_unique<QgsHillshadeFilter>( inputFile, outputFile, outputFormat, 300, 30 ) )
49 , mHillshadeFilter315( std::make_unique<QgsHillshadeFilter>( inputFile, outputFile, outputFormat, 315, 30 ) )
50{
51 /*mReliefColors = calculateOptimizedReliefClasses();
52 setDefaultReliefColors();*/
53}
54
55QgsRelief::~QgsRelief() = default;
56
58{
59 mReliefColors.clear();
60}
61
63{
64 mReliefColors.push_back( color );
65}
66
67void QgsRelief::setDefaultReliefColors()
68{
70 addReliefColorClass( QgsRasterReliefColor( QColor( 9, 176, 76 ), 0, 200 ) );
71 addReliefColorClass( QgsRasterReliefColor( QColor( 20, 228, 128 ), 200, 500 ) );
72 addReliefColorClass( QgsRasterReliefColor( QColor( 167, 239, 153 ), 500, 1000 ) );
73 addReliefColorClass( QgsRasterReliefColor( QColor( 218, 188, 143 ), 1000, 2000 ) );
74 addReliefColorClass( QgsRasterReliefColor( QColor( 233, 158, 91 ), 2000, 4000 ) );
75 addReliefColorClass( QgsRasterReliefColor( QColor( 255, 255, 255 ), 4000, 9000 ) );
76}
77
79{
80 auto inputLayer = std::make_unique< QgsRasterLayer >( mInputFile, u"relief"_s, u"gdal"_s );
81 if ( !inputLayer->isValid() )
82 {
84 }
85 QgsRasterDataProvider *inputProvider = inputLayer->dataProvider();
86 if ( !inputProvider )
87 {
89 }
90
91 const int xSize = inputProvider->xSize();
92 const int ySize = inputProvider->ySize();
93 mCellSizeX = std::fabs( inputLayer->rasterUnitsPerPixelX() );
94 mCellSizeY = std::fabs( inputLayer->rasterUnitsPerPixelY() );
95
96 //output driver
97 QgsRasterFileWriter writer( mOutputFile );
98 writer.setOutputFormat( mOutputFormat );
99 writer.setCreationOptions( mCreationOptions );
100 std::unique_ptr<QgsRasterDataProvider> outputProvider( writer.createMultiBandRaster( Qgis::DataType::Byte, xSize, ySize, inputProvider->extent(), inputProvider->crs(), 3 ) );
101 if ( !outputProvider )
102 {
104 }
105
106 //initialize dependency filters with cell sizes
107 mHillshadeFilter285->setCellSizeX( mCellSizeX );
108 mHillshadeFilter285->setCellSizeY( mCellSizeY );
109 mHillshadeFilter285->setZFactor( mZFactor );
110 mHillshadeFilter300->setCellSizeX( mCellSizeX );
111 mHillshadeFilter300->setCellSizeY( mCellSizeY );
112 mHillshadeFilter300->setZFactor( mZFactor );
113 mHillshadeFilter315->setCellSizeX( mCellSizeX );
114 mHillshadeFilter315->setCellSizeY( mCellSizeY );
115 mHillshadeFilter315->setZFactor( mZFactor );
116 mSlopeFilter->setCellSizeX( mCellSizeX );
117 mSlopeFilter->setCellSizeY( mCellSizeY );
118 mSlopeFilter->setZFactor( mZFactor );
119 mAspectFilter->setCellSizeX( mCellSizeX );
120 mAspectFilter->setCellSizeY( mCellSizeY );
121 mAspectFilter->setZFactor( mZFactor );
122
123 if ( inputProvider->sourceHasNoDataValue( 1 ) )
124 {
125 mInputNodataValue = inputProvider->sourceNoDataValue( 1 );
126 }
127
128 mSlopeFilter->setInputNodataValue( mInputNodataValue );
129 mAspectFilter->setInputNodataValue( mInputNodataValue );
130 mHillshadeFilter285->setInputNodataValue( mInputNodataValue );
131 mHillshadeFilter300->setInputNodataValue( mInputNodataValue );
132 mHillshadeFilter315->setInputNodataValue( mInputNodataValue );
133
134 outputProvider->setNoDataValue( 1, mOutputNodataValue );
135 outputProvider->setNoDataValue( 2, mOutputNodataValue );
136 outputProvider->setNoDataValue( 3, mOutputNodataValue );
137 mSlopeFilter->setOutputNodataValue( mOutputNodataValue );
138 mAspectFilter->setOutputNodataValue( mOutputNodataValue );
139 mHillshadeFilter285->setOutputNodataValue( mOutputNodataValue );
140 mHillshadeFilter300->setOutputNodataValue( mOutputNodataValue );
141 mHillshadeFilter315->setOutputNodataValue( mOutputNodataValue );
142
143 if ( ySize < 3 ) //we require at least three rows (should be true for most datasets)
144 {
146 }
147
148 // iterate row-by-row over the raster
149 QgsRasterIterator iter( inputProvider );
150 iter.setMaximumTileWidth( xSize );
151 iter.setMaximumTileHeight( 1 );
152 iter.startRasterRead( 1, xSize, ySize, inputProvider->extent() );
153
154 //keep only three scanlines in memory at a time
155 std::vector<float> scanLine1( xSize );
156 std::vector<float> scanLine2( xSize );
157 std::vector<float> scanLine3( xSize );
158 std::vector<unsigned char> resultRedLine( xSize );
159 std::vector<unsigned char> resultGreenLine( xSize );
160 std::vector<unsigned char> resultBlueLine( xSize );
161
162 const double maxProgressDuringBlockWriting = outputProvider->hasReportsDuringClose() ? 50.0 : 100.0;
163
164 auto readRow = [&iter, this]( std::vector<float> &scanLine ) {
165 int iterCols = 0;
166 int iterRows = 0;
167 int iterLeft = 0;
168 int iterTop = 0;
169 std::unique_ptr<QgsRasterBlock> block;
170 if ( iter.readNextRasterPart( 1, iterCols, iterRows, block, iterLeft, iterTop ) && block && block->isValid() )
171 {
172 bool isNoData = false;
173 for ( int j = 0; j < iterCols; ++j )
174 {
175 const double val = block->valueAndNoData( 0, j, isNoData );
176 scanLine[j] = isNoData ? mInputNodataValue : static_cast<float>( val );
177 }
178 }
179 else
180 {
181 std::fill( scanLine.begin(), scanLine.end(), mInputNodataValue );
182 }
183 };
184
185 //values outside the layer extent (if the 3x3 window is on the border) are sent to the processing method as (input) nodata values
186 for ( int i = 0; i < ySize; ++i )
187 {
188 if ( feedback )
189 {
190 feedback->setProgress( maxProgressDuringBlockWriting * i / static_cast<double>( ySize ) );
191 }
192
193 if ( feedback && feedback->isCanceled() )
194 {
195 return Result::Canceled;
196 }
197
198 if ( i == 0 )
199 {
200 //fill scanline 1 with (input) nodata for the values above the first row and feed scanline2 with the first row
201 std::fill( scanLine1.begin(), scanLine1.end(), mInputNodataValue );
202 readRow( scanLine2 );
203 }
204 else
205 {
206 //normally fetch only scanLine3 and release scanline 1 if we move forward one row
207 std::swap( scanLine1, scanLine2 );
208 std::swap( scanLine2, scanLine3 );
209 }
210
211 if ( i == ySize - 1 ) //fill the row below the bottom with nodata values
212 {
213 std::fill( scanLine3.begin(), scanLine3.end(), mInputNodataValue );
214 }
215 else
216 {
217 readRow( scanLine3 );
218 }
219
220 for ( int j = 0; j < xSize; ++j )
221 {
222 bool resultOk = false;
223 if ( j == 0 )
224 {
225 resultOk = processNineCellWindow(
226 &mInputNodataValue,
227 &scanLine1[j],
228 &scanLine1[j + 1],
229 &mInputNodataValue,
230 &scanLine2[j],
231 &scanLine2[j + 1],
232 &mInputNodataValue,
233 &scanLine3[j],
234 &scanLine3[j + 1],
235 &resultRedLine[j],
236 &resultGreenLine[j],
237 &resultBlueLine[j]
238 );
239 }
240 else if ( j == xSize - 1 )
241 {
242 resultOk = processNineCellWindow(
243 &scanLine1[j - 1],
244 &scanLine1[j],
245 &mInputNodataValue,
246 &scanLine2[j - 1],
247 &scanLine2[j],
248 &mInputNodataValue,
249 &scanLine3[j - 1],
250 &scanLine3[j],
251 &mInputNodataValue,
252 &resultRedLine[j],
253 &resultGreenLine[j],
254 &resultBlueLine[j]
255 );
256 }
257 else
258 {
259 resultOk = processNineCellWindow(
260 &scanLine1[j - 1],
261 &scanLine1[j],
262 &scanLine1[j + 1],
263 &scanLine2[j - 1],
264 &scanLine2[j],
265 &scanLine2[j + 1],
266 &scanLine3[j - 1],
267 &scanLine3[j],
268 &scanLine3[j + 1],
269 &resultRedLine[j],
270 &resultGreenLine[j],
271 &resultBlueLine[j]
272 );
273 }
274
275 if ( !resultOk )
276 {
277 resultRedLine[j] = mOutputNodataValue;
278 resultGreenLine[j] = mOutputNodataValue;
279 resultBlueLine[j] = mOutputNodataValue;
280 }
281 }
282
283 auto redBlock = std::make_unique<QgsRasterBlock>( Qgis::DataType::Byte, xSize, 1 );
284 auto greenBlock = std::make_unique<QgsRasterBlock>( Qgis::DataType::Byte, xSize, 1 );
285 auto blueBlock = std::make_unique<QgsRasterBlock>( Qgis::DataType::Byte, xSize, 1 );
286 for ( int j = 0; j < xSize; ++j )
287 {
288 redBlock->setValue( 0, j, resultRedLine[j] );
289 greenBlock->setValue( 0, j, resultGreenLine[j] );
290 blueBlock->setValue( 0, j, resultBlueLine[j] );
291 }
292
293 if ( !outputProvider->writeBlock( redBlock.get(), 1, 0, i ) )
294 {
295 QgsDebugError( u"Raster IO Error"_s );
296 }
297 if ( !outputProvider->writeBlock( greenBlock.get(), 2, 0, i ) )
298 {
299 QgsDebugError( u"Raster IO Error"_s );
300 }
301 if ( !outputProvider->writeBlock( blueBlock.get(), 3, 0, i ) )
302 {
303 QgsDebugError( u"Raster IO Error"_s );
304 }
305 }
306
307 if ( feedback && outputProvider->hasReportsDuringClose() )
308 {
309 std::unique_ptr<QgsFeedback> scaledFeedback( QgsFeedback::createScaledFeedback( feedback, maxProgressDuringBlockWriting, 100.0 ) );
310 if ( !outputProvider->closeWithProgress( scaledFeedback.get() ) )
311 {
312 if ( feedback->isCanceled() )
313 return Result::Canceled;
315 }
316 }
317
318 if ( feedback )
319 {
320 feedback->setProgress( 100 );
321 }
322
323 if ( feedback && feedback->isCanceled() )
324 {
325 return Result::Canceled;
326 }
327
328 return Result::Success;
329}
330
331bool QgsRelief::processNineCellWindow( float *x1, float *x2, float *x3, float *x4, float *x5, float *x6, float *x7, float *x8, float *x9, unsigned char *red, unsigned char *green, unsigned char *blue )
332{
333 //1. component: color and hillshade from 300 degrees
334 int r = 0;
335 int g = 0;
336 int b = 0;
337
338 const float hillShadeValue300 = mHillshadeFilter300->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
339 if ( hillShadeValue300 != mOutputNodataValue )
340 {
341 if ( !getElevationColor( *x5, &r, &g, &b ) )
342 {
343 r = hillShadeValue300;
344 g = hillShadeValue300;
345 b = hillShadeValue300;
346 }
347 else
348 {
349 r = r / 2.0 + hillShadeValue300 / 2.0;
350 g = g / 2.0 + hillShadeValue300 / 2.0;
351 b = b / 2.0 + hillShadeValue300 / 2.0;
352 }
353 }
354
355 //2. component: hillshade and slope
356 const float hillShadeValue315 = mHillshadeFilter315->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
357 const float slope = mSlopeFilter->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
358 if ( hillShadeValue315 != mOutputNodataValue && slope != mOutputNodataValue )
359 {
360 int r2, g2, b2;
361 if ( slope > 15 )
362 {
363 r2 = 0 / 2.0 + hillShadeValue315 / 2.0;
364 g2 = 0 / 2.0 + hillShadeValue315 / 2.0;
365 b2 = 0 / 2.0 + hillShadeValue315 / 2.0;
366 }
367 else if ( slope >= 1 )
368 {
369 const int slopeValue = 255 - ( slope / 15.0 * 255.0 );
370 r2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
371 g2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
372 b2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
373 }
374 else
375 {
376 r2 = hillShadeValue315;
377 g2 = hillShadeValue315;
378 b2 = hillShadeValue315;
379 }
380
381 //combine with r,g,b with 70 percentage coverage
382 r = r * 0.7 + r2 * 0.3;
383 g = g * 0.7 + g2 * 0.3;
384 b = b * 0.7 + b2 * 0.3;
385 }
386
387 //3. combine yellow aspect with 10% transparency, illumination from 285 degrees
388 const float hillShadeValue285 = mHillshadeFilter285->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
389 const float aspect = mAspectFilter->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
390 if ( hillShadeValue285 != mOutputNodataValue && aspect != mOutputNodataValue )
391 {
392 double angle_diff = std::fabs( 285 - aspect );
393 if ( angle_diff > 180 )
394 {
395 angle_diff -= 180;
396 }
397
398 int r3, g3, b3;
399 if ( angle_diff < 90 )
400 {
401 const int aspectVal = ( 1 - std::cos( angle_diff * M_PI / 180 ) ) * 255;
402 r3 = 0.5 * 255 + hillShadeValue315 * 0.5;
403 g3 = 0.5 * 255 + hillShadeValue315 * 0.5;
404 b3 = 0.5 * aspectVal + hillShadeValue315 * 0.5;
405 }
406 else //white
407 {
408 r3 = 0.5 * 255 + hillShadeValue315 * 0.5;
409 g3 = 0.5 * 255 + hillShadeValue315 * 0.5;
410 b3 = 0.5 * 255 + hillShadeValue315 * 0.5;
411 }
412
413 r = r3 * 0.1 + r * 0.9;
414 g = g3 * 0.1 + g * 0.9;
415 b = b3 * 0.1 + b * 0.9;
416 }
417
418 *red = ( unsigned char ) r;
419 *green = ( unsigned char ) g;
420 *blue = ( unsigned char ) b;
421 return true;
422}
423
424bool QgsRelief::getElevationColor( double elevation, int *red, int *green, int *blue ) const
425{
426 QList<QgsRasterReliefColor>::const_iterator reliefColorIt = mReliefColors.constBegin();
427 for ( ; reliefColorIt != mReliefColors.constEnd(); ++reliefColorIt )
428 {
429 if ( elevation >= reliefColorIt->minElevation && elevation <= reliefColorIt->maxElevation )
430 {
431 const QColor &c = reliefColorIt->color;
432 *red = c.red();
433 *green = c.green();
434 *blue = c.blue();
435
436 return true;
437 }
438 }
439 return false;
440}
441
442//this function is mainly there for debugging
444{
445 auto inputLayer = std::make_unique< QgsRasterLayer >( mInputFile, u"relief"_s, u"gdal"_s );
446 if ( !inputLayer->isValid() )
447 {
448 return false;
449 }
450
451 QgsRasterDataProvider *inputProvider = inputLayer->dataProvider();
452 if ( !inputProvider )
453 {
454 return false;
455 }
456
457 //open first raster band for reading (elevation raster is always single band)
458 // get minimum and maximum of elevation raster -> 252 elevation classes
460
461 //go through raster cells and get frequency of classes
462
463 //store elevation frequency in 252 elevation classes
464 std::vector<double> frequency( 252, 0.0 );
465 const double frequencyClassRange = ( stats.maximumValue - stats.minimumValue ) / 252.0;
466
467 QgsRasterIterator iter( inputProvider );
468 iter.startRasterRead( 1, inputProvider->xSize(), inputProvider->ySize(), inputProvider->extent() );
469
470 int iterCols = 0;
471 int iterRows = 0;
472 int iterLeft = 0;
473 int iterTop = 0;
474 std::unique_ptr<QgsRasterBlock> block;
475
476 bool isNoData = false;
477 int elevationClass = -1;
478 while ( iter.readNextRasterPart( 1, iterCols, iterRows, block, iterLeft, iterTop ) )
479 {
480 for ( int row = 0; row < iterRows; ++row )
481 {
482 for ( int col = 0; col < iterCols; ++col )
483 {
484 const double value = block->valueAndNoData( row, col, isNoData );
485 if ( isNoData )
486 {
487 continue;
488 }
489
490 elevationClass = frequencyClassForElevation( value, stats.minimumValue, frequencyClassRange );
491 if ( elevationClass >= 0 && elevationClass < 252 )
492 {
493 frequency[elevationClass] += 1.0;
494 }
495 }
496 }
497 }
498
499 //log10 transformation for all frequency values
500 for ( int i = 0; i < 252; ++i )
501 {
502 frequency[i] = std::log10( frequency[i] );
503 }
504
505 //write out frequency values to csv file for debugging
506 QFile outFile( file );
507 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
508 {
509 return false;
510 }
511
512 QTextStream outstream( &outFile );
513 for ( int i = 0; i < 252; ++i )
514 {
515 outstream << QString::number( i ) + ',' + QString::number( frequency[i] ) << Qt::endl;
516 }
517 outFile.close();
518 return true;
519}
520
522{
523 auto inputLayer = std::make_unique< QgsRasterLayer >( mInputFile, u"relief"_s, u"gdal"_s );
524 if ( !inputLayer->isValid() )
525 {
526 return {};
527 }
528 return QgsRasterLayerUtils::calculateOptimizedReliefClasses( inputLayer->dataProvider(), 1 );
529}
530
531int QgsRelief::frequencyClassForElevation( double elevation, double minElevation, double elevationClassRange )
532{
533 return ( elevation - minElevation ) / elevationClassRange;
534}
@ Byte
Eight bit unsigned integer (quint8).
Definition qgis.h:395
Calculates aspect values in a window of 3x3 cells based on first order derivatives in x- and y- direc...
virtual QgsCoordinateReferenceSystem crs() const =0
Returns the coordinate system for the data source.
Base class for feedback objects to be used for cancellation of something running in a worker thread.
Definition qgsfeedback.h:44
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...
A hillshade filter.
The RasterBandStats struct is a container for statistics about a single raster band.
double minimumValue
The minimum cell value in the raster band.
double maximumValue
The maximum cell value in the raster band.
Base class for raster data providers.
virtual bool sourceHasNoDataValue(int bandNo) const
Returns true if source band has no data value.
virtual double sourceNoDataValue(int bandNo) const
Value representing no data value.
QgsRectangle extent() const override=0
Returns the extent of the layer.
The raster file writer which allows you to save a raster to a new file.
void setCreationOptions(const QStringList &options)
Sets a list of data source creation options to use when creating the output raster file.
QgsRasterDataProvider * createMultiBandRaster(Qgis::DataType dataType, int width, int height, const QgsRectangle &extent, const QgsCoordinateReferenceSystem &crs, int nBands) SIP_FACTORY
Create a raster file with given number of bands without initializing the pixel data.
void setOutputFormat(const QString &format)
Sets the output format.
virtual int xSize() const
Gets raster size.
Q_DECL_DEPRECATED QgsRasterBandStats bandStatistics(int bandNo, int stats, const QgsRectangle &extent=QgsRectangle(), int sampleSize=0, QgsRasterBlockFeedback *feedback=nullptr)
Returns the band statistics.
virtual int ySize() const
Iterator for sequentially processing raster cells.
void setMaximumTileWidth(int w)
Sets the maximum tile width returned during iteration.
bool readNextRasterPart(int bandNumber, int &nCols, int &nRows, QgsRasterBlock **block, int &topLeftCol, int &topLeftRow)
Fetches next part of raster data, caller takes ownership of the block and caller should delete the bl...
void startRasterRead(int bandNumber, qgssize nCols, qgssize nRows, const QgsRectangle &extent, QgsRasterBlockFeedback *feedback=nullptr)
Start reading of raster band.
void setMaximumTileHeight(int h)
Sets the minimum tile height returned during iteration.
static QList< QgsRasterReliefColor > calculateOptimizedReliefClasses(QgsRasterDataProvider *provider, int band)
Calculates optimized relief class breaks according with the method of Buenzli (2011) using an iterati...
Defines elevation range and color for raster relief coloring.
void clearReliefColors()
Clears all existing relief colors.
Definition qgsrelief.cpp:57
Result
Calculation results.
Definition qgsrelief.h:59
@ Canceled
Operation was canceled.
Definition qgsrelief.h:64
@ Success
Calculation succeeded.
Definition qgsrelief.h:60
@ OutputCreationFailed
Creation of output layer failed.
Definition qgsrelief.h:62
@ InvalidInput
Invalid input layer.
Definition qgsrelief.h:61
bool exportFrequencyDistributionToCsv(const QString &file)
Writes frequency of elevation values to a file for manual inspection.
QgsRelief(const QString &inputFile, const QString &outputFile, const QString &outputFormat)
Constructor for QgsRelief.
Definition qgsrelief.cpp:41
void addReliefColorClass(const QgsRasterReliefColor &color)
Adds a relief color.
Definition qgsrelief.cpp:62
QList< QgsRasterReliefColor > calculateOptimizedReliefClasses()
Calculates class breaks according with the method of Buenzli (2011) using an iterative algorithm for ...
QgsRelief::Result processRaster(QgsFeedback *feedback=nullptr)
Starts the calculation.
Definition qgsrelief.cpp:78
Calculates slope values in a window of 3x3 cells based on first order derivatives in x- and y- direct...
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
#define QgsDebugError(str)
Definition qgslogger.h:71