Skip to content
Permalink
Browse files

Don't crash when using raster calculator with huge raster (refs #13336)

Now we abort and advise the user if insufficient memory is available
to perform the calculation.

At some future stage it would be nice to perform the calculations
in blocks to allow this scenario, but for now it's better not to
crash.
  • Loading branch information
nyalldawson committed Jan 26, 2016
1 parent 5600078 commit a665b7d72e7042aadd4a5cec10e9e9988a46b70f
@@ -18,6 +18,17 @@ class QgsRasterCalculator

public:

//! Result of the calculation
enum Result
{
Success, /*!< Calculation sucessful */
CreateOutputError, /*!< Error creating output data file */
InputLayerError, /*!< Error reading input layer */
Cancelled, /*!< User cancelled calculation */
ParserError, /*!< Error parsing formula */
MemoryError, /*!< Error allocating memory for result */
};

/** QgsRasterCalculator constructor.
* @param formulaString formula for raster calculation
* @param outputFile output file path
@@ -69,7 +69,7 @@ int QgsRasterCalculator::processCalculation( QProgressDialog* p )
if ( !calcNode )
{
//error
return 4;
return static_cast<int>( ParserError );
}

QMap< QString, QgsRasterBlock* > inputBlocks;
@@ -78,7 +78,9 @@ int QgsRasterCalculator::processCalculation( QProgressDialog* p )
{
if ( !it->raster ) // no raster layer in entry
{
return 2;
delete calcNode;
qDeleteAll( inputBlocks );
return static_cast< int >( InputLayerError );
}

QgsRasterBlock* block = nullptr;
@@ -96,14 +98,20 @@ int QgsRasterCalculator::processCalculation( QProgressDialog* p )
{
block = it->raster->dataProvider()->block( it->bandNumber, mOutputRectangle, mNumOutputColumns, mNumOutputRows );
}
if ( block->isEmpty() )
{
delete calcNode;
qDeleteAll( inputBlocks );
return static_cast<int>( MemoryError );
}
inputBlocks.insert( it->ref, block );
}

//open output dataset for writing
GDALDriverH outputDriver = openOutputDriver();
if ( !outputDriver )
{
return 1;
return static_cast< int >( CreateOutputError );
}

GDALDatasetH outputDataset = openOutputFile( outputDriver );
@@ -169,11 +177,11 @@ int QgsRasterCalculator::processCalculation( QProgressDialog* p )
{
//delete the dataset without closing (because it is faster)
GDALDeleteDataset( outputDriver, TO8F( mOutputFile ) );
return 3;
return static_cast< int >( Cancelled );
}
GDALClose( outputDataset );

return 0;
return static_cast< int >( Success );
}

QgsRasterCalculator::QgsRasterCalculator()
@@ -41,6 +41,17 @@ class ANALYSIS_EXPORT QgsRasterCalculator
{
public:

//! Result of the calculation
enum Result
{
Success = 0, /*!< Calculation sucessful */
CreateOutputError = 1, /*!< Error creating output data file */
InputLayerError = 2, /*!< Error reading input layer */
Cancelled = 3, /*!< User cancelled calculation */
ParserError = 4, /*!< Error parsing formula */
MemoryError = 5, /*!< Error allocating memory for result */
};

/** QgsRasterCalculator constructor.
* @param formulaString formula for raster calculation
* @param outputFile output file path
@@ -70,6 +81,7 @@ class ANALYSIS_EXPORT QgsRasterCalculator
/** Starts the calculation and writes new raster
@param p progress bar (or 0 if called from non-gui code)
@return 0 in case of success*/
//TODO QGIS 3.0 - return QgsRasterCalculator::Result
int processCalculation( QProgressDialog* p = nullptr );

private:
@@ -4305,12 +4305,46 @@ void QgisApp::showRasterCalculator()

QProgressDialog p( tr( "Calculating..." ), tr( "Abort..." ), 0, 0 );
p.setWindowModality( Qt::WindowModal );
if ( rc.processCalculation( &p ) == 0 )
QgsRasterCalculator::Result res = static_cast< QgsRasterCalculator::Result >( rc.processCalculation( &p ) );
switch ( res )
{
if ( d.addLayerToProject() )
{
addRasterLayer( d.outputFile(), QFileInfo( d.outputFile() ).baseName() );
}
case QgsRasterCalculator::Success:
if ( d.addLayerToProject() )
{
addRasterLayer( d.outputFile(), QFileInfo( d.outputFile() ).baseName() );
}
messageBar()->pushMessage( tr( "Raster calculator" ),
tr( "Calculation complete." ),
QgsMessageBar::INFO, messageTimeout() );
break;

case QgsRasterCalculator::CreateOutputError:
messageBar()->pushMessage( tr( "Raster calculator" ),
tr( "Could not create destination file." ),
QgsMessageBar::CRITICAL );
break;

case QgsRasterCalculator::InputLayerError:
messageBar()->pushMessage( tr( "Raster calculator" ),
tr( "Could not read input layer." ),
QgsMessageBar::CRITICAL );
break;

case QgsRasterCalculator::Cancelled:
break;

case QgsRasterCalculator::ParserError:
messageBar()->pushMessage( tr( "Raster calculator" ),
tr( "Could not parse raster formula." ),
QgsMessageBar::CRITICAL );
break;

case QgsRasterCalculator::MemoryError:
messageBar()->pushMessage( tr( "Raster calculator" ),
tr( "Insufficient memory available for operation." ),
QgsMessageBar::CRITICAL );
break;

}
}
}

0 comments on commit a665b7d

Please sign in to comment.
You can’t perform that action at this time.