Skip to content

Commit fd1d6c1

Browse files
committedSep 25, 2018
Add more descriptive error messages for raster calculation failure
With unit tests
1 parent f4bbb14 commit fd1d6c1

File tree

5 files changed

+149
-27
lines changed

5 files changed

+149
-27
lines changed
 

‎python/analysis/auto_generated/raster/qgsrastercalculator.sip.in

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,6 @@ Represents an individual raster layer/band number entry within a raster calculat
1818

1919
%TypeHeaderCode
2020
#include "qgsrastercalculator.h"
21-
%TypeHeaderCode
22-
#include <qgsrastercalculator.h>
23-
%End
2421
%End
2522
public:
2623

@@ -50,6 +47,7 @@ Performs raster layer calculations.
5047
Canceled,
5148
ParserError,
5249
MemoryError,
50+
BandError,
5351
};
5452

5553
QgsRasterCalculator( const QString &formulaString, const QString &outputFile, const QString &outputFormat,
@@ -83,7 +81,22 @@ QgsRasterCalculator constructor.
8381
.. versionadded:: 2.10
8482
%End
8583

86-
int processCalculation( QgsFeedback *feedback = 0 );
84+
Result processCalculation( QgsFeedback *feedback = 0 );
85+
%Docstring
86+
Starts the calculation and writes a new raster.
87+
88+
The optional ``feedback`` argument can be used for progress reporting and cancelation support.
89+
90+
:return: QgsRasterCalculator.Success in case of success. If an error is encountered then
91+
a description of the error can be obtained by calling lastError().
92+
%End
93+
94+
QString lastError() const;
95+
%Docstring
96+
Returns a description of the last error encountered.
97+
98+
.. versionadded:: 3.4
99+
%End
87100

88101
};
89102

‎src/analysis/raster/qgsrastercalculator.cpp

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -58,15 +58,16 @@ QgsRasterCalculator::QgsRasterCalculator( const QString &formulaString, const QS
5858
{
5959
}
6060

61-
int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
61+
QgsRasterCalculator::Result QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
6262
{
63+
mLastError.clear();
64+
6365
//prepare search string / tree
64-
QString errorString;
65-
std::unique_ptr< QgsRasterCalcNode > calcNode( QgsRasterCalcNode::parseRasterCalcString( mFormulaString, errorString ) );
66+
std::unique_ptr< QgsRasterCalcNode > calcNode( QgsRasterCalcNode::parseRasterCalcString( mFormulaString, mLastError ) );
6667
if ( !calcNode )
6768
{
6869
//error
69-
return static_cast<int>( ParserError );
70+
return ParserError;
7071
}
7172

7273
QMap< QString, QgsRasterBlock * > inputBlocks;
@@ -75,8 +76,16 @@ int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
7576
{
7677
if ( !it->raster ) // no raster layer in entry
7778
{
79+
mLastError = QObject::tr( "No raster layer for entry %1" ).arg( it->ref );
80+
qDeleteAll( inputBlocks );
81+
return InputLayerError;
82+
}
83+
84+
if ( it->bandNumber <= 0 || it->bandNumber > it->raster->bandCount() )
85+
{
86+
mLastError = QObject::tr( "Band number %1 is not valid for entry %2" ).arg( it->bandNumber ).arg( it->ref );
7887
qDeleteAll( inputBlocks );
79-
return static_cast< int >( InputLayerError );
88+
return BandError;
8089
}
8190

8291
std::unique_ptr< QgsRasterBlock > block;
@@ -94,7 +103,7 @@ int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
94103
if ( rasterBlockFeedback->isCanceled() )
95104
{
96105
qDeleteAll( inputBlocks );
97-
return static_cast< int >( Canceled );
106+
return Canceled;
98107
}
99108
}
100109
else
@@ -103,8 +112,9 @@ int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
103112
}
104113
if ( block->isEmpty() )
105114
{
115+
mLastError = QObject::tr( "Could not allocate required memory for %1" ).arg( it->ref );
106116
qDeleteAll( inputBlocks );
107-
return static_cast<int>( MemoryError );
117+
return MemoryError;
108118
}
109119
inputBlocks.insert( it->ref, block.release() );
110120
}
@@ -113,13 +123,15 @@ int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
113123
GDALDriverH outputDriver = openOutputDriver();
114124
if ( !outputDriver )
115125
{
116-
return static_cast< int >( CreateOutputError );
126+
mLastError = QObject::tr( "Could not obtain driver for %1" ).arg( mOutputFormat );
127+
return CreateOutputError;
117128
}
118129

119130
gdal::dataset_unique_ptr outputDataset( openOutputFile( outputDriver ) );
120131
if ( !outputDataset )
121132
{
122-
return static_cast< int >( CreateOutputError );
133+
mLastError = QObject::tr( "Could not create output %1" ).arg( mOutputFile );
134+
return CreateOutputError;
123135
}
124136

