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() );
100 if ( !outputProvider )
106 mHillshadeFilter285->setCellSizeX( mCellSizeX );
107 mHillshadeFilter285->setCellSizeY( mCellSizeY );
108 mHillshadeFilter285->setZFactor( mZFactor );
109 mHillshadeFilter300->setCellSizeX( mCellSizeX );
110 mHillshadeFilter300->setCellSizeY( mCellSizeY );
111 mHillshadeFilter300->setZFactor( mZFactor );
112 mHillshadeFilter315->setCellSizeX( mCellSizeX );
113 mHillshadeFilter315->setCellSizeY( mCellSizeY );
114 mHillshadeFilter315->setZFactor( mZFactor );
115 mSlopeFilter->setCellSizeX( mCellSizeX );
116 mSlopeFilter->setCellSizeY( mCellSizeY );
117 mSlopeFilter->setZFactor( mZFactor );
118 mAspectFilter->setCellSizeX( mCellSizeX );
119 mAspectFilter->setCellSizeY( mCellSizeY );
120 mAspectFilter->setZFactor( mZFactor );
128 mInputNodataValue = -9999;
131 mSlopeFilter->setInputNodataValue( mInputNodataValue );
132 mAspectFilter->setInputNodataValue( mInputNodataValue );
133 mHillshadeFilter285->setInputNodataValue( mInputNodataValue );
134 mHillshadeFilter300->setInputNodataValue( mInputNodataValue );
135 mHillshadeFilter315->setInputNodataValue( mInputNodataValue );
137 mOutputNodataValue = -9999;
138 outputProvider->setNoDataValue( 1, mOutputNodataValue );
139 outputProvider->setNoDataValue( 2, mOutputNodataValue );
140 outputProvider->setNoDataValue( 3, mOutputNodataValue );
141 mSlopeFilter->setOutputNodataValue( mOutputNodataValue );
142 mAspectFilter->setOutputNodataValue( mOutputNodataValue );
143 mHillshadeFilter285->setOutputNodataValue( mOutputNodataValue );
144 mHillshadeFilter300->setOutputNodataValue( mOutputNodataValue );
145 mHillshadeFilter315->setOutputNodataValue( mOutputNodataValue );
159 std::vector<float> scanLine1( xSize );
160 std::vector<float> scanLine2( xSize );
161 std::vector<float> scanLine3( xSize );
162 std::vector<unsigned char> resultRedLine( xSize );
163 std::vector<unsigned char> resultGreenLine( xSize );
164 std::vector<unsigned char> resultBlueLine( xSize );
166 const double maxProgressDuringBlockWriting = outputProvider->hasReportsDuringClose() ? 50.0 : 100.0;
168 auto readRow = [&iter,
this]( std::vector<float> &scanLine ) {
173 std::unique_ptr<QgsRasterBlock> block;
174 if ( iter.
readNextRasterPart( 1, iterCols, iterRows, block, iterLeft, iterTop ) && block && block->isValid() )
176 bool isNoData =
false;
177 for (
int j = 0; j < iterCols; ++j )
179 const double val = block->valueAndNoData( 0, j, isNoData );
180 scanLine[j] = isNoData ? mInputNodataValue :
static_cast<float>( val );
185 std::fill( scanLine.begin(), scanLine.end(), mInputNodataValue );
190 for (
int i = 0; i < ySize; ++i )
194 feedback->
setProgress( maxProgressDuringBlockWriting * i /
static_cast<double>( ySize ) );
205 std::fill( scanLine1.begin(), scanLine1.end(), mInputNodataValue );
206 readRow( scanLine2 );
211 std::swap( scanLine1, scanLine2 );
212 std::swap( scanLine2, scanLine3 );
215 if ( i == ySize - 1 )
217 std::fill( scanLine3.begin(), scanLine3.end(), mInputNodataValue );
221 readRow( scanLine3 );
224 for (
int j = 0; j < xSize; ++j )
226 bool resultOk =
false;
229 resultOk = processNineCellWindow(
244 else if ( j == xSize - 1 )
246 resultOk = processNineCellWindow(
263 resultOk = processNineCellWindow(
281 resultRedLine[j] = mOutputNodataValue;
282 resultGreenLine[j] = mOutputNodataValue;
283 resultBlueLine[j] = mOutputNodataValue;
290 for (
int j = 0; j < xSize; ++j )
292 redBlock->setValue( 0, j, resultRedLine[j] );
293 greenBlock->setValue( 0, j, resultGreenLine[j] );
294 blueBlock->setValue( 0, j, resultBlueLine[j] );
297 if ( !outputProvider->writeBlock( redBlock.get(), 1, 0, i ) )
301 if ( !outputProvider->writeBlock( greenBlock.get(), 2, 0, i ) )
305 if ( !outputProvider->writeBlock( blueBlock.get(), 3, 0, i ) )
311 if ( feedback && outputProvider->hasReportsDuringClose() )
314 if ( !outputProvider->closeWithProgress( scaledFeedback.get() ) )
335bool 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 )
342 const float hillShadeValue300 = mHillshadeFilter300->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
343 if ( hillShadeValue300 != mOutputNodataValue )
345 if ( !getElevationColor( *x5, &r, &g, &b ) )
347 r = hillShadeValue300;
348 g = hillShadeValue300;
349 b = hillShadeValue300;
353 r = r / 2.0 + hillShadeValue300 / 2.0;
354 g = g / 2.0 + hillShadeValue300 / 2.0;
355 b = b / 2.0 + hillShadeValue300 / 2.0;
360 const float hillShadeValue315 = mHillshadeFilter315->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
361 const float slope = mSlopeFilter->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
362 if ( hillShadeValue315 != mOutputNodataValue && slope != mOutputNodataValue )
367 r2 = 0 / 2.0 + hillShadeValue315 / 2.0;
368 g2 = 0 / 2.0 + hillShadeValue315 / 2.0;
369 b2 = 0 / 2.0 + hillShadeValue315 / 2.0;
371 else if ( slope >= 1 )
373 const int slopeValue = 255 - ( slope / 15.0 * 255.0 );
374 r2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
375 g2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
376 b2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
380 r2 = hillShadeValue315;
381 g2 = hillShadeValue315;
382 b2 = hillShadeValue315;
386 r = r * 0.7 + r2 * 0.3;
387 g = g * 0.7 + g2 * 0.3;
388 b = b * 0.7 + b2 * 0.3;
392 const float hillShadeValue285 = mHillshadeFilter285->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
393 const float aspect = mAspectFilter->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
394 if ( hillShadeValue285 != mOutputNodataValue && aspect != mOutputNodataValue )
396 double angle_diff = std::fabs( 285 - aspect );
397 if ( angle_diff > 180 )
403 if ( angle_diff < 90 )
405 const int aspectVal = ( 1 - std::cos( angle_diff * M_PI / 180 ) ) * 255;
406 r3 = 0.5 * 255 + hillShadeValue315 * 0.5;
407 g3 = 0.5 * 255 + hillShadeValue315 * 0.5;
408 b3 = 0.5 * aspectVal + hillShadeValue315 * 0.5;
412 r3 = 0.5 * 255 + hillShadeValue315 * 0.5;
413 g3 = 0.5 * 255 + hillShadeValue315 * 0.5;
414 b3 = 0.5 * 255 + hillShadeValue315 * 0.5;
417 r = r3 * 0.1 + r * 0.9;
418 g = g3 * 0.1 + g * 0.9;
419 b = b3 * 0.1 + b * 0.9;
422 *red = (
unsigned char ) r;
423 *green = (
unsigned char ) g;
424 *blue = (
unsigned char ) b;
428bool QgsRelief::getElevationColor(
double elevation,
int *red,
int *green,
int *blue )
const
430 QList<QgsRasterReliefColor>::const_iterator reliefColorIt = mReliefColors.constBegin();
431 for ( ; reliefColorIt != mReliefColors.constEnd(); ++reliefColorIt )
433 if ( elevation >= reliefColorIt->minElevation && elevation <= reliefColorIt->maxElevation )
435 const QColor &
c = reliefColorIt->color;
449 auto inputLayer = std::make_unique< QgsRasterLayer >( mInputFile, u
"relief"_s, u
"gdal"_s );
450 if ( !inputLayer->isValid() )
456 if ( !inputProvider )
468 std::vector<double> frequency( 252, 0.0 );
478 std::unique_ptr<QgsRasterBlock> block;
480 bool isNoData =
false;
481 int elevationClass = -1;
484 for (
int row = 0; row < iterRows; ++row )
486 for (
int col = 0; col < iterCols; ++col )
488 const double value = block->valueAndNoData( row, col, isNoData );
494 elevationClass = frequencyClassForElevation( value, stats.
minimumValue, frequencyClassRange );
495 if ( elevationClass >= 0 && elevationClass < 252 )
497 frequency[elevationClass] += 1.0;
504 for (
int i = 0; i < 252; ++i )
506 frequency[i] = std::log10( frequency[i] );
510 QFile outFile( file );
511 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
516 QTextStream outstream( &outFile );
517 for (
int i = 0; i < 252; ++i )
519 outstream << QString::number( i ) +
',' + QString::number( frequency[i] ) << Qt::endl;
527 auto inputLayer = std::make_unique< QgsRasterLayer >( mInputFile, u
"relief"_s, u
"gdal"_s );
528 if ( !inputLayer->isValid() )
535int QgsRelief::frequencyClassForElevation(
double elevation,
double minElevation,
double elevationClassRange )
537 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.
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)