diff --git a/autotest/gdrivers/data/zarr/v3/gzip.zarr/gzip/c/0/0 b/autotest/gdrivers/data/zarr/v3/gzip.zarr/gzip/c/0/0 new file mode 100644 index 000000000000..07b0e69826cc Binary files /dev/null and b/autotest/gdrivers/data/zarr/v3/gzip.zarr/gzip/c/0/0 differ diff --git a/autotest/gdrivers/data/zarr/v3/gzip.zarr/gzip/zarr.json b/autotest/gdrivers/data/zarr/v3/gzip.zarr/gzip/zarr.json new file mode 100644 index 000000000000..ef5494cfe2d6 --- /dev/null +++ b/autotest/gdrivers/data/zarr/v3/gzip.zarr/gzip/zarr.json @@ -0,0 +1,44 @@ +{ + "zarr_format":3, + "node_type":"array", + "shape":[ + 1, + 2 + ], + "data_type":"uint8", + "chunk_grid":{ + "name":"regular", + "configuration":{ + "chunk_shape":[ + 1, + 2 + ] + } + }, + "chunk_key_encoding":{ + "name":"default", + "configuration":{ + "separator":"\/" + } + }, + "fill_value":0, + "codecs":[ + { + "name":"bytes", + "configuration":{ + "endian":"little" + } + }, + { + "name":"gzip", + "configuration":{ + "level":6 + } + } + ], + "attributes":{}, + "dimension_names":[ + "Y", + "X" + ] +} \ No newline at end of file diff --git a/autotest/gdrivers/data/zarr/v3/gzip.zarr/zarr.json b/autotest/gdrivers/data/zarr/v3/gzip.zarr/zarr.json new file mode 100644 index 000000000000..849253ba9b49 --- /dev/null +++ b/autotest/gdrivers/data/zarr/v3/gzip.zarr/zarr.json @@ -0,0 +1,5 @@ +{ + "zarr_format": 3, + "node_type": "group", + "attributes": {} +} diff --git a/autotest/gdrivers/zarr_driver.py b/autotest/gdrivers/zarr_driver.py index 46f9dd447274..c8a6d280d6f0 100644 --- a/autotest/gdrivers/zarr_driver.py +++ b/autotest/gdrivers/zarr_driver.py @@ -534,6 +534,33 @@ def test_zarr_read_compression_methods(datasetname, compressor): ar = rg.OpenMDArray(rg.GetMDArrayNames()[0]) assert ar assert ar.Read() == array.array("b", [1, 2]) + assert json.loads(ar.GetStructuralInfo()["COMPRESSOR"])["id"] == compressor + + +# Check reading different compression methods +@pytest.mark.parametrize( + "datasetname,compressor", + [ + ("gzip.zarr", "gzip"), + ], +) +def test_zarr_v3_read_compression_methods(datasetname, compressor): + + compressors = gdal.GetDriverByName("Zarr").GetMetadataItem("COMPRESSORS") + filename = "data/zarr/v3/" + datasetname + + if compressor not in compressors: + with gdal.quiet_errors(): + ds = gdal.OpenEx(filename, gdal.OF_MULTIDIM_RASTER) + assert ds is None + else: + ds = gdal.OpenEx(filename, gdal.OF_MULTIDIM_RASTER) + rg = ds.GetRootGroup() + assert rg + ar = rg.OpenMDArray(rg.GetMDArrayNames()[0]) + assert ar + assert ar.Read() == array.array("b", [1, 2]) + assert json.loads(ar.GetStructuralInfo()["COMPRESSOR"])["name"] == compressor def test_zarr_read_shuffle_filter(): @@ -545,6 +572,9 @@ def test_zarr_read_shuffle_filter(): ar = rg.OpenMDArray(rg.GetMDArrayNames()[0]) assert ar assert ar.Read() == array.array("h", [1, 2]) + assert json.loads(ar.GetStructuralInfo()["FILTERS"]) == [ + {"elementsize": 2, "id": "shuffle"} + ] def test_zarr_read_shuffle_filter_update(tmp_path): diff --git a/frmts/zarr/zarr.h b/frmts/zarr/zarr.h index 9566da96cc6a..4cac88a12f3f 100644 --- a/frmts/zarr/zarr.h +++ b/frmts/zarr/zarr.h @@ -752,6 +752,7 @@ class ZarrArray CPL_NON_FINAL : public GDALPamMDArray const GDALExtendedDataType m_oType; const std::vector m_aoDtypeElts; const std::vector m_anBlockSize; + CPLStringList m_aosStructuralInfo{}; CPLJSONObject m_dtype{}; GByte *m_pabyNoData = nullptr; std::string m_osDimSeparator{"."}; @@ -903,6 +904,11 @@ class ZarrArray CPL_NON_FINAL : public GDALPamMDArray return m_anBlockSize; } + CSLConstList GetStructuralInfo() const override + { + return m_aosStructuralInfo.List(); + } + const void *GetRawNoDataValue() const override { return m_pabyNoData; @@ -1038,6 +1044,11 @@ class ZarrArray CPL_NON_FINAL : public GDALPamMDArray bool CacheTilePresence(); + void SetStructuralInfo(const char *pszKey, const char *pszValue) + { + m_aosStructuralInfo.SetNameValue(pszKey, pszValue); + } + static void DecodeSourceElt(const std::vector &elts, const GByte *pSrc, GByte *pDst); @@ -1099,10 +1110,7 @@ class ZarrV2Array final : public ZarrArray const std::vector &aoDtypeElts, const std::vector &anBlockSize, bool bFortranOrder); - void SetCompressorJson(const CPLJSONObject &oCompressor) - { - m_oCompressorJSon = oCompressor; - } + void SetCompressorJson(const CPLJSONObject &oCompressor); void SetCompressorDecompressor(const std::string &osDecompressorId, const CPLCompressor *psComp, @@ -1113,10 +1121,7 @@ class ZarrV2Array final : public ZarrArray m_psDecompressor = psDecomp; } - void SetFilters(const CPLJSONArray &oFiltersArray) - { - m_oFiltersArray = oFiltersArray; - } + void SetFilters(const CPLJSONArray &oFiltersArray); void Flush() override; diff --git a/frmts/zarr/zarr_v2_array.cpp b/frmts/zarr/zarr_v2_array.cpp index 20f14e9010a5..3732c0a74841 100644 --- a/frmts/zarr/zarr_v2_array.cpp +++ b/frmts/zarr/zarr_v2_array.cpp @@ -1964,3 +1964,27 @@ ZarrV2Group::LoadArray(const std::string &osArrayName, return poArray; } + +/************************************************************************/ +/* ZarrV2Group::SetCompressorJson() */ +/************************************************************************/ + +void ZarrV2Array::SetCompressorJson(const CPLJSONObject &oCompressor) +{ + m_oCompressorJSon = oCompressor; + if (oCompressor.GetType() != CPLJSONObject::Type::Null) + m_aosStructuralInfo.SetNameValue("COMPRESSOR", + oCompressor.ToString().c_str()); +} + +/************************************************************************/ +/* ZarrV2Group::SetFilters() */ +/************************************************************************/ + +void ZarrV2Array::SetFilters(const CPLJSONArray &oFiltersArray) +{ + m_oFiltersArray = oFiltersArray; + if (oFiltersArray.Size() > 0) + m_aosStructuralInfo.SetNameValue("FILTERS", + oFiltersArray.ToString().c_str()); +} diff --git a/frmts/zarr/zarr_v3_array.cpp b/frmts/zarr/zarr_v3_array.cpp index c63e9bb31667..ccce3e5b0a6c 100644 --- a/frmts/zarr/zarr_v3_array.cpp +++ b/frmts/zarr/zarr_v3_array.cpp @@ -1585,6 +1585,12 @@ ZarrV3Group::LoadArray(const std::string &osArrayName, poArray->ParseSpecialAttributes(m_pSelf.lock(), oAttributes); poArray->SetAttributes(oAttributes); poArray->SetDtype(oDtype); + if (oCodecs.Size() > 0 && + oCodecs[oCodecs.Size() - 1].GetString("name") != "bytes") + { + poArray->SetStructuralInfo( + "COMPRESSOR", oCodecs[oCodecs.Size() - 1].ToString().c_str()); + } if (poCodecs) poArray->SetCodecs(std::move(poCodecs)); RegisterArray(poArray); diff --git a/frmts/zarr/zarr_v3_group.cpp b/frmts/zarr/zarr_v3_group.cpp index e149df5b84dc..b8837273750f 100644 --- a/frmts/zarr/zarr_v3_group.cpp +++ b/frmts/zarr/zarr_v3_group.cpp @@ -706,6 +706,12 @@ std::shared_ptr ZarrV3Group::CreateMDArray( poArray->SetFilename(osFilename); poArray->SetDimSeparator(pszDimSeparator); poArray->SetDtype(dtype); + if (oCodecs.Size() > 0 && + oCodecs[oCodecs.Size() - 1].GetString("name") != "bytes") + { + poArray->SetStructuralInfo( + "COMPRESSOR", oCodecs[oCodecs.Size() - 1].ToString().c_str()); + } if (poCodecs) poArray->SetCodecs(std::move(poCodecs)); poArray->SetUpdatable(true);