diff --git a/autotest/gdrivers/data/s102/generate_test.py b/autotest/gdrivers/data/s102/generate_test.py index 49cacea5894d..c4d3f81a8ed3 100755 --- a/autotest/gdrivers/data/s102/generate_test.py +++ b/autotest/gdrivers/data/s102/generate_test.py @@ -66,7 +66,7 @@ def generate(filename, version, with_QualityOfSurvey=False): BathymetryCoverage_01.attrs["numPointsLongitudinal"] = np.uint32(values.shape[1]) BathymetryCoverage_01.attrs["numPointsLatitudinal"] = np.uint32(values.shape[0]) - f.create_group("Group_F") + group_f = f.create_group("Group_F") f.attrs["issueDate"] = "2023-12-31" f.attrs["geographicIdentifier"] = "Somewhere" @@ -137,6 +137,26 @@ def generate(filename, version, with_QualityOfSurvey=False): ) featureAttributeTable[...] = data + GroupFQualityOfSurvey_struct_type = np.dtype( + [ + ("code", "S16"), + ("name", "S16"), + ("uom.name", "S16"), + ("fillValue", "S16"), + ("datatype", "S16"), + ("lower", "S16"), + ("upper", "S16"), + ("closure", "S16"), + ] + ) + GroupFQualityOfSurvey = group_f.create_dataset( + "QualityOfSurvey", (1,), dtype=GroupFQualityOfSurvey_struct_type + ) + GroupFQualityOfSurvey[...] = np.array( + [("id", "", "", "0", "H5T_INTEGER", "1", "", "geSemiInterval")], + dtype=GroupFQualityOfSurvey_struct_type, + ) + generate("test_s102_v2.1", "INT.IHO.S-102.2.1") generate("test_s102_v2.2", "INT.IHO.S-102.2.2") diff --git a/autotest/gdrivers/data/s102/test_s102_v2.2_with_QualityOfSurvey.h5 b/autotest/gdrivers/data/s102/test_s102_v2.2_with_QualityOfSurvey.h5 index 8a3a199329c4..d199b6972047 100644 Binary files a/autotest/gdrivers/data/s102/test_s102_v2.2_with_QualityOfSurvey.h5 and b/autotest/gdrivers/data/s102/test_s102_v2.2_with_QualityOfSurvey.h5 differ diff --git a/autotest/gdrivers/s102.py b/autotest/gdrivers/s102.py index 696f13cb8f8f..e488a006ead3 100755 --- a/autotest/gdrivers/s102.py +++ b/autotest/gdrivers/s102.py @@ -233,6 +233,7 @@ def test_s102_QualityOfSurvey(): assert ds.GetGeoTransform() == pytest.approx((1.8, 0.4, 0.0, 48.75, 0.0, -0.5)) band = ds.GetRasterBand(1) assert band.DataType == gdal.GDT_UInt32 + assert band.GetNoDataValue() == 0 assert struct.unpack("I" * 6, band.ReadRaster()) == (1000000, 3, 2, 0, 1, 2) rat = band.GetDefaultRAT() diff --git a/frmts/hdf5/hdf5multidim.cpp b/frmts/hdf5/hdf5multidim.cpp index 6421116df070..3552592ae987 100644 --- a/frmts/hdf5/hdf5multidim.cpp +++ b/frmts/hdf5/hdf5multidim.cpp @@ -1052,6 +1052,57 @@ HDF5Array::HDF5Array(const std::string &osParentName, const std::string &osName, memcpy(m_abyNoData.data(), afNoData, m_abyNoData.size()); } + // Special case for S102 QualityOfSurvey nodata value that is typically at 0 + if (GetFullName() == + "/QualityOfSurvey/QualityOfSurvey.01/Group_001/values" && + m_dt.GetClass() == GEDTC_NUMERIC && + m_dt.GetNumericDataType() == GDT_UInt32) + { + if (auto poRootGroup = HDF5Array::GetRootGroup()) + { + if (const auto poGroupF = poRootGroup->OpenGroup("Group_F")) + { + const auto poGroupFArray = + poGroupF->OpenMDArray("QualityOfSurvey"); + if (poGroupFArray && + poGroupFArray->GetDataType().GetClass() == GEDTC_COMPOUND && + poGroupFArray->GetDataType().GetComponents().size() == 8 && + poGroupFArray->GetDataType() + .GetComponents()[0] + ->GetName() == "code" && + poGroupFArray->GetDataType() + .GetComponents()[3] + ->GetName() == "fillValue" && + poGroupFArray->GetDimensionCount() == 1 && + poGroupFArray->GetDimensions()[0]->GetSize() == 1) + { + auto poFillValue = + poGroupFArray->GetView("[\"fillValue\"]"); + if (poFillValue) + { + char *pszVal0 = nullptr; + const GUInt64 anArrayStartIdx0[] = {0}; + const size_t anCount[] = {1}; + const GInt64 anArrayStep[] = {0}; + const GPtrDiff_t anBufferStride[] = {0}; + poFillValue->Read(anArrayStartIdx0, anCount, + anArrayStep, anBufferStride, + GDALExtendedDataType::CreateString(), + &pszVal0); + if (pszVal0) + { + const uint32_t nNoData = atoi(pszVal0); + m_abyNoData.resize(m_dt.GetSize()); + memcpy(m_abyNoData.data(), &nNoData, + m_abyNoData.size()); + } + CPLFree(pszVal0); + } + } + } + } + } + // Special case for S104 nodata value that is typically -9999 if (STARTS_WITH(GetFullName().c_str(), "/WaterLevel/WaterLevel.01/") && GetFullName().find("/values") != std::string::npos &&