125137
GDALSetProjection( outputDataset.get(), mOutputCrs.toWkt().toLocal8Bit().data() );
@@ -179,9 +191,9 @@ int QgsRasterCalculator::processCalculation( QgsFeedback *feedback )
179191
{
180192
//delete the dataset without closing (because it is faster)
181193
gdal::fast_delete_and_close( outputDataset, outputDriver, mOutputFile );
182-
return static_cast< int >( Canceled );
194+
return Canceled;
183195
}
184-
return static_cast< int >( Success );
196+
return Success;
185197
}
186198

187199
GDALDriverH QgsRasterCalculator::openOutputDriver()
@@ -229,3 +241,8 @@ void QgsRasterCalculator::outputGeoTransform( double *transform ) const
229241
transform[4] = 0;
230242
transform[5] = -mOutputRectangle.height() / mNumOutputRows;
231243
}
244+
245+
QString QgsRasterCalculator::lastError() const
246+
{
247+
return mLastError;
248+
}

‎src/analysis/raster/qgsrastercalculator.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,6 @@ class QgsFeedback;
3636
*/
3737
class ANALYSIS_EXPORT QgsRasterCalculatorEntry
3838
{
39-
#ifdef SIP_RUN
40-
% TypeHeaderCode
41-
#include <qgsrastercalculator.h>
42-
% End
43-
#endif
4439

4540
public:
4641

@@ -77,6 +72,7 @@ class ANALYSIS_EXPORT QgsRasterCalculator
7772
Canceled = 3, //!< User canceled calculation
7873
ParserError = 4, //!< Error parsing formula
7974
MemoryError = 5, //!< Error allocating memory for result
75+
BandError = 6, //!< Invalid band number for input
8076
};
8177

8278
/**
@@ -111,10 +107,17 @@ class ANALYSIS_EXPORT QgsRasterCalculator
111107
* Starts the calculation and writes a new raster.
112108
*
113109
* The optional \a feedback argument can be used for progress reporting and cancelation support.
114-
* \returns 0 in case of success
110+
*
111+
* \returns QgsRasterCalculator::Success in case of success. If an error is encountered then
112+
* a description of the error can be obtained by calling lastError().
115113
*/
116-
//TODO QGIS 3.0 - return QgsRasterCalculator::Result
117-
int processCalculation( QgsFeedback *feedback = nullptr );
114+
Result processCalculation( QgsFeedback *feedback = nullptr );
115+
116+
/**
117+
* Returns a description of the last error encountered.
118+
* \since QGIS 3.4
119+
*/
120+
QString lastError() const;
118121

119122
private:
120123
//default constructor forbidden. We need formula, output file, output format and output raster resolution obligatory
@@ -148,6 +151,8 @@ class ANALYSIS_EXPORT QgsRasterCalculator
148151
//! Number of output rows
149152
int mNumOutputRows = 0;
150153

154+
QString mLastError;
155+
151156
/***/
152157
QVector<QgsRasterCalculatorEntry> mRasterEntries;
153158
};

‎src/app/qgisapp.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5593,6 +5593,11 @@ void QgisApp::showRasterCalculator()
55935593
Qgis::Critical );
55945594
break;
55955595

5596+
case QgsRasterCalculator::BandError:
5597+
messageBar()->pushMessage( tr( "Raster calculator" ),
5598+
tr( "Invalid band number for input layer." ),
5599+
Qgis::Critical );
5600+
break;
55965601
}
55975602
}
55985603
}

‎tests/src/analysis/testqgsrastercalculator.cpp

Lines changed: 86 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ class TestQgsRasterCalculator : public QObject
5454
void calcWithLayers();
5555
void calcWithReprojectedLayers();
5656

57+
void errors();
58+
5759
private:
5860

5961
QgsRasterLayer *mpLandsatRasterLayer = nullptr;
@@ -445,7 +447,7 @@ void TestQgsRasterCalculator::calcWithLayers()
445447
tmpName,
446448
QStringLiteral( "GTiff" ),
447449
extent, crs, 2, 3, entries );
448-
QCOMPARE( rc.processCalculation(), 0 );
450+
QCOMPARE( static_cast< int >( rc.processCalculation() ), 0 );
449451

450452
//open output file and check results
451453
QgsRasterLayer *result = new QgsRasterLayer( tmpName, QStringLiteral( "result" ) );
@@ -466,7 +468,7 @@ void TestQgsRasterCalculator::calcWithLayers()
466468
tmpName,
467469
QStringLiteral( "GTiff" ),
468470
extent, crs, 2, 3, entries );
469-
QCOMPARE( rc2.processCalculation(), 0 );
471+
QCOMPARE( static_cast< int >( rc2.processCalculation() ), 0 );
470472

471473
//open output file and check results
472474
result = new QgsRasterLayer( tmpName, QStringLiteral( "result" ) );
@@ -503,15 +505,15 @@ void TestQgsRasterCalculator::calcWithReprojectedLayers()
503505
QgsRectangle extent( 783235, 3348110, 783350, 3347960 );
504506

505507
QTemporaryFile tmpFile;
506-
tmpFile.open(); // fileName is no avialable until open
508+
tmpFile.open(); // fileName is not available until open
507509
QString tmpName = tmpFile.fileName();
508510
tmpFile.close();
509511

