Skip to content

Commit

Permalink
Add methods to set/get nodata value as Int64/UInt64
Browse files Browse the repository at this point in the history
- GDALRasterBand:
    virtual int64_t GetNoDataValueAsInt64( int *pbSuccess = nullptr );
    virtual uint64_t GetNoDataValueAsUInt64( int *pbSuccess = nullptr );
    virtual CPLErr SetNoDataValueAsInt64( int64_t nNoData );
    virtual CPLErr SetNoDataValueAsUInt64( uint64_t nNoData );

  and corresponding C API and Python bindings

- GDALMDArray:
    int64_t GetNoDataValueAsInt64(bool* pbHasNoData = nullptr) const;
    uint64_t GetNoDataValueAsUInt64(bool* pbHasNoData = nullptr) const;
    bool SetNoDataValue(int64_t nNoData);
    bool SetNoDataValue(uint64_t nNoData);

  and corresponding C API and Python bindings

Adapt:
- gdalinfo
- gdal_translate
- gdal_create
- gdal_edit.py
- MEM, GTIFF, VRT, netCDF and ZARR drivers
  • Loading branch information
rouault committed Mar 8, 2022
1 parent a28782e commit 0f9bc86
Show file tree
Hide file tree
Showing 38 changed files with 3,588 additions and 886 deletions.
42 changes: 37 additions & 5 deletions apps/gdal_create.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "commonutils.h"
#include "ogr_spatialref.h"

#include <cstdlib>
#include <memory>
#include <vector>

Expand Down Expand Up @@ -108,7 +109,7 @@ MAIN_START(argc, argv)
std::vector<double> adfBurnValues;
bool bQuiet = false;
int bSetNoData = false;
double dfNoDataValue = 0;
std::string osNoData;
const char* pszInputFile = nullptr;
for( int i = 1; argv != nullptr && argv[i] != nullptr; i++ )
{
Expand Down Expand Up @@ -193,7 +194,7 @@ MAIN_START(argc, argv)
bSetNoData = true;
++i;
// coverity[tainted_data]
dfNoDataValue = CPLAtofM(argv[i]);
osNoData = argv[i];
}

else if( i < argc-1 && EQUAL(argv[i],"-burn") )
Expand Down Expand Up @@ -299,7 +300,24 @@ MAIN_START(argc, argv)
}
if( !bSetNoData && poInputDS->GetRasterCount() > 0 )
{
dfNoDataValue = poInputDS->GetRasterBand(1)->GetNoDataValue(&bSetNoData);
if( eDT == GDT_Int64 )
{
const auto nNoDataValue = poInputDS->GetRasterBand(1)->GetNoDataValueAsInt64(&bSetNoData);
if( bSetNoData )
osNoData = CPLSPrintf(CPL_FRMT_GIB, static_cast<GIntBig>(nNoDataValue));
}
else if( eDT == GDT_UInt64 )
{
const auto nNoDataValue = poInputDS->GetRasterBand(1)->GetNoDataValueAsUInt64(&bSetNoData);
if( bSetNoData )
osNoData = CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoDataValue));
}
else
{
const double dfNoDataValue = poInputDS->GetRasterBand(1)->GetNoDataValue(&bSetNoData);
if( bSetNoData )
osNoData = CPLSPrintf("%.18g", dfNoDataValue);
}
}
}

Expand Down Expand Up @@ -404,8 +422,22 @@ MAIN_START(argc, argv)
{
for( int i = 0; i < nBands; i++ )
{
GDALSetRasterNoDataValue(GDALGetRasterBand(hDS, i+1),
dfNoDataValue);
auto hBand = GDALGetRasterBand(hDS, i+1);
if( eDT == GDT_Int64 )
{
GDALSetRasterNoDataValueAsInt64(hBand,
static_cast<int64_t>(std::strtoll(osNoData.c_str(), nullptr, 10)));
}
else if( eDT == GDT_UInt64 )
{
GDALSetRasterNoDataValueAsUInt64(hBand,
static_cast<uint64_t>(std::strtoull(osNoData.c_str(), nullptr, 10)));
}
else
{
GDALSetRasterNoDataValue(hBand,
CPLAtofM(osNoData.c_str()));
}
}
}
if( !adfBurnValues.empty() )
Expand Down
112 changes: 99 additions & 13 deletions apps/gdal_translate_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ struct GDALTranslateOptions
specified SRS. */
double adfULLR[4];

