Skip to content
Permalink
Browse files

Merge pull request #3134 from rouault/do_not_write_nodata_if_not_exis…

…ting

Nodata related fixes
  • Loading branch information
rouault committed May 30, 2016
2 parents df6410e + f3b635d commit 4b130cbf42af901be74b57918c726de16a57e6ba
@@ -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 */
@@ -95,8 +95,13 @@ bool QgsRasterChecker::runTest( const QString& theVerifiedKey, QString theVerifi
compare( "Source data type", verifiedProvider->srcDataType( band ), expectedProvider->srcDataType( band ), mReport, typesOk );
compare( "Data type", verifiedProvider->dataType( band ), expectedProvider->dataType( band ), mReport, typesOk );

// TODO: not yet sure if noDataValue() should exist at all
//compare( "No data (NULL) value", verifiedProvider->noDataValue( band ), expectedProvider->noDataValue( band ), mReport, typesOk );
// Check nodata
bool noDataOk = true;
compare( "No data (NULL) value existence flag", verifiedProvider->srcHasNoDataValue( band ), expectedProvider->srcHasNoDataValue( band ), mReport, noDataOk );
if ( verifiedProvider->srcHasNoDataValue( band ) && expectedProvider->srcHasNoDataValue( band ) )
{
compare( "No data (NULL) value", verifiedProvider->srcNoDataValue( band ), expectedProvider->srcNoDataValue( band ), mReport, noDataOk );
}

bool statsOk = true;
QgsRasterBandStats verifiedStats = verifiedProvider->bandStatistics( band );
@@ -122,7 +127,7 @@ bool QgsRasterChecker::runTest( const QString& theVerifiedKey, QString theVerifi
mReport += "</table>";
mReport += "<br>";

if ( !statsOk || !typesOk )
if ( !statsOk || !typesOk || !noDataOk )
{
allOk = false;
// create values table anyway so that values are available
@@ -342,7 +342,7 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster(
{
iter->startRasterRead( i, nCols, nRows, outputExtent );
blockList.push_back( nullptr );
if ( destProvider ) // no tiles
if ( destProvider && destHasNoDataValueList.value( i - 1 ) ) // no tiles
{
destProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
}
@@ -436,7 +436,10 @@ QgsRasterFileWriter::WriterError QgsRasterFileWriter::writeDataRaster(
//write data to output file. todo: loop over the data list
for ( int i = 1; i <= nBands; ++i )
{
partDestProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
if ( destHasNoDataValueList.value( i - 1 ) )
{
partDestProvider->setNoDataValue( i, destNoDataValueList.value( i - 1 ) );
}
partDestProvider->write( destBlockList[i - 1]->bits( 0 ), i, iterCols, iterRows, 0, 0 );
delete destBlockList[i - 1];
addToVRT( partFileName( fileIndex ), i, iterCols, iterRows, iterLeft, iterTop );
@@ -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.
Binary file not shown.

0 comments on commit 4b130cb

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