[GDAL provider] Ignore nodata value that are not representable in the data type.

This is related to commit e0d38ba3f912b7ae6b34e66a44e529ec37394458. In case
we have to deal with an inconsitent raster where the nodata value is set to
a value not representable in the data type, ignore it. Otherwise, a NaN value
in a Byte raster would be cast as 0.
This commit is contained in:
Even Rouault 2016-05-28 17:28:59 +02:00
parent e0d38ba3f9
commit f3b635dcbb
5 changed files with 128 additions and 0 deletions

View File

@ -50,6 +50,29 @@ QgsRaster::ContrastEnhancementLimits QgsRaster::contrastEnhancementLimitsFromStr
return ContrastEnhancementNone;
}
bool QgsRaster::isRepresentableValue( double value, QGis::DataType dataType )
{
switch ( dataType )
{
case QGis::Byte:
return value >= std::numeric_limits<quint8>::min() && value <= std::numeric_limits<quint8>::max();
case QGis::UInt16:
return value >= std::numeric_limits<quint16>::min() && value <= std::numeric_limits<quint16>::max();
case QGis::Int16:
return value >= std::numeric_limits<qint16>::min() && value <= std::numeric_limits<qint16>::max();
case QGis::UInt32:
return value >= std::numeric_limits<quint32>::min() && value <= std::numeric_limits<quint32>::max();
case QGis::Int32:
return value >= std::numeric_limits<qint32>::min() && value <= std::numeric_limits<qint32>::max();
case QGis::Float32:
return qIsNaN( value ) || qIsInf( value ) ||
( value >= -std::numeric_limits<float>::max() && value <= std::numeric_limits<float>::max() );
default:
return true;
break;
}
}
double QgsRaster::representableValue( double value, QGis::DataType dataType )
{
switch ( dataType )

View File

@ -110,8 +110,18 @@ class CORE_EXPORT QgsRaster
static QString contrastEnhancementLimitsAsString( QgsRaster::ContrastEnhancementLimits theLimits );
static ContrastEnhancementLimits contrastEnhancementLimitsFromString( const QString& theLimits );
/** Check if the specified value is representable in the given data type.
* Supported are numerical types Byte, UInt16, Int16, UInt32, Int32, Float32, Float64.
* @param value
* @param dataType
* @note added in version 2.16
* @note not available in Python bindings */
static bool isRepresentableValue( double value, QGis::DataType dataType );
/** Get value representable by given data type.
* Supported are numerical types Byte, UInt16, Int16, UInt32, Int32, Float32, Float64.
* This is done through C casting, so you have to be sure that the provided value is
* representable in the output data type. This can be checked with isRepresentableValue().
* @param value
* @param dataType
* @note added in version 2.1 */

View File

@ -2643,6 +2643,15 @@ void QgsGdalProvider::initBaseDataset()
int isValid = false;
double myNoDataValue = GDALGetRasterNoDataValue( myGdalBand, &isValid );
// We check that the double value we just got is representable in the
// data type. In normal situations this should not be needed, but it happens
// to have 8bit TIFFs with nan as the nodata value. If not checking against
// the min/max bounds, it would be cast to 0 by representableValue().
if ( isValid && !QgsRaster::isRepresentableValue( myNoDataValue, dataTypeFromGdal( myGdalDataType ) ) )
{
QgsDebugMsg( QString( "GDALGetRasterNoDataValue = %1 is not representable in data type, so ignoring it" ).arg( myNoDataValue ) );
isValid = false;
}
if ( isValid )
{
QgsDebugMsg( QString( "GDALGetRasterNoDataValue = %1" ).arg( myNoDataValue ) );

View File

@ -12,6 +12,9 @@
* (at your option) any later version. *
* *
***************************************************************************/
#include <limits>
#include <QtTest/QtTest>
#include <QObject>
#include <QString>
@ -42,6 +45,9 @@ class TestQgsGdalProvider : public QObject
void scaleDataType(); //test resultant data types for int raster with float scale (#11573)
void warpedVrt(); //test loading raster which requires a warped vrt
void noData();
void invalidNoDataInSourceIgnored();
void isRepresentableValue();
private:
QString mTestDataDir;
@ -104,5 +110,85 @@ void TestQgsGdalProvider::warpedVrt()
delete provider;
}
void TestQgsGdalProvider::noData()
{
QString raster = QString( TEST_DATA_DIR ) + "/raster/band1_byte_ct_epsg4326.tif";
QgsDataProvider* provider = QgsProviderRegistry::instance()->provider( "gdal", raster );
QVERIFY( provider->isValid() );
QgsRasterDataProvider* rp = dynamic_cast< QgsRasterDataProvider* >( provider );
QVERIFY( rp );
QCOMPARE( rp->srcNoDataValue( 1 ), static_cast<double>( 255 ) );
delete provider;
}
void TestQgsGdalProvider::invalidNoDataInSourceIgnored()
{
QString raster = QString( TEST_DATA_DIR ) + "/raster/byte_with_nan_nodata.tif";
QgsDataProvider* provider = QgsProviderRegistry::instance()->provider( "gdal", raster );
QVERIFY( provider->isValid() );
QgsRasterDataProvider* rp = dynamic_cast< QgsRasterDataProvider* >( provider );
QVERIFY( rp );
QCOMPARE( rp->srcHasNoDataValue( 1 ), false );
delete provider;
}
void TestQgsGdalProvider::isRepresentableValue()
{
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::infinity(), QGis::Byte ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -std::numeric_limits<double>::infinity(), QGis::Byte ), false );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::quiet_NaN(), QGis::Byte ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -1., QGis::Byte ), false );
QCOMPARE( QgsRaster::isRepresentableValue( 0., QGis::Byte ), true );
QCOMPARE( QgsRaster::isRepresentableValue( 255., QGis::Byte ), true );
QCOMPARE( QgsRaster::isRepresentableValue( 256., QGis::Byte ), false );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::infinity(), QGis::UInt16 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -std::numeric_limits<double>::infinity(), QGis::UInt16 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::quiet_NaN(), QGis::UInt16 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -1., QGis::UInt16 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( 0., QGis::UInt16 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( 65535., QGis::UInt16 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( 65536., QGis::UInt16 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::infinity(), QGis::Int16 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -std::numeric_limits<double>::infinity(), QGis::Int16 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::quiet_NaN(), QGis::Int16 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -32769., QGis::Int16 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -32768., QGis::Int16 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( 32767., QGis::Int16 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( 32768., QGis::Int16 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::infinity(), QGis::UInt32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -std::numeric_limits<double>::infinity(), QGis::UInt32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::quiet_NaN(), QGis::UInt32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -1., QGis::UInt32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( 0., QGis::UInt32 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( 4294967295., QGis::UInt32 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( 4294967296., QGis::UInt32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::infinity(), QGis::Int32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -std::numeric_limits<double>::infinity(), QGis::Int32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::quiet_NaN(), QGis::Int32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -2147483649., QGis::Int32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -2147483648., QGis::Int32 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( 2147483647., QGis::Int32 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( 2147483648., QGis::Int32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( 4294967296., QGis::UInt32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::infinity(), QGis::Float32 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( -std::numeric_limits<double>::infinity(), QGis::Float32 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::quiet_NaN(), QGis::Float32 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( -std::numeric_limits<double>::max(), QGis::Float32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::max(), QGis::Float32 ), false );
QCOMPARE( QgsRaster::isRepresentableValue( -std::numeric_limits<float>::max(), QGis::Float32 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<float>::max(), QGis::Float32 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::infinity(), QGis::Float64 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( -std::numeric_limits<double>::infinity(), QGis::Float64 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::quiet_NaN(), QGis::Float64 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( -std::numeric_limits<double>::max(), QGis::Float64 ), true );
QCOMPARE( QgsRaster::isRepresentableValue( std::numeric_limits<double>::max(), QGis::Float64 ), true );
}
QTEST_MAIN( TestQgsGdalProvider )
#include "testqgsgdalprovider.moc"

Binary file not shown.