Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"zarr_format":3,
"node_type":"array",
"shape":[
20
],
"data_type":"float64",
"chunk_grid":{
"name":"regular",
"configuration":{
"chunk_shape":[
20
]
}
},
"chunk_key_encoding":{
"name":"default",
"configuration":{
"separator":"\/"
}
},
"fill_value":"NaN",
"codecs":[
{
"name":"bytes",
"configuration":{
"endian":"little"
}
}
],
"attributes":{
},
"dimension_names":[
"X"
]
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"zarr_format":3,
"node_type":"array",
"shape":[
20
],
"data_type":"float64",
"chunk_grid":{
"name":"regular",
"configuration":{
"chunk_shape":[
20
]
}
},
"chunk_key_encoding":{
"name":"default",
"configuration":{
"separator":"\/"
}
},
"fill_value":"NaN",
"codecs":[
{
"name":"bytes",
"configuration":{
"endian":"little"
}
}
],
"attributes":{
},
"dimension_names":[
"Y"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
k{�s������k�k��k{s��s�k{�s�s�k{{c�{���c�s���{s�k�s�{k��ssksk��{{s��{s{s{ks�ks�s�������sss{�{�{�kk��{�����s�{k�s��c{s���kŭ����c�{s���c�{��{s�{�s�{�{ks�sksc{c�ck{s�s{�s��{{�csc{�ssk��c�cs{k�ksks{�{k{�����{c�{k�cs{��{k{{{k{{{k�{{ssZk�kkkkc�{s��c{{k{ck��ksskc�k�����k{ccsc�c�s�{c�{���k�ZkskZc{sss{{�s�c����{kkks�csccks�sZ{s�����s�ZcsZcckc�ck������{�cs{Jsc{������c���΄k�{�������{k{k{����������s�kkkkksck
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"zarr_format":3,
"node_type":"array",
"shape":[
20,
20
],
"data_type":"uint8",
"chunk_grid":{
"name":"regular",
"configuration":{
"chunk_shape":[
20,
20
]
}
},
"chunk_key_encoding":{
"name":"default",
"configuration":{
"separator":"\/"
}
},
"fill_value":0,
"codecs":[
{
"name":"bytes",
"configuration":{
"endian":"little"
}
}
],
"attributes":{
"zarr_conventions_version": "0.1.0",
"zarr_conventions":
{
"f17cb550-5864-4468-aeb7-f3180cfb622f": {
"version": "0.1",
"name": "geo/proj",
"description": "",
"schema": "http://example.com/geo.proj.schema.json",
"configuration": {
"code": "EPSG:26711",
"spatial_dimensions": ["Y", "X"],
"transform": [60, 0, 440720, 0, 60, 3750120]
}
}
}
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be at the group level?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

from https://github.com/EOPF-Explorer/data-model/tree/main/attributes/geo/proj#description

Recommended usage: Store the CRS-encoding JSON object defined in this specification under the proj key in the attributes of a group that contains arrays to declare CRS metadata for those arrays. This supports the common geospatial practice of storing multiple arrays with the same coordinates in a single group. Array-level definitions are supported for override cases but are less common.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This supports the common geospatial practice of storing multiple arrays with the same coordinates in a single group

That's something we need to discuss. My initial feeling is that I'm no a huge fan of having stuff defined only at an upper level when potentially users can point GDAL to open a inner group directly or an array. So that means either such use case will be invalid, or GDAL would need to walk up the hieararchy (but where to stop?) which involves extra I/O

"dimension_names":[
"Y",
"X"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"zarr_format": 3,
"node_type": "group",
"attributes": {}
}
13 changes: 13 additions & 0 deletions autotest/gdrivers/zarr_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -5869,3 +5869,16 @@ def test_zarr_read_zarr_with_stac_proj_wkt2():
def test_zarr_identify_file_extensions(file_path):
ds = gdal.Open(file_path)
ds.GetRasterBand(1).Checksum()


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


@gdaltest.enable_exceptions()
def test_zarr_read_zarr_geo_proj_prototype():

ds = gdal.Open("data/zarr/byte_geo_proj_prototype.zarr")
assert ds.GetSpatialRef().GetAuthorityCode(None) == "26711"
assert ds.GetSpatialRef().GetDataAxisToSRSAxisMapping() == [1, 2]
assert ds.GetGeoTransform() == (440720.0, 60.0, 0.0, 3751320.0, 0.0, -60.0)
158 changes: 157 additions & 1 deletion frmts/zarr/zarr_array.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2816,7 +2816,163 @@ void ZarrArray::ParseSpecialAttributes(
// 1-dimensional array with the values.
}

if (poSRS)
bool bAxisAssigned = false;
const auto oZarrConventions = oAttributes["zarr_conventions"];
if (oZarrConventions.GetType() == CPLJSONObject::Type::Object)
{
const auto oGeoProj =
oZarrConventions["f17cb550-5864-4468-aeb7-f3180cfb622f"];
if (oGeoProj.GetType() == CPLJSONObject::Type::Object)
{
// Cf https://github.com/EOPF-Explorer/data-model/tree/main/attributes/geo/proj

const auto osVersion = oGeoProj["version"];
// TODO: implement SemVer
if (osVersion.ToString() != "0.1.0")
CPLDebug(
"ZARR",
"geo/proj version unhandled by this implementation: %s",
osVersion.ToString().c_str());

const auto oConfiguration = oGeoProj["configuration"];
if (oConfiguration.GetType() == CPLJSONObject::Type::Object)
{
const auto oCode = oConfiguration["code"];
if (oCode.GetType() == CPLJSONObject::Type::String)
{
poSRS = std::make_shared<OGRSpatialReference>();
poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
if (poSRS->SetFromUserInput(
oCode.ToString().c_str(),
OGRSpatialReference::
SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
OGRERR_NONE)
{
poSRS.reset();
}
}
else
{
const auto oWKT2 = oConfiguration["wkt2"];
if (oWKT2.GetType() == CPLJSONObject::Type::String)
{
poSRS = std::make_shared<OGRSpatialReference>();
poSRS->SetAxisMappingStrategy(
OAMS_TRADITIONAL_GIS_ORDER);
if (poSRS->importFromWkt(oWKT2.ToString().c_str()) !=
OGRERR_NONE)
{
poSRS.reset();
}
}
else
{
const auto oPROJJSON = oConfiguration["projjson"];
if (oPROJJSON.GetType() == CPLJSONObject::Type::Object)
{
poSRS = std::make_shared<OGRSpatialReference>();
poSRS->SetAxisMappingStrategy(
OAMS_TRADITIONAL_GIS_ORDER);
if (poSRS->SetFromUserInput(
oPROJJSON.ToString().c_str(),
OGRSpatialReference::
SET_FROM_USER_INPUT_LIMITATIONS_get()) !=
OGRERR_NONE)
{
poSRS.reset();
}
}
}
}

if (poSRS)
{
const auto oSpatialDimensions =
oConfiguration["spatial_dimensions"];
int iDimName1 = 0;
int iDimName2 = 0;
if (oSpatialDimensions.GetType() ==
CPLJSONObject::Type::Array &&
oSpatialDimensions.ToArray().Size() == 2)
{
const auto osName1 =
oSpatialDimensions.ToArray()[0].ToString();
const auto osName2 =
oSpatialDimensions.ToArray()[1].ToString();
int iDim = 1;
for (const auto &poDim : GetDimensions())
{
if (poDim->GetName() == osName1)
iDimName1 = iDim;
else if (poDim->GetName() == osName2)
iDimName2 = iDim;
++iDim;
}
if (iDimName1 == 0)
{
CPLError(CE_Warning, CPLE_AppDefined,
"spatial_dimensions[0] = %s is a unknown "
"Zarr dimension",
osName1.c_str());
}
if (iDimName2 == 0)
{
CPLError(CE_Warning, CPLE_AppDefined,
"spatial_dimensions[1] = %s is a unknown "
"Zarr dimension",
osName2.c_str());
}
}
else
{
int iDim = 1;
for (const auto &poDim : GetDimensions())
{
if (poDim->GetName() == "y" ||
poDim->GetName() == "Y" ||
poDim->GetName() == "lat" ||
poDim->GetName() == "latitude" ||
poDim->GetName() == "northing" ||
poDim->GetName() == "row")
{
iDimName1 = iDim;
}
else if (poDim->GetName() == "x" ||
poDim->GetName() == "X" ||
poDim->GetName() == "lon" ||
poDim->GetName() == "longitude" ||
poDim->GetName() == "easting" ||
poDim->GetName() == "col")
{
iDimName2 = iDim;
}
++iDim;
}
}
if (iDimName1 > 0 && iDimName2 > 0)
{
bAxisAssigned = true;
const int iDimY = std::min(iDimName1, iDimName2);
const int iDimX = std::max(iDimName1, iDimName2);
const auto &oMapping =
poSRS->GetDataAxisToSRSAxisMapping();
if (oMapping == std::vector<int>{2, 1} ||
oMapping == std::vector<int>{2, 1, 3})
poSRS->SetDataAxisToSRSAxisMapping({iDimY, iDimX});
else if (oMapping == std::vector<int>{1, 2} ||
oMapping == std::vector<int>{1, 2, 3})
poSRS->SetDataAxisToSRSAxisMapping({iDimX, iDimY});

SetSRS(poSRS);
}
}

// TODO deal with "bbox"? and "transform"
}
}
}

if (poSRS && !bAxisAssigned)
{
int iDimX = 0;
int iDimY = 0;
Expand Down