21#include <cpl_string.h>
38 : mInputFile( inputFile )
39 , mOutputFile( outputFile )
40 , mOutputFormat( outputFormat )
41 , mSlopeFilter( std::make_unique<
QgsSlopeFilter>( inputFile, outputFile, outputFormat ) )
42 , mAspectFilter( std::make_unique<
QgsAspectFilter>( inputFile, outputFile, outputFormat ) )
43 , mHillshadeFilter285( std::make_unique<
QgsHillshadeFilter>( inputFile, outputFile, outputFormat, 285, 30 ) )
44 , mHillshadeFilter300( std::make_unique<
QgsHillshadeFilter>( inputFile, outputFile, outputFormat, 300, 30 ) )
45 , mHillshadeFilter315( std::make_unique<
QgsHillshadeFilter>( inputFile, outputFile, outputFormat, 315, 30 ) )
55 mReliefColors.clear();
60 mReliefColors.push_back( color );
63void QgsRelief::setDefaultReliefColors()
85 GDALDriverH outputDriver = openOutputDriver();
98 mHillshadeFilter285->setCellSizeX( mCellSizeX );
99 mHillshadeFilter285->setCellSizeY( mCellSizeY );
100 mHillshadeFilter285->setZFactor( mZFactor );
101 mHillshadeFilter300->setCellSizeX( mCellSizeX );
102 mHillshadeFilter300->setCellSizeY( mCellSizeY );
103 mHillshadeFilter300->setZFactor( mZFactor );
104 mHillshadeFilter315->setCellSizeX( mCellSizeX );
105 mHillshadeFilter315->setCellSizeY( mCellSizeY );
106 mHillshadeFilter315->setZFactor( mZFactor );
107 mSlopeFilter->setCellSizeX( mCellSizeX );
108 mSlopeFilter->setCellSizeY( mCellSizeY );
109 mSlopeFilter->setZFactor( mZFactor );
110 mAspectFilter->setCellSizeX( mCellSizeX );
111 mAspectFilter->setCellSizeY( mCellSizeY );
112 mAspectFilter->setZFactor( mZFactor );
115 GDALRasterBandH rasterBand = GDALGetRasterBand( inputDataset.get(), 1 );
120 mInputNodataValue = GDALGetRasterNoDataValue( rasterBand,
nullptr );
121 mSlopeFilter->setInputNodataValue( mInputNodataValue );
122 mAspectFilter->setInputNodataValue( mInputNodataValue );
123 mHillshadeFilter285->setInputNodataValue( mInputNodataValue );
124 mHillshadeFilter300->setInputNodataValue( mInputNodataValue );
125 mHillshadeFilter315->setInputNodataValue( mInputNodataValue );
127 GDALRasterBandH outputRedBand = GDALGetRasterBand( outputDataset.get(), 1 );
128 GDALRasterBandH outputGreenBand = GDALGetRasterBand( outputDataset.get(), 2 );
129 GDALRasterBandH outputBlueBand = GDALGetRasterBand( outputDataset.get(), 3 );
131 if ( !outputRedBand || !outputGreenBand || !outputBlueBand )
136 GDALSetRasterNoDataValue( outputRedBand, -9999 );
137 GDALSetRasterNoDataValue( outputGreenBand, -9999 );
138 GDALSetRasterNoDataValue( outputBlueBand, -9999 );
139 mOutputNodataValue = GDALGetRasterNoDataValue( outputRedBand,
nullptr );
140 mSlopeFilter->setOutputNodataValue( mOutputNodataValue );
141 mAspectFilter->setOutputNodataValue( mOutputNodataValue );
142 mHillshadeFilter285->setOutputNodataValue( mOutputNodataValue );
143 mHillshadeFilter300->setOutputNodataValue( mOutputNodataValue );
144 mHillshadeFilter315->setOutputNodataValue( mOutputNodataValue );
152 float *scanLine1 = (
float * ) CPLMalloc(
sizeof(
float ) * xSize );
153 float *scanLine2 = (
float * ) CPLMalloc(
sizeof(
float ) * xSize );
154 float *scanLine3 = (
float * ) CPLMalloc(
sizeof(
float ) * xSize );
156 unsigned char *resultRedLine = (
unsigned char * ) CPLMalloc(
sizeof(
unsigned char ) * xSize );
157 unsigned char *resultGreenLine = (
unsigned char * ) CPLMalloc(
sizeof(
unsigned char ) * xSize );
158 unsigned char *resultBlueLine = (
unsigned char * ) CPLMalloc(
sizeof(
unsigned char ) * xSize );
163 for (
int i = 0; i < ySize; ++i )
167 feedback->
setProgress( 100.0 * i /
static_cast<double>( ySize ) );
178 for (
int a = 0; a < xSize; ++a )
180 scanLine1[a] = mInputNodataValue;
182 if ( GDALRasterIO( rasterBand, GF_Read, 0, 0, xSize, 1, scanLine2, xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
190 CPLFree( scanLine1 );
191 scanLine1 = scanLine2;
192 scanLine2 = scanLine3;
193 scanLine3 = (
float * ) CPLMalloc(
sizeof(
float ) * xSize );
196 if ( i == ySize - 1 )
198 for (
int a = 0; a < xSize; ++a )
200 scanLine3[a] = mInputNodataValue;
205 if ( GDALRasterIO( rasterBand, GF_Read, 0, i + 1, xSize, 1, scanLine3, xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
211 for (
int j = 0; j < xSize; ++j )
215 resultOk = processNineCellWindow( &mInputNodataValue, &scanLine1[j], &scanLine1[j + 1], &mInputNodataValue, &scanLine2[j], &scanLine2[j + 1], &mInputNodataValue, &scanLine3[j], &scanLine3[j + 1], &resultRedLine[j], &resultGreenLine[j], &resultBlueLine[j] );
217 else if ( j == xSize - 1 )
219 resultOk = processNineCellWindow( &scanLine1[j - 1], &scanLine1[j], &mInputNodataValue, &scanLine2[j - 1], &scanLine2[j], &mInputNodataValue, &scanLine3[j - 1], &scanLine3[j], &mInputNodataValue, &resultRedLine[j], &resultGreenLine[j], &resultBlueLine[j] );
223 resultOk = processNineCellWindow( &scanLine1[j - 1], &scanLine1[j], &scanLine1[j + 1], &scanLine2[j - 1], &scanLine2[j], &scanLine2[j + 1], &scanLine3[j - 1], &scanLine3[j], &scanLine3[j + 1], &resultRedLine[j], &resultGreenLine[j], &resultBlueLine[j] );
228 resultRedLine[j] = mOutputNodataValue;
229 resultGreenLine[j] = mOutputNodataValue;
230 resultBlueLine[j] = mOutputNodataValue;
234 if ( GDALRasterIO( outputRedBand, GF_Write, 0, i, xSize, 1, resultRedLine, xSize, 1, GDT_Byte, 0, 0 ) != CE_None )
238 if ( GDALRasterIO( outputGreenBand, GF_Write, 0, i, xSize, 1, resultGreenLine, xSize, 1, GDT_Byte, 0, 0 ) != CE_None )
242 if ( GDALRasterIO( outputBlueBand, GF_Write, 0, i, xSize, 1, resultBlueLine, xSize, 1, GDT_Byte, 0, 0 ) != CE_None )
253 CPLFree( resultRedLine );
254 CPLFree( resultBlueLine );
255 CPLFree( resultGreenLine );
256 CPLFree( scanLine1 );
257 CPLFree( scanLine2 );
258 CPLFree( scanLine3 );
270bool 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 )
277 const float hillShadeValue300 = mHillshadeFilter300->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
278 if ( hillShadeValue300 != mOutputNodataValue )
280 if ( !getElevationColor( *x5, &r, &g, &b ) )
282 r = hillShadeValue300;
283 g = hillShadeValue300;
284 b = hillShadeValue300;
288 r = r / 2.0 + hillShadeValue300 / 2.0;
289 g = g / 2.0 + hillShadeValue300 / 2.0;
290 b = b / 2.0 + hillShadeValue300 / 2.0;
295 const float hillShadeValue315 = mHillshadeFilter315->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
296 const float slope = mSlopeFilter->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
297 if ( hillShadeValue315 != mOutputNodataValue && slope != mOutputNodataValue )
302 r2 = 0 / 2.0 + hillShadeValue315 / 2.0;
303 g2 = 0 / 2.0 + hillShadeValue315 / 2.0;
304 b2 = 0 / 2.0 + hillShadeValue315 / 2.0;
306 else if ( slope >= 1 )
308 const int slopeValue = 255 - ( slope / 15.0 * 255.0 );
309 r2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
310 g2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
311 b2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
315 r2 = hillShadeValue315;
316 g2 = hillShadeValue315;
317 b2 = hillShadeValue315;
321 r = r * 0.7 + r2 * 0.3;
322 g = g * 0.7 + g2 * 0.3;
323 b = b * 0.7 + b2 * 0.3;
327 const float hillShadeValue285 = mHillshadeFilter285->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
328 const float aspect = mAspectFilter->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
329 if ( hillShadeValue285 != mOutputNodataValue && aspect != mOutputNodataValue )
331 double angle_diff = std::fabs( 285 - aspect );
332 if ( angle_diff > 180 )
338 if ( angle_diff < 90 )
340 const int aspectVal = ( 1 - std::cos( angle_diff * M_PI / 180 ) ) * 255;
341 r3 = 0.5 * 255 + hillShadeValue315 * 0.5;
342 g3 = 0.5 * 255 + hillShadeValue315 * 0.5;
343 b3 = 0.5 * aspectVal + hillShadeValue315 * 0.5;
347 r3 = 0.5 * 255 + hillShadeValue315 * 0.5;
348 g3 = 0.5 * 255 + hillShadeValue315 * 0.5;
349 b3 = 0.5 * 255 + hillShadeValue315 * 0.5;
352 r = r3 * 0.1 + r * 0.9;
353 g = g3 * 0.1 + g * 0.9;
354 b = b3 * 0.1 + b * 0.9;
357 *red = (
unsigned char ) r;
358 *green = (
unsigned char ) g;
359 *blue = (
unsigned char ) b;
363bool QgsRelief::getElevationColor(
double elevation,
int *red,
int *green,
int *blue )
const
365 QList<ReliefColor>::const_iterator reliefColorIt = mReliefColors.constBegin();
366 for ( ; reliefColorIt != mReliefColors.constEnd(); ++reliefColorIt )
368 if ( elevation >= reliefColorIt->minElevation && elevation <= reliefColorIt->maxElevation )
370 const QColor &
c = reliefColorIt->color;
387 nCellsX = GDALGetRasterXSize( inputDataset.get() );
388 nCellsY = GDALGetRasterYSize( inputDataset.get() );
391 if ( GDALGetRasterCount( inputDataset.get() ) < 1 )
399GDALDriverH QgsRelief::openOutputDriver()
402 GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
424 const int xSize = GDALGetRasterXSize( inputDataset );
425 const int ySize = GDALGetRasterYSize( inputDataset );
428 char **papszOptions =
nullptr;
431 papszOptions = CSLSetNameValue( papszOptions,
"COMPRESS",
"PACKBITS" );
434 gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), xSize, ySize, 3, GDT_Byte, papszOptions ) );
435 CSLDestroy( papszOptions );
436 papszOptions =
nullptr;
438 if ( !outputDataset )
444 double geotransform[6];
445 if ( GDALGetGeoTransform( inputDataset, geotransform ) != CE_None )
449 GDALSetGeoTransform( outputDataset.get(), geotransform );
452 mCellSizeX = geotransform[1];
453 if ( mCellSizeX < 0 )
455 mCellSizeX = -mCellSizeX;
457 mCellSizeY = geotransform[5];
458 if ( mCellSizeY < 0 )
460 mCellSizeY = -mCellSizeY;
463 const char *projection = GDALGetProjectionRef( inputDataset );
464 GDALSetProjection( outputDataset.get(), projection );
466 return outputDataset;
472 int nCellsX, nCellsY;
480 GDALRasterBandH elevationBand = GDALGetRasterBand( inputDataset.get(), 1 );
481 if ( !elevationBand )
489 minMax[0] = GDALGetRasterMinimum( elevationBand, &minOk );
490 minMax[1] = GDALGetRasterMaximum( elevationBand, &maxOk );
492 if ( !minOk || !maxOk )
494 GDALComputeRasterMinMax( elevationBand,
true, minMax );
500 double frequency[252] = { 0 };
501 const double frequencyClassRange = ( minMax[1] - minMax[0] ) / 252.0;
503 float *scanLine = (
float * ) CPLMalloc(
sizeof(
float ) * nCellsX );
504 int elevationClass = -1;
506 for (
int i = 0; i < nCellsY; ++i )
508 if ( GDALRasterIO( elevationBand, GF_Read, 0, i, nCellsX, 1, scanLine, nCellsX, 1, GDT_Float32, 0, 0 ) != CE_None )
513 for (
int j = 0; j < nCellsX; ++j )
515 elevationClass = frequencyClassForElevation( scanLine[j], minMax[0], frequencyClassRange );
516 if ( elevationClass >= 0 && elevationClass < 252 )
518 frequency[elevationClass] += 1.0;
526 for (
int i = 0; i < 252; ++i )
528 frequency[i] = std::log10( frequency[i] );
532 QFile outFile( file );
533 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
538 QTextStream outstream( &outFile );
539#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
540 outstream.setCodec(
"UTF-8" );
542 for (
int i = 0; i < 252; ++i )
544 outstream << QString::number( i ) +
',' + QString::number( frequency[i] ) << Qt::endl;
552 QList<QgsRelief::ReliefColor> resultList;
554 int nCellsX, nCellsY;
562 GDALRasterBandH elevationBand = GDALGetRasterBand( inputDataset.get(), 1 );
563 if ( !elevationBand )
571 minMax[0] = GDALGetRasterMinimum( elevationBand, &minOk );
572 minMax[1] = GDALGetRasterMaximum( elevationBand, &maxOk );
574 if ( !minOk || !maxOk )
576 GDALComputeRasterMinMax( elevationBand,
true, minMax );
582 double frequency[252] = { 0 };
583 const double frequencyClassRange = ( minMax[1] - minMax[0] ) / 252.0;
585 float *scanLine = (
float * ) CPLMalloc(
sizeof(
float ) * nCellsX );
586 int elevationClass = -1;
588 for (
int i = 0; i < nCellsY; ++i )
590 if ( GDALRasterIO( elevationBand, GF_Read, 0, i, nCellsX, 1, scanLine, nCellsX, 1, GDT_Float32, 0, 0 ) != CE_None )
594 for (
int j = 0; j < nCellsX; ++j )
596 elevationClass = frequencyClassForElevation( scanLine[j], minMax[0], frequencyClassRange );
597 elevationClass = std::max( std::min( elevationClass, 251 ), 0 );
598 frequency[elevationClass] += 1.0;
605 for (
int i = 0; i < 252; ++i )
607 frequency[i] = std::log10( frequency[i] );
611 QList<int> classBreaks;
612 classBreaks.append( 0 );
613 classBreaks.append( 28 );
614 classBreaks.append( 56 );
615 classBreaks.append( 84 );
616 classBreaks.append( 112 );
617 classBreaks.append( 140 );
618 classBreaks.append( 168 );
619 classBreaks.append( 196 );
620 classBreaks.append( 224 );
621 classBreaks.append( 252 );
623 for (
int i = 0; i < 10; ++i )
625 optimiseClassBreaks( classBreaks, frequency );
629 for (
int i = 0; i < classBreaks.size(); ++i )
631 qWarning(
"%d", classBreaks[i] );
635 QVector<QColor> colorList;
636 colorList.reserve( 9 );
637 colorList.push_back( QColor( 7, 165, 144 ) );
638 colorList.push_back( QColor( 12, 221, 162 ) );
639 colorList.push_back( QColor( 33, 252, 183 ) );
640 colorList.push_back( QColor( 247, 252, 152 ) );
641 colorList.push_back( QColor( 252, 196, 8 ) );
642 colorList.push_back( QColor( 252, 166, 15 ) );
643 colorList.push_back( QColor( 175, 101, 15 ) );
644 colorList.push_back( QColor( 255, 133, 92 ) );
645 colorList.push_back( QColor( 204, 204, 204 ) );
647 resultList.reserve( classBreaks.size() );
648 for (
int i = 1; i < classBreaks.size(); ++i )
650 const double minElevation = minMax[0] + classBreaks[i - 1] * frequencyClassRange;
651 const double maxElevation = minMax[0] + classBreaks[i] * frequencyClassRange;
658void QgsRelief::optimiseClassBreaks( QList<int> &breaks,
double *frequencies )
660 const int nClasses = breaks.size() - 1;
661 double *a =
new double[nClasses];
662 double *b =
new double[nClasses];
664 for (
int i = 0; i < nClasses; ++i )
667 QList<QPair<int, double>> regressionInput;
668 regressionInput.reserve( breaks.at( i + 1 ) - breaks.at( i ) );
669 for (
int j = breaks.at( i ); j < breaks.at( i + 1 ); ++j )
671 regressionInput.push_back( qMakePair( j, frequencies[j] ) );
674 double aParam, bParam;
675 if ( !regressionInput.isEmpty() && calculateRegression( regressionInput, aParam, bParam ) )
687 const QList<int> classesToRemove;
690 for (
int i = 1; i < nClasses; ++i )
692 if ( breaks[i] == breaks[i - 1] )
703 int newX = ( b[i - 1] - b[i] ) / ( a[i] - a[i - 1] );
705 if ( newX <= breaks[i - 1] )
707 newX = breaks[i - 1];
710 else if ( i < nClasses - 1 && newX >= breaks[i + 1] )
712 newX = breaks[i + 1];
720 for (
int i = classesToRemove.size() - 1; i >= 0; --i )
722 breaks.removeAt( classesToRemove.at( i ) );
729int QgsRelief::frequencyClassForElevation(
double elevation,
double minElevation,
double elevationClassRange )
731 return ( elevation - minElevation ) / elevationClassRange;
734bool QgsRelief::calculateRegression(
const QList<QPair<int, double>> &input,
double &a,
double &b )
739 QList<QPair<int, double>>::const_iterator inputIt = input.constBegin();
740 for ( ; inputIt != input.constEnd(); ++inputIt )
742 xSum += inputIt->first;
743 ySum += inputIt->second;
745 xMean = xSum / input.size();
746 yMean = ySum / input.size();
748 double sumCounter = 0;
749 double sumDenominator = 0;
750 inputIt = input.constBegin();
751 for ( ; inputIt != input.constEnd(); ++inputIt )
753 sumCounter += ( ( inputIt->first - xMean ) * ( inputIt->second - yMean ) );
754 sumDenominator += ( ( inputIt->first - xMean ) * ( inputIt->first - xMean ) );
757 a = sumCounter / sumDenominator;
758 b = yMean - a * xMean;
Calculates aspect values in a window of 3x3 cells based on first order derivatives in x- and y- direc...
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 bool supportsRasterCreate(GDALDriverH driver)
Reads whether a driver supports GDALCreate() for raster purposes.
bool exportFrequencyDistributionToCsv(const QString &file)
Write frequency of elevation values to file for manual inspection.
QgsRelief(const QString &inputFile, const QString &outputFile, const QString &outputFormat)
QList< QgsRelief::ReliefColor > calculateOptimizedReliefClasses()
Calculates class breaks according with the method of Buenzli (2011) using an iterative algorithm for ...
int processRaster(QgsFeedback *feedback=nullptr)
Starts the calculation, reads from mInputFile and stores the result in mOutputFile.
void addReliefColorClass(const QgsRelief::ReliefColor &color)
Calculates slope values in a window of 3x3 cells based on first order derivatives in x- and y- direct...
void CORE_EXPORT fast_delete_and_close(dataset_unique_ptr &dataset, GDALDriverH driver, const QString &path)
Performs a fast close of an unwanted GDAL dataset handle by deleting the underlying data store.
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.
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).
#define QgsDebugError(str)