/*! set a nodata value specified in GDALTranslateOptions::dfNoDataReal to the output bands */
/*! set a nodata value specified in GDALTranslateOptions::szNoData to the output bands */
bool bSetNoData;

/*! avoid setting a nodata value to the output file if one exists for the source file */
Expand All @@ -221,7 +221,7 @@ struct GDALTranslateOptions
/*! Assign a specified nodata value to output bands ( GDALTranslateOptions::bSetNoData option
should be set). Note that if the input dataset has a nodata value, this does not cause
pixel values that are equal to that nodata value to be changed to the value specified. */
double dfNoDataReal;
char szNoData[32];

/*! to expose a dataset with 1 band with a color table as a dataset with
3 (RGB) or 4 (RGBA) bands. Useful for output drivers such as JPEG,
Expand Down Expand Up @@ -2085,9 +2085,84 @@ GDALDatasetH GDALTranslate( const char *pszDest, GDALDatasetH hSrcDataset,
/* -------------------------------------------------------------------- */
if( psOptions->bSetNoData )
{
const double dfVal = AdjustNoDataValue(
psOptions->dfNoDataReal, poVRTBand, psOptions);
poVRTBand->SetNoDataValue( dfVal );
if( poVRTBand->GetRasterDataType() == GDT_Int64 )
{
if( strchr(psOptions->szNoData, '.') ||
CPLGetValueType(psOptions->szNoData) == CPL_VALUE_STRING )
{
const double dfNoData = CPLAtof(psOptions->szNoData);
if( dfNoData >= static_cast<double>(std::numeric_limits<int64_t>::min()) &&
dfNoData <= static_cast<double>(std::numeric_limits<int64_t>::max()) &&
dfNoData == static_cast<double>(static_cast<int64_t>(dfNoData)) )
{
poVRTBand->SetNoDataValueAsInt64(
static_cast<int64_t>(dfNoData));
}
else
{
CPLError(CE_Warning, CPLE_AppDefined,
"Cannot set nodata value %s on a Int64 band",
psOptions->szNoData);
}
}
else
{
errno = 0;
const auto val = std::strtoll(psOptions->szNoData, nullptr, 10);
if( errno == 0 )
{
poVRTBand->SetNoDataValueAsInt64(static_cast<int64_t>(val));
}
else
{
CPLError(CE_Warning, CPLE_AppDefined,
"Cannot set nodata value %s on a Int64 band",
psOptions->szNoData);
}
}
}
else if( poVRTBand->GetRasterDataType() == GDT_UInt64 )
{
if( strchr(psOptions->szNoData, '.') ||
CPLGetValueType(psOptions->szNoData) == CPL_VALUE_STRING )
{
const double dfNoData = CPLAtof(psOptions->szNoData);
if( dfNoData >= static_cast<double>(std::numeric_limits<uint64_t>::min()) &&
dfNoData <= static_cast<double>(std::numeric_limits<uint64_t>::max()) &&
dfNoData == static_cast<double>(static_cast<uint64_t>(dfNoData)) )
{
poVRTBand->SetNoDataValueAsUInt64(
static_cast<uint64_t>(dfNoData));
}
else
{
CPLError(CE_Warning, CPLE_AppDefined,
"Cannot set nodata value %s on a UInt64 band",
psOptions->szNoData);
}
}
else
{
errno = 0;
const auto val = std::strtoull(psOptions->szNoData, nullptr, 10);
if( errno == 0 )
{
poVRTBand->SetNoDataValueAsUInt64(static_cast<uint64_t>(val));
}
else
{
CPLError(CE_Warning, CPLE_AppDefined,
"Cannot set nodata value %s on a UInt64 band",
psOptions->szNoData);
}
}
}
else
{
const double dfVal = AdjustNoDataValue(
CPLAtof(psOptions->szNoData), poVRTBand, psOptions);
poVRTBand->SetNoDataValue( dfVal );
}
}

