Skip to content

Commit 94e206c

Browse files
committed
gdal_translate: only copy nodata value if it can be exactly represented
1 parent a2aa93b commit 94e206c

File tree

2 files changed

+47
-153
lines changed

2 files changed

+47
-153
lines changed

apps/gdal_translate_lib.cpp

Lines changed: 41 additions & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -586,71 +586,6 @@ EditISIS3MetadataForBandChange(const char *pszJSON, int nSrcBandCount,
586586
return oRoot.Format(CPLJSONObject::PrettyFormat::Pretty);
587587
}
588588

589-
/************************************************************************/
590-
/* AdjustNoDataValue() */
591-
/************************************************************************/
592-
593-
static double AdjustNoDataValue(double dfInputNoDataValue,
594-
GDALRasterBand *poBand,
595-
const GDALTranslateOptions *psOptions)
596-
{
597-
bool bSignedByte = false;
598-
const char *pszPixelType =
599-
psOptions->aosCreateOptions.FetchNameValue("PIXELTYPE");
600-
if (pszPixelType == nullptr && poBand->GetRasterDataType() == GDT_Byte)
601-
{
602-
poBand->EnablePixelTypeSignedByteWarning(false);
603-
pszPixelType = poBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
604-
poBand->EnablePixelTypeSignedByteWarning(true);
605-
}
606-
if (pszPixelType != nullptr && EQUAL(pszPixelType, "SIGNEDBYTE"))
607-
bSignedByte = true;
608-
int bClamped = FALSE;
609-
int bRounded = FALSE;
610-
double dfVal = 0.0;
611-
const GDALDataType eBandType = poBand->GetRasterDataType();
612-
if (bSignedByte)
613-
{
614-
if (dfInputNoDataValue < -128.0)
615-
{
616-
dfVal = -128.0;
617-
bClamped = TRUE;
618-
}
619-
else if (dfInputNoDataValue > 127.0)
620-
{
621-
dfVal = 127.0;
622-
bClamped = TRUE;
623-
}
624-
else
625-
{
626-
dfVal = static_cast<int>(floor(dfInputNoDataValue + 0.5));
627-
if (dfVal != dfInputNoDataValue)
628-
bRounded = TRUE;
629-
}
630-
}
631-
else
632-
{
633-
dfVal = GDALAdjustValueToDataType(eBandType, dfInputNoDataValue,
634-
&bClamped, &bRounded);
635-
}
636-
637-
if (bClamped)
638-
{
639-
CPLError(CE_Warning, CPLE_AppDefined,
640-
"for band %d, nodata value has been clamped "
641-
"to %.0f, the original value being out of range.",
642-
poBand->GetBand(), dfVal);
643-
}
644-
else if (bRounded)
645-
{
646-
CPLError(CE_Warning, CPLE_AppDefined,
647-
"for band %d, nodata value has been rounded "
648-
"to %.0f, %s being an integer datatype.",
649-
poBand->GetBand(), dfVal, GDALGetDataTypeName(eBandType));
650-
}
651-
return dfVal;
652-
}
653-
654589
/************************************************************************/
655590
/* GDALTranslate() */
656591
/************************************************************************/
@@ -2508,87 +2443,46 @@ GDALDatasetH GDALTranslate(const char *pszDest, GDALDatasetH hSrcDataset,
25082443
*/
25092444
if (psOptions->bSetNoData)
25102445
{
2511-
if (poVRTBand->GetRasterDataType() == GDT_Int64)
2512-
{
2513-
if (psOptions->osNoData.find('.') != std::string::npos ||
2514-
CPLGetValueType(psOptions->osNoData.c_str()) ==
2515-
CPL_VALUE_STRING)
2516-
{
2517-
const double dfNoData =
2518-
CPLAtof(psOptions->osNoData.c_str());
2519-
if (GDALIsValueExactAs<int64_t>(dfNoData))
2520-
{
2521-
poVRTBand->SetNoDataValueAsInt64(
2522-
static_cast<int64_t>(dfNoData));
2523-
}
2524-
else
2525-
{
2526-
CPLError(CE_Warning, CPLE_AppDefined,
2527-
"Cannot set nodata value %s on a Int64 band",
2528-
psOptions->osNoData.c_str());
2529-
}
2530-
}
2531-
else
2532-
{
2533-
errno = 0;
2534-
const auto val =
2535-
std::strtoll(psOptions->osNoData.c_str(), nullptr, 10);
2536-
if (errno == 0)
2537-
{
2538-
poVRTBand->SetNoDataValueAsInt64(
2539-
static_cast<int64_t>(val));
2540-
}
2541-
else
2542-
{
2543-
CPLError(CE_Warning, CPLE_AppDefined,
2544-
"Cannot set nodata value %s on a Int64 band",
2545-
psOptions->osNoData.c_str());
2546-
}
2547-
}
2446+
const char *pszPixelType =
2447+
psOptions->aosCreateOptions.FetchNameValue("PIXELTYPE");
2448+
if (pszPixelType == nullptr &&
2449+
poVRTBand->GetRasterDataType() == GDT_Byte)
2450+
{
2451+
poVRTBand->EnablePixelTypeSignedByteWarning(false);
2452+
pszPixelType =
2453+
poVRTBand->GetMetadataItem("PIXELTYPE", "IMAGE_STRUCTURE");
2454+
poVRTBand->EnablePixelTypeSignedByteWarning(true);
25482455
}
2549-
else if (poVRTBand->GetRasterDataType() == GDT_UInt64)
2456+
2457+
bool bCannotBeExactlyRepresented = false;
2458+
2459+
if (pszPixelType != nullptr && EQUAL(pszPixelType, "SIGNEDBYTE"))
25502460
{
2551-
if (psOptions->osNoData.find('.') != std::string::npos ||
2552-
CPLGetValueType(psOptions->osNoData.c_str()) ==
2553-
CPL_VALUE_STRING)
2461+
char *endptr = nullptr;
2462+
const double dfVal =
2463+
CPLStrtod(psOptions->osNoData.c_str(), &endptr);
2464+
if (endptr == psOptions->osNoData.c_str() +
2465+
psOptions->osNoData.size() &&
2466+
dfVal >= -128.0 && dfVal <= 127.0 &&
2467+
static_cast<int8_t>(dfVal) == dfVal)
25542468
{
2555-
const double dfNoData =
2556-
CPLAtof(psOptions->osNoData.c_str());
2557-
if (GDALIsValueExactAs<uint64_t>(dfNoData))
2558-
{
2559-
poVRTBand->SetNoDataValueAsUInt64(
2560-
static_cast<uint64_t>(dfNoData));
2561-
}
2562-
else
2563-
{
2564-
CPLError(CE_Warning, CPLE_AppDefined,
2565-
"Cannot set nodata value %s on a UInt64 band",
2566-
psOptions->osNoData.c_str());
2567-
}
2469+
poVRTBand->SetNoDataValue(dfVal);
25682470
}
25692471
else
25702472
{
2571-
errno = 0;
2572-
const auto val =
2573-
std::strtoull(psOptions->osNoData.c_str(), nullptr, 10);
2574-
if (errno == 0)
2575-
{
2576-
poVRTBand->SetNoDataValueAsUInt64(
2577-
static_cast<uint64_t>(val));
2578-
}
2579-
else
2580-
{
2581-
CPLError(CE_Warning, CPLE_AppDefined,
2582-
"Cannot set nodata value %s on a UInt64 band",
2583-
psOptions->osNoData.c_str());
2584-
}
2473+
bCannotBeExactlyRepresented = true;
25852474
}
25862475
}
25872476
else
25882477
{
2589-
const double dfVal = AdjustNoDataValue(
2590-
CPLAtof(psOptions->osNoData.c_str()), poVRTBand, psOptions);
2591-
poVRTBand->SetNoDataValue(dfVal);
2478+
poVRTBand->SetNoDataValueAsString(psOptions->osNoData.c_str(),
2479+
&bCannotBeExactlyRepresented);
2480+
}
2481+
if (bCannotBeExactlyRepresented)
2482+
{
2483+
CPLError(CE_Warning, CPLE_AppDefined,
2484+
"Nodata value was not set to output band, "
2485+
"as it cannot be represented on its data type.");
25922486
}
25932487
}
25942488

@@ -2770,7 +2664,7 @@ static void AttachDomainMetadata(GDALDatasetH hDS,
27702664
static void CopyBandInfo(GDALRasterBand *poSrcBand, GDALRasterBand *poDstBand,
27712665
int bCanCopyStatsMetadata, int bCopyScale,
27722666
int bCopyNoData, bool bCopyRAT,
2773-
const GDALTranslateOptions *psOptions)
2667+
const GDALTranslateOptions * /*psOptions*/)
27742668

27752669
{
27762670

@@ -2821,24 +2715,20 @@ static void CopyBandInfo(GDALRasterBand *poSrcBand, GDALRasterBand *poDstBand,
28212715

28222716
if (bCopyNoData)
28232717
{
2824-
if (poSrcBand->GetRasterDataType() != GDT_Int64 &&
2825-
poSrcBand->GetRasterDataType() != GDT_UInt64 &&
2826-
poDstBand->GetRasterDataType() != GDT_Int64 &&
2827-
poDstBand->GetRasterDataType() != GDT_UInt64)
2718+
int bSuccess = FALSE;
2719+
CPL_IGNORE_RET_VAL(poSrcBand->GetNoDataValue(&bSuccess));
2720+
if (bSuccess)
28282721
{
2829-
int bSuccess = FALSE;
2830-
double dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
2831-
if (bSuccess)
2722+
bool bCannotBeExactlyRepresented = false;
2723+
if (!GDALCopyNoDataValue(poDstBand, poSrcBand,
2724+
&bCannotBeExactlyRepresented) &&
2725+
bCannotBeExactlyRepresented)
28322726
{
2833-
const double dfVal =
2834-
AdjustNoDataValue(dfNoData, poDstBand, psOptions);
2835-
poDstBand->SetNoDataValue(dfVal);
2727+
CPLError(CE_Warning, CPLE_AppDefined,
2728+
"Source nodata value was not copied to output band, "
2729+
"as it cannot be represented on its data type.");
28362730
}
28372731
}
2838-
else
2839-
{
2840-
GDALCopyNoDataValue(poDstBand, poSrcBand);
2841-
}
28422732
}
28432733

28442734
if (bCopyScale)

autotest/utilities/test_gdal_translate_lib.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,9 @@ def test_gdal_translate_lib_nodata_uint64():
279279
@pytest.mark.parametrize("nodata", (1 << 65, 3.2))
280280
def test_gdal_translate_lib_nodata_uint64_invalid(nodata):
281281

282-
with gdaltest.error_raised(gdal.CE_Warning, "Cannot set nodata value"):
282+
with gdaltest.error_raised(
283+
gdal.CE_Warning, "Nodata value was not set to output band"
284+
):
283285
ds = gdal.Translate(
284286
"",
285287
"../gcore/data/byte.tif",
@@ -310,7 +312,9 @@ def test_gdal_translate_lib_nodata_int64():
310312
@pytest.mark.parametrize("nodata", (1 << 65, 3.2))
311313
def test_gdal_translate_lib_nodata_int64_invalid(nodata):
312314

313-
with gdaltest.error_raised(gdal.CE_Warning, "Cannot set nodata value"):
315+
with gdaltest.error_raised(
316+
gdal.CE_Warning, "Nodata value was not set to output band"
317+
):
314318
ds = gdal.Translate(
315319
"",
316320
"../gcore/data/byte.tif",

0 commit comments

Comments
 (0)