Skip to content

Commit

Permalink
Don't crash when using raster calculator with huge raster (refs #13336)
Browse files Browse the repository at this point in the history
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 a665b7d
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 10 deletions.
11 changes: 11 additions & 0 deletions python/analysis/raster/qgsrastercalculator.sip
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 13 additions & 5 deletions src/analysis/raster/qgsrastercalculator.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ int QgsRasterCalculator::processCalculation( QProgressDialog* p )
if ( !calcNode )
{
//error
return 4;
return static_cast<int>( ParserError );
}

QMap< QString, QgsRasterBlock* > inputBlocks;
Expand All @@ -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;
Expand All @@ -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 );
Expand Down Expand Up @@ -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()
Expand Down
12 changes: 12 additions & 0 deletions src/analysis/raster/qgsrastercalculator.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
44 changes: 39 additions & 5 deletions src/app/qgisapp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

}
}
}
Expand Down

0 comments on commit a665b7d

Please sign in to comment.