@@ -6971,3 +6971,247 @@ def test_zarr_read_spatial_geotransform():
69716971 assert ds .GetGeoTransform () == (450000 , 10 , 0.1 , 5000000 , 0.15 , - 10 )
69726972 assert ds .GetSpatialRef () is None
69736973 assert ds .GetMetadata () == {"AREA_OR_POINT" : "Area" }
6974+
6975+
6976+ ###############################################################################
6977+ #
6978+
6979+
6980+ @gdaltest .enable_exceptions ()
6981+ def test_zarr_write_spatial_geotransform (tmp_vsimem ):
6982+
6983+ src_ds = gdal .Open ("data/byte.tif" )
6984+ gdal .GetDriverByName ("Zarr" ).CreateCopy (
6985+ tmp_vsimem / "out.zarr" ,
6986+ src_ds ,
6987+ options = {"FORMAT" : "ZARR_V3" , "GEOREFERENCING_CONVENTION" : "SPATIAL_PROJ" },
6988+ )
6989+
6990+ with gdal .VSIFile (tmp_vsimem / "out.zarr" / "zarr.json" , "rb" ) as f :
6991+ j = json .loads (f .read ())
6992+
6993+ assert j == {
6994+ "zarr_format" : 3 ,
6995+ "node_type" : "group" ,
6996+ "attributes" : {},
6997+ "consolidated_metadata" : {
6998+ "kind" : "inline" ,
6999+ "must_understand" : False ,
7000+ "metadata" : {
7001+ "X" : {
7002+ "zarr_format" : 3 ,
7003+ "node_type" : "array" ,
7004+ "shape" : [20 ],
7005+ "data_type" : "float64" ,
7006+ "chunk_grid" : {
7007+ "name" : "regular" ,
7008+ "configuration" : {"chunk_shape" : [20 ]},
7009+ },
7010+ "chunk_key_encoding" : {
7011+ "name" : "default" ,
7012+ "configuration" : {"separator" : "/" },
7013+ },
7014+ "fill_value" : "NaN" ,
7015+ "codecs" : [
7016+ {"name" : "bytes" , "configuration" : {"endian" : "little" }}
7017+ ],
7018+ "attributes" : {},
7019+ "dimension_names" : ["X" ],
7020+ },
7021+ "Y" : {
7022+ "zarr_format" : 3 ,
7023+ "node_type" : "array" ,
7024+ "shape" : [20 ],
7025+ "data_type" : "float64" ,
7026+ "chunk_grid" : {
7027+ "name" : "regular" ,
7028+ "configuration" : {"chunk_shape" : [20 ]},
7029+ },
7030+ "chunk_key_encoding" : {
7031+ "name" : "default" ,
7032+ "configuration" : {"separator" : "/" },
7033+ },
7034+ "fill_value" : "NaN" ,
7035+ "codecs" : [
7036+ {"name" : "bytes" , "configuration" : {"endian" : "little" }}
7037+ ],
7038+ "attributes" : {},
7039+ "dimension_names" : ["Y" ],
7040+ },
7041+ "out" : {
7042+ "zarr_format" : 3 ,
7043+ "node_type" : "array" ,
7044+ "shape" : [20 , 20 ],
7045+ "data_type" : "uint8" ,
7046+ "chunk_grid" : {
7047+ "name" : "regular" ,
7048+ "configuration" : {"chunk_shape" : [20 , 20 ]},
7049+ },
7050+ "chunk_key_encoding" : {
7051+ "name" : "default" ,
7052+ "configuration" : {"separator" : "/" },
7053+ },
7054+ "fill_value" : None ,
7055+ "codecs" : [
7056+ {"name" : "bytes" , "configuration" : {"endian" : "little" }}
7057+ ],
7058+ "attributes" : {
7059+ "COLOR_INTERPRETATION" : "Gray" ,
7060+ "proj:code" : "EPSG:26711" ,
7061+ "spatial:bbox" : [
7062+ 440720.0 ,
7063+ 3750120.0 ,
7064+ 441920.0 ,
7065+ 3751320.0 ,
7066+ ],
7067+ "spatial:transform_type" : "affine" ,
7068+ "spatial:transform" : [
7069+ 60.0 ,
7070+ 0.0 ,
7071+ 440720.0 ,
7072+ 0.0 ,
7073+ - 60.0 ,
7074+ 3751320.0 ,
7075+ ],
7076+ "spatial:registration" : "pixel" ,
7077+ "spatial:dimensions" : ["Y" , "X" ],
7078+ "zarr_conventions" : [
7079+ {
7080+ "schema_url" : "https://raw.githubusercontent.com/zarr-experimental/geo-proj/refs/tags/v1/schema.json" ,
7081+ "spec_url" : "https://github.com/zarr-experimental/geo-proj/blob/v1/README.md" ,
7082+ "uuid" : "f17cb550-5864-4468-aeb7-f3180cfb622f" ,
7083+ "name" : "proj:" ,
7084+ "description" : "Coordinate reference system information for geospatial data" ,
7085+ },
7086+ {
7087+ "schema_url" : "https://raw.githubusercontent.com/zarr-conventions/spatial/refs/tags/v1/schema.json" ,
7088+ "spec_url" : "https://github.com/zarr-conventions/spatial/blob/v1/README.md" ,
7089+ "uuid" : "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4" ,
7090+ "name" : "spatial:" ,
7091+ "description" : "Spatial coordinate information" ,
7092+ },
7093+ ],
7094+ },
7095+ "dimension_names" : ["Y" , "X" ],
7096+ },
7097+ },
7098+ },
7099+ }
7100+
7101+ # Remove CF-like X and Y coordinate variables, to be sure we read from
7102+ # spatial:transform
7103+ gdal .RmdirRecursive (tmp_vsimem / "out.zarr" / "X" )
7104+ gdal .RmdirRecursive (tmp_vsimem / "out.zarr" / "Y" )
7105+
7106+ ds = gdal .Open (tmp_vsimem / "out.zarr" )
7107+ assert ds .GetSpatialRef ().IsSame (src_ds .GetSpatialRef ())
7108+ assert ds .GetGeoTransform () == src_ds .GetGeoTransform ()
7109+ assert ds .GetRasterBand (1 ).Checksum () == src_ds .GetRasterBand (1 ).Checksum ()
7110+
7111+
7112+ ###############################################################################
7113+ #
7114+
7115+
7116+ @gdaltest .enable_exceptions ()
7117+ def test_zarr_write_spatial_geotransform_no_epsg_code_rotated_gt_and_pixel_center (
7118+ tmp_vsimem ,
7119+ ):
7120+
7121+ src_ds = gdal .GetDriverByName ("MEM" ).Create ("" , 2 , 3 )
7122+ src_ds .SetGeoTransform ([1 , 2 , 3 , 4 , 5 , 6 ])
7123+ src_ds .SetSpatialRef (
7124+ osr .SpatialReference ("+proj=longlat +ellps=GRS80 +towgs84=0,0,0" )
7125+ )
7126+ src_ds .SetMetadataItem ("AREA_OR_POINT" , "Point" )
7127+
7128+ gdal .GetDriverByName ("Zarr" ).CreateCopy (
7129+ tmp_vsimem / "out.zarr" ,
7130+ src_ds ,
7131+ options = {"FORMAT" : "ZARR_V3" , "GEOREFERENCING_CONVENTION" : "SPATIAL_PROJ" },
7132+ )
7133+
7134+ with gdal .VSIFile (tmp_vsimem / "out.zarr" / "zarr.json" , "rb" ) as f :
7135+ j = json .loads (f .read ())
7136+
7137+ assert j ["consolidated_metadata" ]["metadata" ]["out" ]["attributes" ][
7138+ "proj:wkt2"
7139+ ].startswith ("BOUNDCRS" )
7140+ assert (
7141+ j ["consolidated_metadata" ]["metadata" ]["out" ]["attributes" ]["proj:projjson" ][
7142+ "type"
7143+ ]
7144+ == "BoundCRS"
7145+ )
7146+
7147+ # Set those 2 elements to dummy value for comparison, as their content might be PROJ dependent
7148+ j ["consolidated_metadata" ]["metadata" ]["out" ]["attributes" ][
7149+ "proj:wkt2"
7150+ ] = "redacted"
7151+ j ["consolidated_metadata" ]["metadata" ]["out" ]["attributes" ][
7152+ "proj:projjson"
7153+ ] = "redacted"
7154+
7155+ assert j == {
7156+ "zarr_format" : 3 ,
7157+ "node_type" : "group" ,
7158+ "attributes" : {},
7159+ "consolidated_metadata" : {
7160+ "kind" : "inline" ,
7161+ "must_understand" : False ,
7162+ "metadata" : {
7163+ "out" : {
7164+ "zarr_format" : 3 ,
7165+ "node_type" : "array" ,
7166+ "shape" : [3 , 2 ],
7167+ "data_type" : "uint8" ,
7168+ "chunk_grid" : {
7169+ "name" : "regular" ,
7170+ "configuration" : {"chunk_shape" : [3 , 2 ]},
7171+ },
7172+ "chunk_key_encoding" : {
7173+ "name" : "default" ,
7174+ "configuration" : {"separator" : "/" },
7175+ },
7176+ "fill_value" : None ,
7177+ "codecs" : [
7178+ {"name" : "bytes" , "configuration" : {"endian" : "little" }}
7179+ ],
7180+ "attributes" : {
7181+ "proj:wkt2" : "redacted" ,
7182+ "proj:projjson" : "redacted" ,
7183+ "spatial:bbox" : [3.5 , 9.5 , 11.5 , 26.5 ],
7184+ "spatial:transform_type" : "affine" ,
7185+ "spatial:transform" : [2.0 , 3.0 , 3.5 , 5.0 , 6.0 , 9.5 ],
7186+ "spatial:dimensions" : ["Y" , "X" ],
7187+ "spatial:registration" : "node" ,
7188+ "zarr_conventions" : [
7189+ {
7190+ "schema_url" : "https://raw.githubusercontent.com/zarr-experimental/geo-proj/refs/tags/v1/schema.json" ,
7191+ "spec_url" : "https://github.com/zarr-experimental/geo-proj/blob/v1/README.md" ,
7192+ "uuid" : "f17cb550-5864-4468-aeb7-f3180cfb622f" ,
7193+ "name" : "proj:" ,
7194+ "description" : "Coordinate reference system information for geospatial data" ,
7195+ },
7196+ {
7197+ "schema_url" : "https://raw.githubusercontent.com/zarr-conventions/spatial/refs/tags/v1/schema.json" ,
7198+ "spec_url" : "https://github.com/zarr-conventions/spatial/blob/v1/README.md" ,
7199+ "uuid" : "689b58e2-cf7b-45e0-9fff-9cfc0883d6b4" ,
7200+ "name" : "spatial:" ,
7201+ "description" : "Spatial coordinate information" ,
7202+ },
7203+ ],
7204+ },
7205+ "dimension_names" : ["Y" , "X" ],
7206+ },
7207+ },
7208+ },
7209+ }
7210+
7211+ ds = gdal .Open (tmp_vsimem / "out.zarr" )
7212+ assert ds .GetSpatialRef ().IsSame (src_ds .GetSpatialRef ())
7213+ assert ds .GetGeoTransform () == src_ds .GetGeoTransform ()
7214+ assert ds .GetMetadataItem ("AREA_OR_POINT" ) == src_ds .GetMetadataItem (
7215+ "AREA_OR_POINT"
7216+ )
7217+ assert ds .GetRasterBand (1 ).Checksum () == src_ds .GetRasterBand (1 ).Checksum ()
0 commit comments