26 #include "cpl_string.h"
33 #include <QTextStream>
36 : mInputFile( inputFile )
37 , mOutputFile( outputFile )
38 , mOutputFormat( outputFormat )
39 , mSlopeFilter( std::make_unique<
QgsSlopeFilter >( inputFile, outputFile, outputFormat ) )
40 , mAspectFilter( std::make_unique<
QgsAspectFilter > ( inputFile, outputFile, outputFormat ) )
41 , mHillshadeFilter285( std::make_unique<
QgsHillshadeFilter >( inputFile, outputFile, outputFormat, 285, 30 ) )
42 , mHillshadeFilter300( std::make_unique<
QgsHillshadeFilter >( inputFile, outputFile, outputFormat, 300, 30 ) )
43 , mHillshadeFilter315( std::make_unique<
QgsHillshadeFilter >( inputFile, outputFile, outputFormat, 315, 30 ) )
53 mReliefColors.clear();
58 mReliefColors.push_back( color );
61 void QgsRelief::setDefaultReliefColors()
83 GDALDriverH outputDriver = openOutputDriver();
96 mHillshadeFilter285->setCellSizeX( mCellSizeX );
97 mHillshadeFilter285->setCellSizeY( mCellSizeY );
98 mHillshadeFilter285->setZFactor( mZFactor );
99 mHillshadeFilter300->setCellSizeX( mCellSizeX );
100 mHillshadeFilter300->setCellSizeY( mCellSizeY );
101 mHillshadeFilter300->setZFactor( mZFactor );
102 mHillshadeFilter315->setCellSizeX( mCellSizeX );
103 mHillshadeFilter315->setCellSizeY( mCellSizeY );
104 mHillshadeFilter315->setZFactor( mZFactor );
105 mSlopeFilter->setCellSizeX( mCellSizeX );
106 mSlopeFilter->setCellSizeY( mCellSizeY );
107 mSlopeFilter->setZFactor( mZFactor );
108 mAspectFilter->setCellSizeX( mCellSizeX );
109 mAspectFilter->setCellSizeY( mCellSizeY );
110 mAspectFilter->setZFactor( mZFactor );
113 GDALRasterBandH rasterBand = GDALGetRasterBand( inputDataset.get(), 1 );
118 mInputNodataValue = GDALGetRasterNoDataValue( rasterBand,
nullptr );
119 mSlopeFilter->setInputNodataValue( mInputNodataValue );
120 mAspectFilter->setInputNodataValue( mInputNodataValue );
121 mHillshadeFilter285->setInputNodataValue( mInputNodataValue );
122 mHillshadeFilter300->setInputNodataValue( mInputNodataValue );
123 mHillshadeFilter315->setInputNodataValue( mInputNodataValue );
125 GDALRasterBandH outputRedBand = GDALGetRasterBand( outputDataset.get(), 1 );
126 GDALRasterBandH outputGreenBand = GDALGetRasterBand( outputDataset.get(), 2 );
127 GDALRasterBandH outputBlueBand = GDALGetRasterBand( outputDataset.get(), 3 );
129 if ( !outputRedBand || !outputGreenBand || !outputBlueBand )
134 GDALSetRasterNoDataValue( outputRedBand, -9999 );
135 GDALSetRasterNoDataValue( outputGreenBand, -9999 );
136 GDALSetRasterNoDataValue( outputBlueBand, -9999 );
137 mOutputNodataValue = GDALGetRasterNoDataValue( outputRedBand,
nullptr );
138 mSlopeFilter->setOutputNodataValue( mOutputNodataValue );
139 mAspectFilter->setOutputNodataValue( mOutputNodataValue );
140 mHillshadeFilter285->setOutputNodataValue( mOutputNodataValue );
141 mHillshadeFilter300->setOutputNodataValue( mOutputNodataValue );
142 mHillshadeFilter315->setOutputNodataValue( mOutputNodataValue );
150 float *scanLine1 = (
float * ) CPLMalloc(
sizeof(
float ) * xSize );
151 float *scanLine2 = (
float * ) CPLMalloc(
sizeof(
float ) * xSize );
152 float *scanLine3 = (
float * ) CPLMalloc(
sizeof(
float ) * xSize );
154 unsigned char *resultRedLine = (
unsigned char * ) CPLMalloc(
sizeof(
unsigned char ) * xSize );
155 unsigned char *resultGreenLine = (
unsigned char * ) CPLMalloc(
sizeof(
unsigned char ) * xSize );
156 unsigned char *resultBlueLine = (
unsigned char * ) CPLMalloc(
sizeof(
unsigned char ) * xSize );
161 for (
int i = 0; i < ySize; ++i )
165 feedback->
setProgress( 100.0 * i /
static_cast< double >( ySize ) );
176 for (
int a = 0; a < xSize; ++a )
178 scanLine1[a] = mInputNodataValue;
180 if ( GDALRasterIO( rasterBand, GF_Read, 0, 0, xSize, 1, scanLine2, xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
182 QgsDebugMsg( QStringLiteral(
"Raster IO Error" ) );
188 CPLFree( scanLine1 );
189 scanLine1 = scanLine2;
190 scanLine2 = scanLine3;
191 scanLine3 = (
float * ) CPLMalloc(
sizeof(
float ) * xSize );
194 if ( i == ySize - 1 )
196 for (
int a = 0; a < xSize; ++a )
198 scanLine3[a] = mInputNodataValue;
203 if ( GDALRasterIO( rasterBand, GF_Read, 0, i + 1, xSize, 1, scanLine3, xSize, 1, GDT_Float32, 0, 0 ) != CE_None )
205 QgsDebugMsg( QStringLiteral(
"Raster IO Error" ) );
209 for (
int j = 0; j < xSize; ++j )
213 resultOk = processNineCellWindow( &mInputNodataValue, &scanLine1[j], &scanLine1[j + 1], &mInputNodataValue, &scanLine2[j], \
214 &scanLine2[j + 1], &mInputNodataValue, &scanLine3[j], &scanLine3[j + 1], \
215 &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], \
220 &mInputNodataValue, &scanLine3[j - 1], &scanLine3[j], &mInputNodataValue, \
221 &resultRedLine[j], &resultGreenLine[j], &resultBlueLine[j] );
225 resultOk = processNineCellWindow( &scanLine1[j - 1], &scanLine1[j], &scanLine1[j + 1], &scanLine2[j - 1], &scanLine2[j], \
226 &scanLine2[j + 1], &scanLine3[j - 1], &scanLine3[j], &scanLine3[j + 1], \
227 &resultRedLine[j], &resultGreenLine[j], &resultBlueLine[j] );
232 resultRedLine[j] = mOutputNodataValue;
233 resultGreenLine[j] = mOutputNodataValue;
234 resultBlueLine[j] = mOutputNodataValue;
238 if ( GDALRasterIO( outputRedBand, GF_Write, 0, i, xSize, 1, resultRedLine, xSize, 1, GDT_Byte, 0, 0 ) != CE_None )
240 QgsDebugMsg( QStringLiteral(
"Raster IO Error" ) );
242 if ( GDALRasterIO( outputGreenBand, GF_Write, 0, i, xSize, 1, resultGreenLine, xSize, 1, GDT_Byte, 0, 0 ) != CE_None )
244 QgsDebugMsg( QStringLiteral(
"Raster IO Error" ) );
246 if ( GDALRasterIO( outputBlueBand, GF_Write, 0, i, xSize, 1, resultBlueLine, xSize, 1, GDT_Byte, 0, 0 ) != CE_None )
248 QgsDebugMsg( QStringLiteral(
"Raster IO Error" ) );
257 CPLFree( resultRedLine );
258 CPLFree( resultBlueLine );
259 CPLFree( resultGreenLine );
260 CPLFree( scanLine1 );
261 CPLFree( scanLine2 );
262 CPLFree( scanLine3 );
274 bool QgsRelief::processNineCellWindow(
float *x1,
float *x2,
float *x3,
float *x4,
float *x5,
float *x6,
float *x7,
float *x8,
float *x9,
275 unsigned char *red,
unsigned char *green,
unsigned char *blue )
282 const float hillShadeValue300 = mHillshadeFilter300->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
283 if ( hillShadeValue300 != mOutputNodataValue )
285 if ( !setElevationColor( *x5, &r, &g, &b ) )
287 r = hillShadeValue300;
288 g = hillShadeValue300;
289 b = hillShadeValue300;
293 r = r / 2.0 + hillShadeValue300 / 2.0;
294 g = g / 2.0 + hillShadeValue300 / 2.0;
295 b = b / 2.0 + hillShadeValue300 / 2.0;
300 const float hillShadeValue315 = mHillshadeFilter315->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
301 const float slope = mSlopeFilter->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
302 if ( hillShadeValue315 != mOutputNodataValue && slope != mOutputNodataValue )
307 r2 = 0 / 2.0 + hillShadeValue315 / 2.0;
308 g2 = 0 / 2.0 + hillShadeValue315 / 2.0;
309 b2 = 0 / 2.0 + hillShadeValue315 / 2.0;
311 else if ( slope >= 1 )
313 const int slopeValue = 255 - ( slope / 15.0 * 255.0 );
314 r2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
315 g2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
316 b2 = slopeValue / 2.0 + hillShadeValue315 / 2.0;
320 r2 = hillShadeValue315;
321 g2 = hillShadeValue315;
322 b2 = hillShadeValue315;
326 r = r * 0.7 + r2 * 0.3;
327 g = g * 0.7 + g2 * 0.3;
328 b = b * 0.7 + b2 * 0.3;
332 const float hillShadeValue285 = mHillshadeFilter285->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
333 const float aspect = mAspectFilter->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
334 if ( hillShadeValue285 != mOutputNodataValue && aspect != mOutputNodataValue )
336 double angle_diff = std::fabs( 285 - aspect );
337 if ( angle_diff > 180 )
343 if ( angle_diff < 90 )
345 const int aspectVal = ( 1 - std::cos( angle_diff * M_PI / 180 ) ) * 255;
346 r3 = 0.5 * 255 + hillShadeValue315 * 0.5;
347 g3 = 0.5 * 255 + hillShadeValue315 * 0.5;
348 b3 = 0.5 * aspectVal + hillShadeValue315 * 0.5;
352 r3 = 0.5 * 255 + hillShadeValue315 * 0.5;
353 g3 = 0.5 * 255 + hillShadeValue315 * 0.5;
354 b3 = 0.5 * 255 + hillShadeValue315 * 0.5;
357 r = r3 * 0.1 + r * 0.9;
358 g = g3 * 0.1 + g * 0.9;
359 b = b3 * 0.1 + b * 0.9;
362 *red = (
unsigned char )r;
363 *green = (
unsigned char )g;
364 *blue = (
unsigned char )b;
368 bool QgsRelief::setElevationColor(
double elevation,
int *red,
int *green,
int *blue )
370 QList< ReliefColor >::const_iterator reliefColorIt = mReliefColors.constBegin();
371 for ( ; reliefColorIt != mReliefColors.constEnd(); ++reliefColorIt )
373 if ( elevation >= reliefColorIt->minElevation && elevation <= reliefColorIt->maxElevation )
375 const QColor &
c = reliefColorIt->color;
392 nCellsX = GDALGetRasterXSize( inputDataset.get() );
393 nCellsY = GDALGetRasterYSize( inputDataset.get() );
396 if ( GDALGetRasterCount( inputDataset.get() ) < 1 )
404 GDALDriverH QgsRelief::openOutputDriver()
407 GDALDriverH outputDriver = GDALGetDriverByName( mOutputFormat.toLocal8Bit().data() );
429 const int xSize = GDALGetRasterXSize( inputDataset );
430 const int ySize = GDALGetRasterYSize( inputDataset );
433 char **papszOptions =
nullptr;
436 papszOptions = CSLSetNameValue( papszOptions,
"COMPRESS",
"PACKBITS" );
439 gdal::dataset_unique_ptr outputDataset( GDALCreate( outputDriver, mOutputFile.toUtf8().constData(), xSize, ySize, 3, GDT_Byte, papszOptions ) );
440 CSLDestroy( papszOptions );
441 papszOptions =
nullptr;
443 if ( !outputDataset )
449 double geotransform[6];
450 if ( GDALGetGeoTransform( inputDataset, geotransform ) != CE_None )
454 GDALSetGeoTransform( outputDataset.get(), geotransform );
457 mCellSizeX = geotransform[1];
458 if ( mCellSizeX < 0 )
460 mCellSizeX = -mCellSizeX;
462 mCellSizeY = geotransform[5];
463 if ( mCellSizeY < 0 )
465 mCellSizeY = -mCellSizeY;
468 const char *projection = GDALGetProjectionRef( inputDataset );
469 GDALSetProjection( outputDataset.get(), projection );
471 return outputDataset;
477 int nCellsX, nCellsY;
485 GDALRasterBandH elevationBand = GDALGetRasterBand( inputDataset.get(), 1 );
486 if ( !elevationBand )
494 minMax[0] = GDALGetRasterMinimum( elevationBand, &minOk );
495 minMax[1] = GDALGetRasterMaximum( elevationBand, &maxOk );
497 if ( !minOk || !maxOk )
499 GDALComputeRasterMinMax( elevationBand,
true, minMax );
505 double frequency[252] = {0};
506 const double frequencyClassRange = ( minMax[1] - minMax[0] ) / 252.0;
508 float *scanLine = (
float * ) CPLMalloc(
sizeof(
float ) * nCellsX );
509 int elevationClass = -1;
511 for (
int i = 0; i < nCellsY; ++i )
513 if ( GDALRasterIO( elevationBand, GF_Read, 0, i, nCellsX, 1,
514 scanLine, nCellsX, 1, GDT_Float32,
517 QgsDebugMsg( QStringLiteral(
"Raster IO Error" ) );
520 for (
int j = 0; j < nCellsX; ++j )
522 elevationClass = frequencyClassForElevation( scanLine[j], minMax[0], frequencyClassRange );
523 if ( elevationClass >= 0 && elevationClass < 252 )
525 frequency[elevationClass] += 1.0;
533 for (
int i = 0; i < 252; ++i )
535 frequency[i] = std::log10( frequency[i] );
539 QFile outFile( file );
540 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
545 QTextStream outstream( &outFile );
546 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
547 outstream.setCodec(
"UTF-8" );
549 for (
int i = 0; i < 252; ++i )
551 #if QT_VERSION < QT_VERSION_CHECK(5, 14, 0)
552 outstream << QString::number( i ) +
',' + QString::number( frequency[i] ) << endl;
554 outstream << QString::number( i ) +
',' + QString::number( frequency[i] ) << Qt::endl;
563 QList< QgsRelief::ReliefColor > resultList;
565 int nCellsX, nCellsY;
573 GDALRasterBandH elevationBand = GDALGetRasterBand( inputDataset.get(), 1 );
574 if ( !elevationBand )
582 minMax[0] = GDALGetRasterMinimum( elevationBand, &minOk );
583 minMax[1] = GDALGetRasterMaximum( elevationBand, &maxOk );
585 if ( !minOk || !maxOk )
587 GDALComputeRasterMinMax( elevationBand,
true, minMax );
593 double frequency[252] = {0};
594 const double frequencyClassRange = ( minMax[1] - minMax[0] ) / 252.0;
596 float *scanLine = (
float * ) CPLMalloc(
sizeof(
float ) * nCellsX );
597 int elevationClass = -1;
599 for (
int i = 0; i < nCellsY; ++i )
601 if ( GDALRasterIO( elevationBand, GF_Read, 0, i, nCellsX, 1,
602 scanLine, nCellsX, 1, GDT_Float32,
605 QgsDebugMsg( QStringLiteral(
"Raster IO Error" ) );
607 for (
int j = 0; j < nCellsX; ++j )
609 elevationClass = frequencyClassForElevation( scanLine[j], minMax[0], frequencyClassRange );
610 elevationClass = std::max( std::min( elevationClass, 251 ), 0 );
611 frequency[elevationClass] += 1.0;
618 for (
int i = 0; i < 252; ++i )
620 frequency[i] = std::log10( frequency[i] );
624 QList<int> classBreaks;
625 classBreaks.append( 0 );
626 classBreaks.append( 28 );
627 classBreaks.append( 56 );
628 classBreaks.append( 84 );
629 classBreaks.append( 112 );
630 classBreaks.append( 140 );
631 classBreaks.append( 168 );
632 classBreaks.append( 196 );
633 classBreaks.append( 224 );
634 classBreaks.append( 252 );
636 for (
int i = 0; i < 10; ++i )
638 optimiseClassBreaks( classBreaks, frequency );
642 for (
int i = 0; i < classBreaks.size(); ++i )
644 qWarning(
"%d", classBreaks[i] );
648 QVector<QColor> colorList;
649 colorList.reserve( 9 );
650 colorList.push_back( QColor( 7, 165, 144 ) );
651 colorList.push_back( QColor( 12, 221, 162 ) );
652 colorList.push_back( QColor( 33, 252, 183 ) );
653 colorList.push_back( QColor( 247, 252, 152 ) );
654 colorList.push_back( QColor( 252, 196, 8 ) );
655 colorList.push_back( QColor( 252, 166, 15 ) );
656 colorList.push_back( QColor( 175, 101, 15 ) );
657 colorList.push_back( QColor( 255, 133, 92 ) );
658 colorList.push_back( QColor( 204, 204, 204 ) );
660 resultList.reserve( classBreaks.size() );
661 for (
int i = 1; i < classBreaks.size(); ++i )
663 const double minElevation = minMax[0] + classBreaks[i - 1] * frequencyClassRange;
664 const double maxElevation = minMax[0] + classBreaks[i] * frequencyClassRange;
671 void QgsRelief::optimiseClassBreaks( QList<int> &breaks,
double *frequencies )
673 const int nClasses = breaks.size() - 1;
674 double *a =
new double[nClasses];
675 double *b =
new double[nClasses];
677 for (
int i = 0; i < nClasses; ++i )
680 QList< QPair < int, double > > regressionInput;
681 regressionInput.reserve( breaks.at( i + 1 ) - breaks.at( i ) );
682 for (
int j = breaks.at( i ); j < breaks.at( i + 1 ); ++j )
684 regressionInput.push_back( qMakePair( j, frequencies[j] ) );
687 double aParam, bParam;
688 if ( !regressionInput.isEmpty() && calculateRegression( regressionInput, aParam, bParam ) )
700 const QList<int> classesToRemove;
703 for (
int i = 1; i < nClasses ; ++i )
705 if ( breaks[i] == breaks[ i - 1 ] )
716 int newX = ( b[i - 1] - b[ i ] ) / ( a[ i ] - a[ i - 1 ] );
718 if ( newX <= breaks[i - 1] )
720 newX = breaks[i - 1];
723 else if ( i < nClasses - 1 && newX >= breaks[i + 1] )
725 newX = breaks[i + 1];
733 for (
int i = classesToRemove.size() - 1; i >= 0; --i )
735 breaks.removeAt( classesToRemove.at( i ) );
742 int QgsRelief::frequencyClassForElevation(
double elevation,
double minElevation,
double elevationClassRange )
744 return ( elevation - minElevation ) / elevationClassRange;
747 bool QgsRelief::calculateRegression(
const QList< QPair < int, double > > &input,
double &a,
double &b )
752 QList< QPair < int, double > >::const_iterator inputIt = input.constBegin();
753 for ( ; inputIt != input.constEnd(); ++inputIt )
755 xSum += inputIt->first;
756 ySum += inputIt->second;
758 xMean = xSum / input.size();
759 yMean = ySum / input.size();
761 double sumCounter = 0;
762 double sumDenominator = 0;
763 inputIt = input.constBegin();
764 for ( ; inputIt != input.constEnd(); ++inputIt )
766 sumCounter += ( ( inputIt->first - xMean ) * ( inputIt->second - yMean ) );
767 sumDenominator += ( ( inputIt->first - xMean ) * ( inputIt->first - xMean ) );
770 a = sumCounter / sumDenominator;
771 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 SIP_HOLDGIL
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)