26 #include "cpl_string.h"    33 #include <QTextStream>    36   : mInputFile( inputFile )
    37   , mOutputFile( outputFile )
    38   , mOutputFormat( outputFormat )
    39   , mSlopeFilter( qgis::make_unique< 
QgsSlopeFilter >( inputFile, outputFile, outputFormat ) )
    40   , mAspectFilter( qgis::make_unique< 
QgsAspectFilter > ( inputFile, outputFile, outputFormat ) )
    41   , mHillshadeFilter285( qgis::make_unique< 
QgsHillshadeFilter >( inputFile, outputFile, outputFormat, 285, 30 ) )
    42   , mHillshadeFilter300( qgis::make_unique< 
QgsHillshadeFilter >( inputFile, outputFile, outputFormat, 300, 30 ) )
    43   , mHillshadeFilter315( qgis::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   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   float hillShadeValue315 = mHillshadeFilter315->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
   301   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       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   float hillShadeValue285 = mHillshadeFilter285->processNineCellWindow( x1, x2, x3, x4, x5, x6, x7, x8, x9 );
   333   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       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   int xSize = GDALGetRasterXSize( inputDataset );
   430   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   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   for ( 
int i = 0; i < 252; ++i )
   548     outstream << QString::number( i ) + 
',' + QString::number( frequency[i] ) << endl;
   556   QList< QgsRelief::ReliefColor > resultList;
   558   int nCellsX, nCellsY;
   566   GDALRasterBandH elevationBand = GDALGetRasterBand( inputDataset.get(), 1 );
   567   if ( !elevationBand )
   575   minMax[0] = GDALGetRasterMinimum( elevationBand, &minOk );
   576   minMax[1] = GDALGetRasterMaximum( elevationBand, &maxOk );
   578   if ( !minOk || !maxOk )
   580     GDALComputeRasterMinMax( elevationBand, 
true, minMax );
   586   double frequency[252] = {0};
   587   double frequencyClassRange = ( minMax[1] - minMax[0] ) / 252.0;
   589   float *scanLine = ( 
float * ) CPLMalloc( 
sizeof( 
float ) *  nCellsX );
   590   int elevationClass = -1;
   592   for ( 
int i = 0; i < nCellsY; ++i )
   594     if ( GDALRasterIO( elevationBand, GF_Read, 0, i, nCellsX, 1,
   595                        scanLine, nCellsX, 1, GDT_Float32,
   598       QgsDebugMsg( QStringLiteral( 
"Raster IO Error" ) );
   600     for ( 
int j = 0; j < nCellsX; ++j )
   602       elevationClass = frequencyClassForElevation( scanLine[j], minMax[0], frequencyClassRange );
   603       elevationClass = std::max( std::min( elevationClass, 251 ), 0 );
   604       frequency[elevationClass] += 1.0;
   611   for ( 
int i = 0; i < 252; ++i )
   613     frequency[i] = std::log10( frequency[i] );
   617   QList<int> classBreaks;
   618   classBreaks.append( 0 );
   619   classBreaks.append( 28 );
   620   classBreaks.append( 56 );
   621   classBreaks.append( 84 );
   622   classBreaks.append( 112 );
   623   classBreaks.append( 140 );
   624   classBreaks.append( 168 );
   625   classBreaks.append( 196 );
   626   classBreaks.append( 224 );
   627   classBreaks.append( 252 );
   629   for ( 
int i = 0; i < 10; ++i )
   631     optimiseClassBreaks( classBreaks, frequency );
   635   for ( 
int i = 0; i < classBreaks.size(); ++i )
   637     qWarning( 
"%d", classBreaks[i] );
   641   QVector<QColor> colorList;
   642   colorList.reserve( 9 );
   643   colorList.push_back( QColor( 7, 165, 144 ) );
   644   colorList.push_back( QColor( 12, 221, 162 ) );
   645   colorList.push_back( QColor( 33, 252, 183 ) );
   646   colorList.push_back( QColor( 247, 252, 152 ) );
   647   colorList.push_back( QColor( 252, 196, 8 ) );
   648   colorList.push_back( QColor( 252, 166, 15 ) );
   649   colorList.push_back( QColor( 175, 101, 15 ) );
   650   colorList.push_back( QColor( 255, 133, 92 ) );
   651   colorList.push_back( QColor( 204, 204, 204 ) );
   653   resultList.reserve( classBreaks.size() );
   654   for ( 
int i = 1; i < classBreaks.size(); ++i )
   656     double minElevation = minMax[0] + classBreaks[i - 1] * frequencyClassRange;
   657     double maxElevation = minMax[0] + classBreaks[i] * frequencyClassRange;
   664 void QgsRelief::optimiseClassBreaks( QList<int> &breaks, 
double *frequencies )
   666   int nClasses = breaks.size() - 1;
   667   double *a = 
new double[nClasses]; 
   668   double *b = 
new double[nClasses]; 
   670   for ( 
int i = 0; i < nClasses; ++i )
   673     QList< QPair < int, double > > regressionInput;
   674     regressionInput.reserve( breaks.at( i + 1 ) - breaks.at( i ) );
   675     for ( 
int j = breaks.at( i ); j < breaks.at( i + 1 ); ++j )
   677       regressionInput.push_back( qMakePair( j, frequencies[j] ) );
   680     double aParam, bParam;
   681     if ( !regressionInput.isEmpty() && calculateRegression( regressionInput, aParam, bParam ) )
   693   QList<int> classesToRemove;
   696   for ( 
int i = 1; i < nClasses ; ++i )
   698     if ( breaks[i] == breaks[ i - 1 ] )
   709       int newX = ( b[i - 1] - b[ i ] ) / ( a[ i ] - a[ i - 1 ] );
   711       if ( newX <= breaks[i - 1] )
   713         newX = breaks[i - 1];
   716       else if ( i < nClasses - 1 && newX >= breaks[i + 1] )
   718         newX = breaks[i + 1];
   726   for ( 
int i = classesToRemove.size() - 1; i >= 0; --i )
   728     breaks.removeAt( classesToRemove.at( i ) );
   735 int QgsRelief::frequencyClassForElevation( 
double elevation, 
double minElevation, 
double elevationClassRange )
   737   return ( elevation - minElevation ) / elevationClassRange;
   740 bool QgsRelief::calculateRegression( 
const QList< QPair < int, double > > &input, 
double &a, 
double &b )
   745   QList< QPair < int, double > >::const_iterator inputIt = input.constBegin();
   746   for ( ; inputIt != input.constEnd(); ++inputIt )
   748     xSum += inputIt->first;
   749     ySum += inputIt->second;
   751   xMean = xSum / input.size();
   752   yMean = ySum / input.size();
   754   double sumCounter = 0;
   755   double sumDenominator = 0;
   756   inputIt = input.constBegin();
   757   for ( ; inputIt != input.constEnd(); ++inputIt )
   759     sumCounter += ( ( inputIt->first - xMean ) * ( inputIt->second - yMean ) );
   760     sumDenominator += ( ( inputIt->first - xMean ) * ( inputIt->first - xMean ) );
   763   a = sumCounter / sumDenominator;
   764   b = yMean - a * xMean;
 
QList< QgsRelief::ReliefColor > calculateOptimizedReliefClasses()
Calculates class breaks according with the method of Buenzli (2011) using an iterative algorithm for ...
 
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference) 
 
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. 
 
Base class for feedback objects to be used for cancelation of something running in a worker thread...
 
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 exportFrequencyDistributionToCsv(const QString &file)
Write frequency of elevation values to file for manual inspection. 
 
int processRaster(QgsFeedback *feedback=nullptr)
Starts the calculation, reads from mInputFile and stores the result in mOutputFile. 
 
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...
 
Calculates aspect values in a window of 3x3 cells based on first order derivatives in x- and y- direc...
 
bool isCanceled() const
Tells whether the operation has been canceled already. 
 
Calculates slope values in a window of 3x3 cells based on first order derivatives in x- and y- direct...
 
QgsRelief(const QString &inputFile, const QString &outputFile, const QString &outputFormat)
 
void addReliefColorClass(const QgsRelief::ReliefColor &color)
 
std::unique_ptr< std::remove_pointer< GDALDatasetH >::type, GDALDatasetCloser > dataset_unique_ptr
Scoped GDAL dataset.