mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
Add more descriptive error messages for raster calculation failure
With unit tests
This commit is contained in:
parent
f4bbb147b2
commit
fd1d6c1037
@ -18,9 +18,6 @@ Represents an individual raster layer/band number entry within a raster calculat
|
||||
|
||||
%TypeHeaderCode
|
||||
#include "qgsrastercalculator.h"
|
||||
%TypeHeaderCode
|
||||
#include <qgsrastercalculator.h>
|
||||
%End
|
||||
%End
|
||||
public:
|
||||
|
||||
@ -50,6 +47,7 @@ Performs raster layer calculations.
|
||||
Canceled,
|
||||
ParserError,
|
||||
MemoryError,
|
||||
BandError,
|
||||
};
|
||||
|
||||
QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat,
|
||||
@ -83,7 +81,22 @@ QgsRasterCalculator constructor.
|
||||
.. versionadded:: 2.10
|
||||
%End
|
||||
|
||||
int processCalculation( QgsFeedback *feedback = 0 );
|
||||
Result processCalculation( QgsFeedback *feedback = 0 );
|
||||
%Docstring
|
||||
Starts the calculation and writes a new raster.
|
||||
|
||||
The optional ``feedback`` argument can be used for progress reporting and cancelation support.
|
||||
|
||||
:return: QgsRasterCalculator.Success in case of success. If an error is encountered then
|
||||
a description of the error can be obtained by calling lastError().
|
||||
%End
|
||||
|
||||
QString lastError() const;
|
||||
%Docstring
|
||||
Returns a description of the last error encountered.
|
||||
|
||||
.. versionadded:: 3.4
|
||||
%End
|
||||
|
||||
};
|
||||
|
||||
|
@ -58,15 +58,16 @@ QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QS
|
||||
{
|
||||
}
|
||||
|
||||
int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
|
||||
QgsRasterCalculator::Result QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
|
||||
{
|
||||
mLastError.clear();
|
||||
|
||||
//prepare search string / tree
|
||||
QString errorString;
|
||||
std::unique_ptr< QgsRasterCalcNode > calcNode( QgsRasterCalcNode::parseRasterCalcString( mFormulaString, errorString ) );
|
||||
std::unique_ptr< QgsRasterCalcNode > calcNode( QgsRasterCalcNode::parseRasterCalcString( mFormulaString, mLastError ) );
|
||||
if ( !calcNode )
|
||||
{
|
||||
//error
|
||||
return static_cast<int>( ParserError );
|
||||
return ParserError;
|
||||
}
|
||||
|
||||
QMap< QString, QgsRasterBlock * > inputBlocks;
|
||||
@ -75,8 +76,16 @@ int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
|
||||
{
|
||||
if ( !it->raster ) // no raster layer in entry
|
||||
{
|
||||
mLastError = QObject::tr( "No raster layer for entry %1" ).arg( it->ref );
|
||||
qDeleteAll( inputBlocks );
|
||||
return static_cast< int >( InputLayerError );
|
||||
return InputLayerError;
|
||||
}
|
||||
|
||||
if ( it->bandNumber <= 0 || it->bandNumber > it->raster->bandCount() )
|
||||
{
|
||||
mLastError = QObject::tr( "Band number %1 is not valid for entry %2" ).arg( it->bandNumber ).arg( it->ref );
|
||||
qDeleteAll( inputBlocks );
|
||||
return BandError;
|
||||
}
|
||||
|
||||
std::unique_ptr< QgsRasterBlock > block;
|
||||
@ -94,7 +103,7 @@ int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
|
||||
if ( rasterBlockFeedback->isCanceled() )
|
||||
{
|
||||
qDeleteAll( inputBlocks );
|
||||
return static_cast< int >( Canceled );
|
||||
return Canceled;
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -103,8 +112,9 @@ int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
|
||||
}
|
||||
if ( block->isEmpty() )
|
||||
{
|
||||
mLastError = QObject::tr( "Could not allocate required memory for %1" ).arg( it->ref );
|
||||
qDeleteAll( inputBlocks );
|
||||
return static_cast<int>( MemoryError );
|
||||
return MemoryError;
|
||||
}
|
||||
inputBlocks.insert( it->ref, block.release() );
|
||||
}
|
||||
@ -113,13 +123,15 @@ int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
|
||||
GDALDriverH outputDriver = openOutputDriver();
|
||||
if ( !outputDriver )
|
||||
{
|
||||
return static_cast< int >( CreateOutputError );
|
||||
mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
|
||||
return CreateOutputError;
|
||||
}
|
||||
|
||||
gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
|
||||
if ( !outputDataset )
|
||||
{
|
||||
return static_cast< int >( CreateOutputError );
|
||||
mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
|
||||
return CreateOutputError;
|
||||
}
|
||||
|
||||
GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt().toLocal8Bit().data() );
|
||||
@ -179,9 +191,9 @@ int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
|
||||
{
|
||||
//delete the dataset without closing (because it is faster)
|
||||
gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
|
||||
return static_cast< int >( Canceled );
|
||||
return Canceled;
|
||||
}
|
||||
return static_cast< int >( Success );
|
||||
return Success;
|
||||
}
|
||||
|
||||
GDALDriverH QgsRasterCalculator::openOutputDriver()
|
||||
@ -229,3 +241,8 @@ void QgsRasterCalculator::outputGeoTransform( double *transform ) const
|
||||
transform[4] = 0;
|
||||
transform[5] = -mOutputRectangle.height() / mNumOutputRows;
|
||||
}
|
||||
|
||||
QString QgsRasterCalculator::lastError() const
|
||||
{
|
||||
return mLastError;
|
||||
}
|
||||
|
@ -36,11 +36,6 @@ class QgsFeedback;
|
||||
*/
|
||||
class ANALYSIS_EXPORT QgsRasterCalculatorEntry
|
||||
{
|
||||
#ifdef SIP_RUN
|
||||
% TypeHeaderCode
|
||||
#include <qgsrastercalculator.h>
|
||||
% End
|
||||
#endif
|
||||
|
||||
public:
|
||||
|
||||
@ -77,6 +72,7 @@ class ANALYSIS_EXPORT QgsRasterCalculator
|
||||
Canceled = 3, //!< User canceled calculation
|
||||
ParserError = 4, //!< Error parsing formula
|
||||
MemoryError = 5, //!< Error allocating memory for result
|
||||
BandError = 6, //!< Invalid band number for input
|
||||
};
|
||||
|
||||
/**
|
||||
@ -111,10 +107,17 @@ class ANALYSIS_EXPORT QgsRasterCalculator
|
||||
* Starts the calculation and writes a new raster.
|
||||
*
|
||||
* The optional \a feedback argument can be used for progress reporting and cancelation support.
|
||||
* \returns 0 in case of success
|
||||
*
|
||||
* \returns QgsRasterCalculator::Success in case of success. If an error is encountered then
|
||||
* a description of the error can be obtained by calling lastError().
|
||||
*/
|
||||
//TODO QGIS 3.0 - return QgsRasterCalculator::Result
|
||||
int processCalculation( QgsFeedback *feedback = nullptr );
|
||||
Result processCalculation( QgsFeedback *feedback = nullptr );
|
||||
|
||||
/**
|
||||
* Returns a description of the last error encountered.
|
||||
* \since QGIS 3.4
|
||||
*/
|
||||
QString lastError() const;
|
||||
|
||||
private:
|
||||
//default constructor forbidden. We need formula, output file, output format and output raster resolution obligatory
|
||||
@ -148,6 +151,8 @@ class ANALYSIS_EXPORT QgsRasterCalculator
|
||||
//! Number of output rows
|
||||
int mNumOutputRows = 0;
|
||||
|
||||
QString mLastError;
|
||||
|
||||
/***/
|
||||
QVector<QgsRasterCalculatorEntry> mRasterEntries;
|
||||
};
|
||||
|
@ -5593,6 +5593,11 @@ void QgisApp::showRasterCalculator()
|
||||
Qgis::Critical );
|
||||
break;
|
||||
|
||||
case QgsRasterCalculator::BandError:
|
||||
messageBar()->pushMessage( tr( "Raster calculator" ),
|
||||
tr( "Invalid band number for input layer." ),
|
||||
Qgis::Critical );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,6 +54,8 @@ class TestQgsRasterCalculator : public QObject
|
||||
void calcWithLayers();
|
||||
void calcWithReprojectedLayers();
|
||||
|
||||
void errors();
|
||||
|
||||
private:
|
||||
|
||||
QgsRasterLayer *mpLandsatRasterLayer = nullptr;
|
||||
@ -445,7 +447,7 @@ void TestQgsRasterCalculator::calcWithLayers()
|
||||
tmpName,
|
||||
QStringLiteral( "GTiff" ),
|
||||
extent, crs, 2, 3, entries );
|
||||
QCOMPARE( rc.processCalculation(), 0 );
|
||||
QCOMPARE( static_cast< int >( rc.processCalculation() ), 0 );
|
||||
|
||||
//open output file and check results
|
||||
QgsRasterLayer *result = new QgsRasterLayer( tmpName, QStringLiteral( "result" ) );
|
||||
@ -466,7 +468,7 @@ void TestQgsRasterCalculator::calcWithLayers()
|
||||
tmpName,
|
||||
QStringLiteral( "GTiff" ),
|
||||
extent, crs, 2, 3, entries );
|
||||
QCOMPARE( rc2.processCalculation(), 0 );
|
||||
QCOMPARE( static_cast< int >( rc2.processCalculation() ), 0 );
|
||||
|
||||
//open output file and check results
|
||||
result = new QgsRasterLayer( tmpName, QStringLiteral( "result" ) );
|
||||
@ -503,7 +505,7 @@ void TestQgsRasterCalculator::calcWithReprojectedLayers()
|
||||
QgsRectangle extent( 783235, 3348110, 783350, 3347960 );
|
||||
|
||||
QTemporaryFile tmpFile;
|
||||
tmpFile.open(); // fileName is no avialable until open
|
||||
tmpFile.open(); // fileName is not available until open
|
||||
QString tmpName = tmpFile.fileName();
|
||||
tmpFile.close();
|
||||
|
||||
@ -511,7 +513,7 @@ void TestQgsRasterCalculator::calcWithReprojectedLayers()
|
||||
tmpName,
|
||||
QStringLiteral( "GTiff" ),
|
||||
extent, crs, 2, 3, entries );
|
||||
QCOMPARE( rc.processCalculation(), 0 );
|
||||
QCOMPARE( static_cast< int >( rc.processCalculation() ), 0 );
|
||||
|
||||
//open output file and check results
|
||||
QgsRasterLayer *result = new QgsRasterLayer( tmpName, QStringLiteral( "result" ) );
|
||||
@ -528,5 +530,85 @@ void TestQgsRasterCalculator::calcWithReprojectedLayers()
|
||||
delete block;
|
||||
}
|
||||
|
||||
void TestQgsRasterCalculator::errors()
|
||||
{
|
||||
QgsRasterCalculatorEntry entry1;
|
||||
entry1.bandNumber = 0; // bad band
|
||||
entry1.raster = mpLandsatRasterLayer;
|
||||
entry1.ref = QStringLiteral( "landsat@0" );
|
||||
|
||||
QVector<QgsRasterCalculatorEntry> entries;
|
||||
entries << entry1;
|
||||
|
||||
QgsCoordinateReferenceSystem crs;
|
||||
crs.createFromId( 32633, QgsCoordinateReferenceSystem::EpsgCrsId );
|
||||
QgsRectangle extent( 783235, 3348110, 783350, 3347960 );
|
||||
|
||||
QTemporaryFile tmpFile;
|
||||
tmpFile.open(); // fileName is not available until open
|
||||
QString tmpName = tmpFile.fileName();
|
||||
tmpFile.close();
|
||||
|
||||
QgsRasterCalculator rc( QStringLiteral( "\"landsat@0\"" ),
|
||||
tmpName,
|
||||
QStringLiteral( "GTiff" ),
|
||||
extent, crs, 2, 3, entries );
|
||||
QCOMPARE( static_cast< int >( rc.processCalculation() ), 6 );
|
||||
QCOMPARE( rc.lastError(), QStringLiteral( "Band number 0 is not valid for entry landsat@0" ) );
|
||||
|
||||
entry1.bandNumber = 10; // bad band
|
||||
entries.clear();
|
||||
entries << entry1;
|
||||
rc = QgsRasterCalculator( QStringLiteral( "\"landsat@0\"" ),
|
||||
tmpName,
|
||||
QStringLiteral( "GTiff" ),
|
||||
extent, crs, 2, 3, entries );
|
||||
QCOMPARE( static_cast< int >( rc.processCalculation() ), 6 );
|
||||
QCOMPARE( rc.lastError(), QStringLiteral( "Band number 10 is not valid for entry landsat@0" ) );
|
||||
|
||||
|
||||
// no raster
|
||||
entry1.raster = nullptr;
|
||||
entry1.bandNumber = 1;
|
||||
entries.clear();
|
||||
entries << entry1;
|
||||
rc = QgsRasterCalculator( QStringLiteral( "\"landsat@0\"" ),
|
||||
tmpName,
|
||||
QStringLiteral( "GTiff" ),
|
||||
extent, crs, 2, 3, entries );
|
||||
QCOMPARE( static_cast< int >( rc.processCalculation() ), 2 );
|
||||
QCOMPARE( rc.lastError(), QStringLiteral( "No raster layer for entry landsat@0" ) );
|
||||
|
||||
// bad driver
|
||||
entry1.raster = mpLandsatRasterLayer;
|
||||
entry1.bandNumber = 1;
|
||||
entries.clear();
|
||||
entries << entry1;
|
||||
rc = QgsRasterCalculator( QStringLiteral( "\"landsat@0\"" ),
|
||||
tmpName,
|
||||
QStringLiteral( "xxxxx" ),
|
||||
extent, crs, 2, 3, entries );
|
||||
QCOMPARE( static_cast< int >( rc.processCalculation() ), 1 );
|
||||
QCOMPARE( rc.lastError(), QStringLiteral( "Could not obtain driver for xxxxx" ) );
|
||||
|
||||
// bad filename
|
||||
rc = QgsRasterCalculator( QStringLiteral( "\"landsat@0\"" ),
|
||||
QStringLiteral( "/goodluckwritinghere/blah/blah.tif" ),
|
||||
QStringLiteral( "GTiff" ),
|
||||
extent, crs, 2, 3, entries );
|
||||
QCOMPARE( static_cast< int >( rc.processCalculation() ), 1 );
|
||||
QCOMPARE( rc.lastError(), QStringLiteral( "Could not create output /goodluckwritinghere/blah/blah.tif" ) );
|
||||
|
||||
// cancelled
|
||||
QgsFeedback feedback;
|
||||
feedback.cancel();
|
||||
rc = QgsRasterCalculator( QStringLiteral( "\"landsat@0\"" ),
|
||||
tmpName,
|
||||
QStringLiteral( "GTiff" ),
|
||||
extent, crs, 2, 3, entries );
|
||||
QCOMPARE( static_cast< int >( rc.processCalculation( &feedback ) ), 3 );
|
||||
QVERIFY( rc.lastError().isEmpty() );
|
||||
}
|
||||
|
||||
QGSTEST_MAIN( TestQgsRasterCalculator )
|
||||
#include "testqgsrastercalculator.moc"
|
||||
|
Loading…
x
Reference in New Issue
Block a user