@@ -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+
156213def 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