21#include <cpl_string.h>
39using namespace Qt::StringLiterals;
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 ) )
59 mReliefColors.clear();
64 mReliefColors.push_back( color );
67void QgsRelief::setDefaultReliefColors()
80 auto inputLayer = std::make_unique< QgsRasterLayer >( mInputFile, u
"relief"_s, u
"gdal"_s );
81 if ( !inputLayer->isValid() )
91 const int xSize = inputProvider->
xSize();
92 const int ySize = inputProvider->
ySize();
93 mCellSizeX = std::fabs( inputLayer->rasterUnitsPerPixelX() );
94 mCellSizeY = std::fabs( inputLayer->rasterUnitsPerPixelY() );
101 if ( !outputProvider )
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 );
128 mSlopeFilter->setInputNodataValue( mInputNodataValue );
129 mAspectFilter->setInputNodataValue( mInputNodataValue );
130 mHillshadeFilter285->setInputNodataValue( mInputNodataValue );
131 mHillshadeFilter300->setInputNodataValue( mInputNodataValue );
132 mHillshadeFilter315->setInputNodataValue( mInputNodataValue );
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 );
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 );
162 const double maxProgressDuringBlockWriting = outputProvider->hasReportsDuringClose() ? 50.0 : 100.0;
164 auto readRow = [&iter,
this]( std::vector<float> &scanLine ) {
169 std::unique_ptr<QgsRasterBlock> block;
170 if ( iter.
readNextRasterPart( 1, iterCols, iterRows, block, iterLeft, iterTop ) && block && block->isValid() )
172 bool isNoData =
false;
173 for (
int j = 0; j < iterCols; ++j )
175 const double val = block->valueAndNoData( 0, j, isNoData );
176 scanLine[j] = isNoData ? mInputNodataValue :
static_cast<float>( val );
181 std::fill( scanLine.begin(), scanLine.end(), mInputNodataValue );
186 for (
int i = 0; i < ySize; ++i )
190 feedback->
setProgress( maxProgressDuringBlockWriting * i /
static_cast<double>( ySize ) );
201 std::fill( scanLine1.begin(), scanLine1.end(), mInputNodataValue );
202 readRow( scanLine2 );
207 std::swap( scanLine1, scanLine2 );
208 std::swap( scanLine2, scanLine3 );
211 if ( i == ySize - 1 )
213 std::fill( scanLine3.begin(), scanLine3.end(), mInputNodataValue );
217 readRow( scanLine3 );
220 for (
int j = 0; j < xSize; ++j )
222 bool resultOk =
false;
225 resultOk = processNineCellWindow(
240 else if ( j == xSize - 1 )
242 resultOk = processNineCellWindow(
259 resultOk = processNineCellWindow(
277 resultRedLine[j] = mOutputNodataValue;
278 resultGreenLine[j] = mOutputNodataValue;
279 resultBlueLine[j] = mOutputNodataValue;
286 for (
int j = 0; j < xSize; ++j )
288 redBlock->setValue( 0, j, resultRedLine[j] );
289 greenBlock->setValue( 0, j, resultGreenLine[j] );
290 blueBlock->setValue( 0, j, resultBlueLine[j] );
293 if ( !outputProvider->writeBlock( redBlock.get(), 1, 0, i ) )
297 if ( !outputProvider->writeBlock( greenBlock.get(), 2, 0, i ) )
301 if ( !outputProvider->writeBlock( blueBlock.get(), 3, 0, i ) )
307 if ( feedback && outputProvider->hasReportsDuringClose() )
310 if ( !outputProvider->closeWithProgress( scaledFeedback.get() ) )
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 )
338 const float hillShadeValue300 = mHillshadeFilter300->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
339 if ( hillShadeValue300 != mOutputNodataValue )
341 if ( !getElevationColor( *x5, &r, &g, &b ) )
343 r = hillShadeValue300;
344 g = hillShadeValue300;
345 b = hillShadeValue300;
349 r = r / 2.0 + hillShadeValue300 / 2.0;
350 g = g / 2.0 + hillShadeValue300 / 2.0;
351 b = b / 2.0 + hillShadeValue300 / 2.0;
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 )
363 r2 = 0 / 2.0 + hillShadeValue315 / 2.0;
364 g2 = 0 / 2.0 + hillShadeValue315 / 2.0;
365 b2 = 0 / 2.0 + hillShadeValue315 / 2.0;
367 else if ( slope >= 1 )
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;
376 r2 = hillShadeValue315;
377 g2 = hillShadeValue315;
378 b2 = hillShadeValue315;
382 r = r * 0.7 + r2 * 0.3;
383 g = g * 0.7 + g2 * 0.3;
384 b = b * 0.7 + b2 * 0.3;
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 )
392 double angle_diff = std::fabs( 285 - aspect );
393 if ( angle_diff > 180 )
399 if ( angle_diff < 90 )
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;
408 r3 = 0.5 * 255 + hillShadeValue315 * 0.5;
409 g3 = 0.5 * 255 + hillShadeValue315 * 0.5;
410 b3 = 0.5 * 255 + hillShadeValue315 * 0.5;
413 r = r3 * 0.1 + r * 0.9;
414 g = g3 * 0.1 + g * 0.9;
415 b = b3 * 0.1 + b * 0.9;
418 *red = (
unsigned char ) r;
419 *green = (
unsigned char ) g;
420 *blue = (
unsigned char ) b;
424bool QgsRelief::getElevationColor(
double elevation,
int *red,
int *green,
int *blue )
const
426 QList<QgsRasterReliefColor>::const_iterator reliefColorIt = mReliefColors.constBegin();
427 for ( ; reliefColorIt != mReliefColors.constEnd(); ++reliefColorIt )
429 if ( elevation >= reliefColorIt->minElevation && elevation <= reliefColorIt->maxElevation )
431 const QColor &
c = reliefColorIt->color;
445 auto inputLayer = std::make_unique< QgsRasterLayer >( mInputFile, u
"relief"_s, u
"gdal"_s );
446 if ( !inputLayer->isValid() )
452 if ( !inputProvider )
464 std::vector<double> frequency( 252, 0.0 );
474 std::unique_ptr<QgsRasterBlock> block;
476 bool isNoData =
false;
477 int elevationClass = -1;
480 for (
int row = 0; row < iterRows; ++row )
482 for (
int col = 0; col < iterCols; ++col )
484 const double value = block->valueAndNoData( row, col, isNoData );
490 elevationClass = frequencyClassForElevation( value, stats.
minimumValue, frequencyClassRange );
491 if ( elevationClass >= 0 && elevationClass < 252 )
493 frequency[elevationClass] += 1.0;
500 for (
int i = 0; i < 252; ++i )
502 frequency[i] = std::log10( frequency[i] );
506 QFile outFile( file );
507 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
512 QTextStream outstream( &outFile );
513 for (
int i = 0; i < 252; ++i )
515 outstream << QString::number( i ) +
',' + QString::number( frequency[i] ) << Qt::endl;
523 auto inputLayer = std::make_unique< QgsRasterLayer >( mInputFile, u
"relief"_s, u
"gdal"_s );
524 if ( !inputLayer->isValid() )
531int QgsRelief::frequencyClassForElevation(
double elevation,
double minElevation,
double elevationClassRange )
533 return ( elevation - minElevation ) / elevationClassRange;
@ Byte
Eight bit unsigned integer (quint8).
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.
bool isCanceled() const
Tells whether the operation has been canceled already.
void setProgress(double progress)
Sets the current progress for the feedback object.
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...
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.
Result
Calculation results.
@ Canceled
Operation was canceled.
@ Success
Calculation succeeded.
@ OutputCreationFailed
Creation of output layer failed.
@ InvalidInput
Invalid input layer.
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.
void addReliefColorClass(const QgsRasterReliefColor &color)
Adds a relief color.
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.
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)