if( psOptions->bSetScale )
Expand Down Expand Up @@ -2270,13 +2345,23 @@ static void CopyBandInfo( GDALRasterBand * poSrcBand, GDALRasterBand * poDstBand

if (bCopyNoData)
{
int bSuccess = FALSE;
double dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
if( bSuccess )
if( poSrcBand->GetRasterDataType() != GDT_Int64 &&
poSrcBand->GetRasterDataType() != GDT_UInt64 &&
poDstBand->GetRasterDataType() != GDT_Int64 &&
poDstBand->GetRasterDataType() != GDT_UInt64 )
{
int bSuccess = FALSE;
double dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
if( bSuccess )
{
const double dfVal = AdjustNoDataValue(
dfNoData, poDstBand, psOptions);
poDstBand->SetNoDataValue( dfVal );
}
}
else
{
const double dfVal = AdjustNoDataValue(
dfNoData, poDstBand, psOptions);
poDstBand->SetNoDataValue( dfVal );
GDALCopyNoDataValue(poDstBand, poSrcBand);
}
}

Expand Down Expand Up @@ -2391,7 +2476,7 @@ GDALTranslateOptions *GDALTranslateOptionsNew(char** papszArgv, GDALTranslateOpt
psOptions->adfULLR[3] = 0;
psOptions->bSetNoData = false;
psOptions->bUnsetNoData = false;
psOptions->dfNoDataReal = 0.0;
psOptions->szNoData[0] = 0;
psOptions->nRGBExpand = 0;
psOptions->nMaskBand = 0;
psOptions->bStats = false;
Expand Down Expand Up @@ -2574,7 +2659,8 @@ GDALTranslateOptions *GDALTranslateOptionsNew(char** papszArgv, GDALTranslateOpt
else
{
psOptions->bSetNoData = true;
psOptions->dfNoDataReal = CPLAtofM(papszArgv[i+1]);
snprintf(psOptions->szNoData, sizeof(psOptions->szNoData),
"%s", papszArgv[i+1]);
}
i += 1;
}
Expand Down
78 changes: 66 additions & 12 deletions apps/gdalinfo_lib.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1003,24 +1003,78 @@ char *GDALInfo( GDALDatasetH hDataset, const GDALInfoOptions *psOptions )
}

int bGotNodata = FALSE;
const double dfNoData = GDALGetRasterNoDataValue( hBand, &bGotNodata );
if( bGotNodata )
if( GDALGetRasterDataType(hBand) == GDT_Int64 )
{
if( bJson )
const auto nNoData = GDALGetRasterNoDataValueAsInt64( hBand, &bGotNodata );
if( bGotNodata )
{
json_object *poNoDataValue = gdal_json_object_new_double_or_str_for_non_finite(dfNoData, 18);
json_object_object_add(poBand, "noDataValue",
poNoDataValue);
if( bJson )
{
json_object *poNoDataValue = json_object_new_int64(nNoData);
json_object_object_add(poBand, "noDataValue",
poNoDataValue);
}
else
{
Concat(osStr, psOptions->bStdoutOutput,
" NoData Value=" CPL_FRMT_GIB "\n",
static_cast<GIntBig>(nNoData) );
}
}
else if( CPLIsNan(dfNoData) )
}
else if( GDALGetRasterDataType(hBand) == GDT_UInt64 )
{
const auto nNoData = GDALGetRasterNoDataValueAsUInt64( hBand, &bGotNodata );
if( bGotNodata )
{
Concat(osStr, psOptions->bStdoutOutput,
" NoData Value=nan\n" );
if( bJson )
{
if( nNoData < static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) )
{
json_object *poNoDataValue = json_object_new_int64(
static_cast<int64_t>(nNoData));
json_object_object_add(poBand, "noDataValue",
poNoDataValue);
}
else
{
// not pretty to serialize as a string but there's no
// way to serialize a uint64_t with libjson-c
json_object *poNoDataValue = json_object_new_string(
CPLSPrintf(CPL_FRMT_GUIB, static_cast<GUIntBig>(nNoData)));
json_object_object_add(poBand, "noDataValue",
poNoDataValue);
}
}
else
{
Concat(osStr, psOptions->bStdoutOutput,
" NoData Value=" CPL_FRMT_GUIB "\n",
static_cast<GUIntBig>(nNoData) );
}
}
else
}
else
{
const double dfNoData = GDALGetRasterNoDataValue( hBand, &bGotNodata );
if( bGotNodata )
{
Concat(osStr, psOptions->bStdoutOutput,
" NoData Value=%.18g\n", dfNoData );
if( bJson )
{
json_object *poNoDataValue = gdal_json_object_new_double_or_str_for_non_finite(dfNoData, 18);
json_object_object_add(poBand, "noDataValue",
poNoDataValue);
}
else if( CPLIsNan(dfNoData) )
{
Concat(osStr, psOptions->bStdoutOutput,
" NoData Value=nan\n" );
}
else
{
Concat(osStr, psOptions->bStdoutOutput,
" NoData Value=%.18g\n", dfNoData );
}
}
}

