Skip to content
Permalink
Browse files

[GDAL provider] Ignore nodata value that are not representable in the…

… data type.

This is related to commit e0d38ba. 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.
  • Loading branch information
rouault committed May 28, 2016
1 parent e0d38ba commit f3b635dcbbbd42845ec47a517cd89a9953a1260a
@@ -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 )
@@ -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 */
@@ -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 ) );
@@ -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.

0 comments on commit f3b635d

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