2626from urllib .parse import urlparse
2727
2828import fsspec
29+ import numpy as np
2930import rasterio
3031from boto3 import Session
3132from boto3 .resources .base import ServiceResource
@@ -360,6 +361,17 @@ def _build_cube_metadata(self, ds_dict: XarrayDict) -> tuple[dict, dict]:
360361 variables = {}
361362
362363 for ds in ds_dict .values ():
364+ epsg_code = None
365+ proj_bbox = None
366+
367+ if hasattr (ds , "rio" ) and ds .rio .crs is not None :
368+ epsg_code = ds .rio .crs .to_epsg ()
369+ try :
370+ proj_bbox = list (ds .rio .bounds ())
371+ except Exception :
372+ proj_bbox = None
373+ epsg_code = epsg_code or 4326
374+
363375 # Dimensions
364376 for dim_name in ds .sizes .keys ():
365377 dim_name_str = str (dim_name )
@@ -375,20 +387,52 @@ def _build_cube_metadata(self, ds_dict: XarrayDict) -> tuple[dict, dict]:
375387
376388 dim_entry : dict [str , Any ] = {"type" : dim_type }
377389
378- # Extent or values
379- if dim_name_str in ds .coords :
380- values = ds [dim_name_str ].values
381- if values .ndim == 1 :
382- dim_entry ["values" ] = values .tolist ()
383- else :
384- dim_entry ["extent" ] = [float (values .min ()), float (values .max ())]
385-
386- if dim_type != "temporal" :
387- # Reference system
388- epsg_code = 4326 # default
389- if hasattr (ds , "rio" ) and hasattr (ds .rio , "crs" ) and ds .rio .crs is not None :
390- epsg_code = ds .rio .crs .to_epsg ()
390+ if dim_type == "spatial" :
391+ # Axis
392+ if dim_name_str in ("x" , "lon" ):
393+ dim_entry ["axis" ] = "x"
394+ elif dim_name_str in ("y" , "lat" ):
395+ dim_entry ["axis" ] = "y"
396+ elif dim_name_str == "z" :
397+ dim_entry ["axis" ] = "z"
398+
391399 dim_entry ["reference_system" ] = epsg_code
400+ dim_entry ["proj:code" ] = epsg_code
401+
402+ if proj_bbox is not None :
403+ dim_entry ["proj:bbox" ] = proj_bbox
404+
405+ # Extent + step
406+ if dim_name_str in ds .coords :
407+ values = ds [dim_name_str ].values
408+ dim_entry ["extent" ] = [
409+ float (np .nanmin (values )),
410+ float (np .nanmax (values )),
411+ ]
412+
413+ if values .ndim == 1 and values .size > 1 :
414+ diffs = np .diff (values )
415+ dim_entry ["step" ] = (
416+ float (diffs [0 ]) if np .allclose (diffs , diffs [0 ]) else None
417+ )
418+
419+ elif dim_type == "temporal" :
420+ values = ds [dim_name_str ].values
421+ dim_entry ["extent" ] = [
422+ str (values .min ()),
423+ str (values .max ()),
424+ ]
425+
426+ if values .size > 1 :
427+ diffs = np .diff (values .astype ("datetime64[ns]" ))
428+ dim_entry ["step" ] = (
429+ str (diffs [0 ]) if np .all (diffs == diffs [0 ]) else None
430+ )
431+ else :
432+ if dim_name_str in ds .coords :
433+ values = ds [dim_name_str ].values
434+ if values .ndim == 1 :
435+ dim_entry ["values" ] = values .tolist ()
392436
393437 dimensions [dim_name_str ] = dim_entry
394438
0 commit comments