diff --git a/apps/gdalbuildvrt_lib.cpp b/apps/gdalbuildvrt_lib.cpp index 4d7da0545b0a..296e376a73cf 100644 --- a/apps/gdalbuildvrt_lib.cpp +++ b/apps/gdalbuildvrt_lib.cpp @@ -436,6 +436,21 @@ static CPLString GetProjectionName(const char *pszProjection) /* AnalyseRaster() */ /************************************************************************/ +static void checkNoDataValues(const std::vector &asProperties) +{ + for (const auto &oProps : asProperties) + { + if (oProps.bHasNoData && GDALDataTypeIsInteger(oProps.dataType) && + !GDALIsValueExactAs(oProps.noDataValue, oProps.dataType)) + { + CPLError(CE_Warning, CPLE_NotSupported, + "Band data type of %s cannot represent the specified " + "NoData value of %g", + GDALGetDataTypeName(oProps.dataType), oProps.noDataValue); + } + } +} + std::string VRTBuilder::AnalyseRaster(GDALDatasetH hDS, DatasetProperty *psDatasetProperties) { @@ -965,6 +980,8 @@ std::string VRTBuilder::AnalyseRaster(GDALDatasetH hDS, } } + checkNoDataValues(asBandProperties); + return ""; } diff --git a/autotest/pymod/gdaltest.py b/autotest/pymod/gdaltest.py index 3c6625889723..77da5d307872 100755 --- a/autotest/pymod/gdaltest.py +++ b/autotest/pymod/gdaltest.py @@ -2095,3 +2095,31 @@ def vsi_open(path, mode="r"): def vrt_has_open_support(): drv = gdal.GetDriverByName("VRT") return drv is not None and drv.GetMetadataItem(gdal.DMD_OPENOPTIONLIST) is not None + + +############################################################################### +# Check that an error or warning is raised + + +@contextlib.contextmanager +def error_raised(type, match=""): + + err_levels = { + gdal.CE_Debug: "CE_Debug", + gdal.CE_Failure: "CE_Failure", + gdal.CE_Fatal: "CE_Fatal", + gdal.CE_None: "CE_None", + gdal.CE_Warning: "CE_Warning", + } + + errors = [] + + def handler(lvl, no, msg): + errors.append({"level": lvl, "number": no, "message": msg}) + + with error_handler(handler): + yield + + assert any( + [err["level"] == type and match in err["message"] for err in errors] + ), f'Did not receive an error of type {err_levels[type]} matching "{match}"' diff --git a/autotest/utilities/test_gdalbuildvrt_lib.py b/autotest/utilities/test_gdalbuildvrt_lib.py index 8645f6bbdcd9..2deb9c2fb80b 100755 --- a/autotest/utilities/test_gdalbuildvrt_lib.py +++ b/autotest/utilities/test_gdalbuildvrt_lib.py @@ -900,3 +900,26 @@ def test_gdalbuildvrt_lib_nodataMaxMaskThreshold_rgb_mask(tmp_vsimem): assert struct.unpack( "f" * 3, vrt_ds.GetRasterBand(1).ReadRaster(buf_type=gdal.GDT_Float32) ) == pytest.approx((1.0, 1.001, 2.0)) + + +############################################################################### + + +@pytest.mark.parametrize( + "dtype,nodata", + [ + (gdal.GDT_Byte, float("nan")), + (gdal.GDT_UInt16, -1), + ], +) +def test_gdalbuildvrt_lib_nodata_invalid(tmp_vsimem, dtype, nodata): + + drv = gdal.GetDriverByName("GTiff") + with drv.Create(tmp_vsimem / "in.tif", 1, 1, eType=dtype) as ds: + ds.GetRasterBand(1).Fill(1) + ds.SetGeoTransform((0, 1, 0, 1, 0, -1)) + + with gdaltest.error_raised( + gdal.CE_Warning, "cannot represent the specified NoData value" + ): + gdal.BuildVRT("", [tmp_vsimem / "in.tif"], VRTNodata=nodata)