Skip to content

Commit 652c289

Browse files
committed
fix: inherit quicklook crs from reflectance
1 parent 66fc5ac commit 652c289

File tree

1 file changed

+67
-2
lines changed

1 file changed

+67
-2
lines changed

src/eopf_geozarr/conversion/geozarr.py

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,63 @@ def create_geozarr_dataset(
153153
return dt_geozarr
154154

155155

156+
# Borrow the reflectance CRS so quicklook datasets stay readable when the upstream quicklook metadata lacks `proj:epsg`.
157+
_QUICKLOOK_PREFIX = "/quality/l2a_quicklook/"
158+
159+
160+
def _infer_epsg(ds: xr.Dataset) -> int | None:
161+
"""Return an EPSG code advertised by the dataset, if any."""
162+
163+
if ds.rio.crs:
164+
epsg = ds.rio.crs.to_epsg()
165+
if epsg is not None:
166+
return int(epsg)
167+
168+
for source in (ds.attrs, getattr(ds, "encoding", {})):
169+
for key in ("proj:epsg", "epsg", "epsg_code"):
170+
value = source.get(key)
171+
if value is None:
172+
continue
173+
try:
174+
return int(value)
175+
except (TypeError, ValueError):
176+
continue
177+
178+
return None
179+
180+
181+
def _ensure_quicklook_crs(
182+
dt: xr.DataTree, group_name: str, ds: xr.Dataset
183+
) -> tuple[xr.Dataset, int | None]:
184+
"""Backfill CRS metadata for quicklook datasets by borrowing from reflectance."""
185+
186+
if not group_name.startswith(_QUICKLOOK_PREFIX) or ds.rio.crs:
187+
return ds, None
188+
189+
remainder = group_name[len(_QUICKLOOK_PREFIX) :].lstrip("/")
190+
resolution = remainder.split("/", 1)[0]
191+
candidates = [
192+
f"/measurements/reflectance/{resolution}",
193+
"/measurements/reflectance",
194+
]
195+
196+
for candidate in candidates:
197+
try:
198+
sibling = dt[candidate].to_dataset()
199+
except KeyError:
200+
continue
201+
202+
epsg = _infer_epsg(sibling)
203+
if epsg is None:
204+
continue
205+
206+
ds = ds.rio.write_crs(f"epsg:{epsg}")
207+
ds.attrs.setdefault("proj:epsg", epsg)
208+
return ds, epsg
209+
210+
return ds, None
211+
212+
156213
def setup_datatree_metadata_geozarr_spec_compliant(
157214
dt: xr.DataTree, groups: Iterable[str], gcp_group: str | None = None
158215
) -> dict[str, xr.Dataset]:
@@ -175,11 +232,17 @@ def setup_datatree_metadata_geozarr_spec_compliant(
175232
grid_mapping_var_name = "spatial_ref"
176233

177234
for key in groups:
178-
if not dt[key].data_vars:
235+
try:
236+
node = dt[key]
237+
except KeyError:
238+
continue
239+
240+
if not node.data_vars:
179241
continue
180242

181243
print(f"Processing group for GeoZarr compliance: {key}")
182-
ds = dt[key].to_dataset().copy()
244+
ds = node.to_dataset().copy()
245+
ds, assigned_epsg = _ensure_quicklook_crs(dt, key, ds)
183246

184247
if gcp_group is not None:
185248
ds_gcp = dt[gcp_group].to_dataset()
@@ -214,6 +277,8 @@ def setup_datatree_metadata_geozarr_spec_compliant(
214277
epsg = ds[var_name].attrs["proj:epsg"]
215278
print(f" Setting CRS for {var_name} to EPSG:{epsg}")
216279
ds = ds.rio.write_crs(f"epsg:{epsg}")
280+
elif assigned_epsg is not None:
281+
ds[var_name].attrs["proj:epsg"] = int(assigned_epsg)
217282

218283
# Add _ARRAY_DIMENSIONS to coordinate variables
219284
_add_coordinate_metadata(ds)

0 commit comments

Comments
 (0)