Expand Down
54 changes: 54 additions & 0 deletions autotest/gcore/tiff_write.py
Original file line number Diff line number Diff line change
Expand Up @@ -8320,6 +8320,32 @@ def test_tiff_write_uint64():
ut = gdaltest.GDALTest('GTiff', 'gtiff/uint64.tif', 1, 1)
return ut.testCreateCopy()


###############################################################################


def test_tiff_write_uint64_nodata():

filename = '/vsimem/test_tiff_write_uint64_nodata.tif'
ds = gdal.GetDriverByName('GTiff').Create(filename, 1, 1, 1, gdal.GDT_UInt64)
val = (1 << 64)-1
assert ds.GetRasterBand(1).SetNoDataValue(val) == gdal.CE_None
ds = None

filename_copy = '/vsimem/test_tiff_write_uint64_nodata_filename_copy.tif'
ds = gdal.Open(filename)
assert ds.GetRasterBand(1).GetNoDataValue() == val
ds = gdal.GetDriverByName('GTiff').CreateCopy(filename_copy, ds)
ds = None

ds = gdal.Open(filename_copy)
assert ds.GetRasterBand(1).GetNoDataValue() == val
ds = None

gdal.GetDriverByName('GTiff').Delete(filename)
gdal.GetDriverByName('GTiff').Delete(filename_copy)


###############################################################################


Expand All @@ -8329,5 +8355,33 @@ def test_tiff_write_int64():
return ut.testCreateCopy()


###############################################################################


def test_tiff_write_int64_nodata():

filename = '/vsimem/test_tiff_write_int64_nodata.tif'
ds = gdal.GetDriverByName('GTiff').Create(filename, 1, 1, 1, gdal.GDT_Int64)
val = -(1 << 63)
assert ds.GetRasterBand(1).SetNoDataValue(val) == gdal.CE_None
ds = None

filename_copy = '/vsimem/test_tiff_write_int64_nodata_filename_copy.tif'
ds = gdal.Open(filename)
assert ds.GetRasterBand(1).GetNoDataValue() == val
ds = gdal.GetDriverByName('GTiff').CreateCopy(filename_copy, ds)
ds = None

ds = gdal.Open(filename_copy)
assert ds.GetRasterBand(1).GetNoDataValue() == val
ds = None

gdal.GetDriverByName('GTiff').Delete(filename)
gdal.GetDriverByName('GTiff').Delete(filename_copy)


###############################################################################


def test_tiff_write_cleanup():
gdaltest.tiff_drv = None
Loading

0 comments on commit 0f9bc86

Please sign in to comment.