510512
QgsRasterCalculator rc( QStringLiteral( "\"landsat@1\" + \"landsat_4326@2\"" ),
511513
tmpName,
512514
QStringLiteral( "GTiff" ),
513515
extent, crs, 2, 3, entries );
514-
QCOMPARE( rc.processCalculation(), 0 );
516+
QCOMPARE( static_cast< int >( rc.processCalculation() ), 0 );
515517

516518
//open output file and check results
517519
QgsRasterLayer *result = new QgsRasterLayer( tmpName, QStringLiteral( "result" ) );
@@ -528,5 +530,85 @@ void TestQgsRasterCalculator::calcWithReprojectedLayers()
528530
delete block;
529531
}
530532

533+
void TestQgsRasterCalculator::errors()
534+
{
535+
QgsRasterCalculatorEntry entry1;
536+
entry1.bandNumber = 0; // bad band
537+
entry1.raster = mpLandsatRasterLayer;
538+
entry1.ref = QStringLiteral( "landsat@0" );
539+
540+
QVector<QgsRasterCalculatorEntry> entries;
541+
entries << entry1;
542+
543+
QgsCoordinateReferenceSystem crs;
544+
crs.createFromId( 32633, QgsCoordinateReferenceSystem::EpsgCrsId );
545+
QgsRectangle extent( 783235, 3348110, 783350, 3347960 );
546+
547+
QTemporaryFile tmpFile;
548+
tmpFile.open(); // fileName is not available until open
549+
QString tmpName = tmpFile.fileName();
550+
tmpFile.close();
551+
552+
QgsRasterCalculator rc( QStringLiteral( "\"landsat@0\"" ),
553+
tmpName,
554+
QStringLiteral( "GTiff" ),
555+
extent, crs, 2, 3, entries );
556+
QCOMPARE( static_cast< int >( rc.processCalculation() ), 6 );
557+
QCOMPARE( rc.lastError(), QStringLiteral( "Band number 0 is not valid for entry landsat@0" ) );
558+
559+
entry1.bandNumber = 10; // bad band
560+
entries.clear();
561+
entries << entry1;
562+
rc = QgsRasterCalculator( QStringLiteral( "\"landsat@0\"" ),
563+
tmpName,
564+
QStringLiteral( "GTiff" ),
565+
extent, crs, 2, 3, entries );
566+
QCOMPARE( static_cast< int >( rc.processCalculation() ), 6 );
567+
QCOMPARE( rc.lastError(), QStringLiteral( "Band number 10 is not valid for entry landsat@0" ) );
568+
569+
570+
// no raster
571+
entry1.raster = nullptr;
572+
entry1.bandNumber = 1;
573+
entries.clear();
574+
entries << entry1;
575+
rc = QgsRasterCalculator( QStringLiteral( "\"landsat@0\"" ),
576+
tmpName,
577+
QStringLiteral( "GTiff" ),
578+
extent, crs, 2, 3, entries );
579+
QCOMPARE( static_cast< int >( rc.processCalculation() ), 2 );
580+
QCOMPARE( rc.lastError(), QStringLiteral( "No raster layer for entry landsat@0" ) );
581+
582+
// bad driver
583+
entry1.raster = mpLandsatRasterLayer;
584+
entry1.bandNumber = 1;
585+
entries.clear();
586+
entries << entry1;
587+
rc = QgsRasterCalculator( QStringLiteral( "\"landsat@0\"" ),
588+
tmpName,
589+
QStringLiteral( "xxxxx" ),
590+
extent, crs, 2, 3, entries );
591+
QCOMPARE( static_cast< int >( rc.processCalculation() ), 1 );
592+
QCOMPARE( rc.lastError(), QStringLiteral( "Could not obtain driver for xxxxx" ) );
593+
594+
// bad filename
595+
rc = QgsRasterCalculator( QStringLiteral( "\"landsat@0\"" ),
596+
QStringLiteral( "/goodluckwritinghere/blah/blah.tif" ),
597+
QStringLiteral( "GTiff" ),
598+
extent, crs, 2, 3, entries );
599+
QCOMPARE( static_cast< int >( rc.processCalculation() ), 1 );
600+
QCOMPARE( rc.lastError(), QStringLiteral( "Could not create output /goodluckwritinghere/blah/blah.tif" ) );
601+
602+
// cancelled
603+
QgsFeedback feedback;
604+
feedback.cancel();
605+
rc = QgsRasterCalculator( QStringLiteral( "\"landsat@0\"" ),
606+
tmpName,
607+
QStringLiteral( "GTiff" ),
608+
extent, crs, 2, 3, entries );
609+
QCOMPARE( static_cast< int >( rc.processCalculation( &feedback ) ), 3 );
610+
QVERIFY( rc.lastError().isEmpty() );
611+
}
612+
531613
QGSTEST_MAIN( TestQgsRasterCalculator )
532614
#include "testqgsrastercalculator.moc"

0 commit comments

Comments
 (0)
Please sign in to comment.