From ef02a614e38a8238bdbe330b68857162a324ab7d Mon Sep 17 00:00:00 2001 From: konstntokas Date: Mon, 20 Jul 2026 17:12:34 +0200 Subject: [PATCH 1/5] backup --- xarray_eopf/amodes/sentinel1.py | 29 ++++++++++++++++++++++------- xarray_eopf/amodes/sentinel3.py | 9 ++++++++- xarray_eopf/backend.py | 3 ++- 3 files changed, 32 insertions(+), 9 deletions(-) diff --git a/xarray_eopf/amodes/sentinel1.py b/xarray_eopf/amodes/sentinel1.py index d769043..a8fb89f 100644 --- a/xarray_eopf/amodes/sentinel1.py +++ b/xarray_eopf/amodes/sentinel1.py @@ -37,6 +37,7 @@ from xarray_eopf.amode import AnalysisMode, AnalysisModeRegistry from xarray_eopf.source import get_source_path from xarray_eopf.utils import NameFilter, assert_arg_has_length, assert_arg_is_instance +from xarray_eopf.constants import FloatInt _SPEED_OF_LIGHT = 299_792_458.0 _S_TO_NS = 10**9 @@ -111,7 +112,14 @@ def get_applicable_params(self, **kwargs) -> dict[str, Any]: resolution = kwargs.get("resolution") if resolution is not None: - assert_arg_is_instance(resolution, "resolution", (float, int)) + assert_arg_is_instance(resolution, "resolution", (float, int, tuple)) + if isinstance(resolution, tuple): + assert_arg_has_length(resolution, "resolution", 2) + if not all(isinstance(v, (float, int)) for v in resolution): + raise TypeError( + "resolution argument must contain exactly " + "two float or int values." + ) params.update(resolution=resolution) bbox = kwargs.get("bbox") @@ -171,7 +179,7 @@ def convert_datatree( datatree: xr.DataTree, includes: str | Iterable[str] | None = None, excludes: str | Iterable[str] | None = None, - resolution: float = None, + resolution: FloatInt | tuple[FloatInt, FloatInt] | None = None, bbox: Sequence[float | int] | None = None, crs: pyproj.CRS | None = None, interp_methods: Literal["nearest", "bilinear"] = "bilinear", @@ -613,7 +621,14 @@ def get_applicable_params(self, **kwargs) -> dict[str, Any]: resolution = kwargs.get("resolution") if resolution is not None: - assert_arg_is_instance(resolution, "resolution", (float, int)) + assert_arg_is_instance(resolution, "resolution", (float, int, tuple)) + if isinstance(resolution, tuple): + assert_arg_has_length(resolution, "resolution", 2) + if not all(isinstance(v, (float, int)) for v in resolution): + raise TypeError( + "resolution argument must contain exactly " + "two float or int values." + ) params.update(resolution=resolution) bbox = kwargs.get("bbox") @@ -648,7 +663,7 @@ def convert_datatree( datatree: xr.DataTree, includes: str | Iterable[str] | None = None, excludes: str | Iterable[str] | None = None, - resolution: float = None, + resolution: FloatInt | tuple[FloatInt, FloatInt] | None = None, bbox: Sequence[float | int] | None = None, crs: pyproj.CRS | None = None, interp_methods: SpatialInterpMethods | None = None, @@ -708,12 +723,12 @@ def _apply_valid_range(array, *, dtype=None, fill_value=None): bbox = source_gm.xy_bbox if resolution is None: if crs and not crs.is_geographic: - center_lat = ( + center = ( (source_gm.xy_bbox[0] + source_gm.xy_bbox[2]) / 2, (source_gm.xy_bbox[1] + source_gm.xy_bbox[3]) / 2, ) resolution = transform_resolution( - center_lat, source_gm.xy_res, source_gm.crs, crs + center, source_gm.xy_res, source_gm.crs, crs ) else: resolution = source_gm.xy_res @@ -753,7 +768,7 @@ def _cleanup_registered_cache_uris() -> None: def get_dem( bbox: Sequence[float | int], - resolution: float | None = None, + resolution: FloatInt | tuple[FloatInt, FloatInt] | None = None, crs: pyproj.CRS | None = None, ): """Fetch and prepare a DEM for the given area of interest. diff --git a/xarray_eopf/amodes/sentinel3.py b/xarray_eopf/amodes/sentinel3.py index 26ffd63..e3d2fe6 100644 --- a/xarray_eopf/amodes/sentinel3.py +++ b/xarray_eopf/amodes/sentinel3.py @@ -56,7 +56,14 @@ def get_applicable_params(self, **kwargs) -> dict[str, Any]: resolution = kwargs.get("resolution") if resolution is not None: - assert_arg_is_instance(resolution, "resolution", (int, float)) + assert_arg_is_instance(resolution, "resolution", (float, int, tuple)) + if isinstance(resolution, tuple): + assert_arg_has_length(resolution, "resolution", 2) + if not all(isinstance(v, (float, int)) for v in resolution): + raise TypeError( + "resolution argument must contain exactly " + "two float or int values." + ) params.update(resolution=resolution) bbox = kwargs.get("bbox") diff --git a/xarray_eopf/backend.py b/xarray_eopf/backend.py index 93ed22e..217206b 100644 --- a/xarray_eopf/backend.py +++ b/xarray_eopf/backend.py @@ -21,6 +21,7 @@ OP_MODES, OpMode, Sen1InterpMethods, + FloatInt, ) from .filter import filter_dataset from .flatten import flatten_datatree, flatten_datatree_as_dict @@ -122,7 +123,7 @@ def open_dataset( variables: str | Iterable[str] | None = None, # params for op_mode=analysis product_type: str | None = None, - resolution: int | float | None = None, + resolution: FloatInt | tuple[FloatInt, FloatInt] | None = None, bbox: Sequence[int | float] | None = None, crs: pyproj.CRS | str | None = None, interp_methods: SpatialInterpMethods | Sen1InterpMethods | None = None, From 20e8002e5aaeeaf74c60deeb8723327e5232dd94 Mon Sep 17 00:00:00 2001 From: konstntokas Date: Tue, 21 Jul 2026 11:42:55 +0200 Subject: [PATCH 2/5] backup --- xarray_eopf/amodes/sentinel1.py | 64 +++++++++++++++++---------------- 1 file changed, 33 insertions(+), 31 deletions(-) diff --git a/xarray_eopf/amodes/sentinel1.py b/xarray_eopf/amodes/sentinel1.py index a8fb89f..6c8676f 100644 --- a/xarray_eopf/amodes/sentinel1.py +++ b/xarray_eopf/amodes/sentinel1.py @@ -46,6 +46,7 @@ _CRS_WGS84 = pyproj.CRS.from_string("EPSG:4326") _DEM_CHUNKSIZE = dict(lat=1800, lon=1800) _CHUNKSIZE = (2048, 2048) +_SLC_SWATHS = ["IW1", "IW2", "IW3"] _REGISTERED_CACHE_URIS = [] @@ -367,26 +368,28 @@ def _open_data( ) -> xr.Dataset: """Load, calibrate, and merge SLC bursts into a single dataset.""" - children = self._get_groups(datatree) + children, modes = self._get_groups(datatree) # First combine all polarizations within each burst. - dss_burst = np.empty(children.shape[1:], dtype=object) - for swath_i, swath in enumerate(children.swath.values): - for burst_idx in children.burst.values: + dss_all = [] + for swath_i, _ in enumerate(_SLC_SWATHS): + dss_swath = [] + for burst_i, _ in enumerate(children[0][swath_i]): burst_dataset = xr.Dataset() - for mode_i, mode in enumerate(children.mode.values): - child = children.sel(mode=mode, swath=swath, burst=burst_idx).item() + for mode_i, mode in enumerate(modes): + child = children[mode_i][swath_i][burst_i] burst = datatree[child] beta0 = self._calibrate_burst(burst) beta0 = self._extract_valid_region(beta0, burst) beta0 = beta0.drop_vars(["line", "pixel"]) beta0 = beta0.rename({"slc": f"beta0_{mode.lower()}"}) burst_dataset.update(beta0) - dss_burst[swath_i, burst_idx] = burst_dataset + dss_swath.append(burst_dataset) + dss_all.append(dss_swath) - dss_swaths = np.empty(children.shape[1], dtype=object) - for swath_i, swath in enumerate(children.swath.values): - dss_swaths[swath_i] = self._merge_bursts(dss_burst[swath_i, :]) + dss_swaths = [] + for swath_i, swath in enumerate(_SLC_SWATHS): + dss_swaths.append(self._merge_bursts(dss_all[swath_i])) dss_swaths = self._align_azimuth(dss_swaths) merged = self._merge_swaths(dss_swaths) @@ -415,32 +418,21 @@ def _expand_names( return merged @staticmethod - def _get_groups(datatree): + def _get_groups(datatree: xr.DataTree) -> tuple[list[list[list[str]]], list[str]]: """Return child group names organized by polarization, swath, and burst.""" - swaths = ["IW1", "IW2", "IW3"] modes = ["VV", "VH", "HV", "HH"] + modes_sel = [] children = [] for mode in modes: mode_children = [] - for swath_i, swath in enumerate(swaths): + for swath_i, swath in enumerate(_SLC_SWATHS): bursts = [x for x in datatree.children if f"_{mode}_{swath}" in x] if bursts: mode_children.append(bursts) + modes_sel.append(modes_sel) if mode_children: children.append(mode_children) - children = np.array(children, dtype=str) - return xr.DataArray( - children, - dims=("mode", "swath", "burst"), - coords={ - "mode": [ - m for m in modes if any(f"_{m}_" in x for x in datatree.children) - ], - "swath": swaths[: children.shape[1]], - "burst": np.arange(children.shape[2]), - }, - name="burst_groups", - ) + return children, list(np.unique(modes_sel)) @staticmethod def _calibrate_burst(burst: xr.DataTree) -> xr.Dataset: @@ -468,7 +460,7 @@ def _extract_valid_region(dataset: xr.Dataset, burst: xr.DataTree) -> xr.Dataset ) @staticmethod - def _merge_bursts(dss: np.ndarray, tolerance: float = 0.01) -> xr.Dataset: + def _merge_bursts(dss: list[xr.Dataset], tolerance: float = 0.01) -> xr.Dataset: """Merge overlapping bursts along azimuth time.""" idxs = np.zeros((len(dss), 2), dtype=int) tol = 0.01 * np.diff(dss[0].azimuth_time.values[:2])[0] @@ -525,7 +517,9 @@ def _merge_bursts(dss: np.ndarray, tolerance: float = 0.01) -> xr.Dataset: return out @staticmethod - def _align_azimuth(dss: np.ndarray, tolerance: float = 0.01) -> list[xr.Dataset]: + def _align_azimuth( + dss: list[xr.Dataset], tolerance: float = 0.01 + ) -> list[xr.Dataset]: """Align all swaths to a shared azimuth-time axis.""" tol = 0.1 * np.diff(dss[0].azimuth_time.values[:2])[0] @@ -832,17 +826,25 @@ def get_dem( lon=slice(bbox_wgs84[0], bbox_wgs84[2]), ).chunk(_DEM_CHUNKSIZE) else: - if resolution is None: - raise ValueError("Resolution must be provided if CRS is not None.") + dem = dem.to_dataset(name="dem") if crs is None: crs = _CRS_WGS84 + if resolution is None: + source_gm = GridMapping.from_dataset(dem) + ref_point = ( + (bbox_wgs84[0] + bbox_wgs84[2]) / 2, + (bbox_wgs84[1] + bbox_wgs84[3]) / 2, + ) + resolution = transform_resolution( + ref_point, source_gm.xy_res, source_gm.crs, crs + ) target_gm = GridMapping.regular_from_bbox( bbox, resolution, crs, tile_size=(_DEM_CHUNKSIZE["lat"], _DEM_CHUNKSIZE["lon"]), ) - dem = resample_in_space(dem.to_dataset(name="dem"), target_gm=target_gm).dem + dem = resample_in_space(dem, target_gm=target_gm).dem return dem From a90c1de5ae4a51f5abd3644056c615c2696d849f Mon Sep 17 00:00:00 2001 From: konstntokas Date: Tue, 21 Jul 2026 12:45:52 +0200 Subject: [PATCH 3/5] ready for review --- tests/amodes/test_sentinel1.py | 43 ++++++++++++++++++++++++++------- tests/amodes/test_sentinel3.py | 6 +++++ tests/helpers/sentinel1.py | 11 ++++++++- xarray_eopf/amodes/sentinel1.py | 12 ++++----- xarray_eopf/backend.py | 8 +++--- 5 files changed, 61 insertions(+), 19 deletions(-) diff --git a/tests/amodes/test_sentinel1.py b/tests/amodes/test_sentinel1.py index 3e6dc21..0d4957d 100644 --- a/tests/amodes/test_sentinel1.py +++ b/tests/amodes/test_sentinel1.py @@ -144,6 +144,10 @@ def test_get_applicable_params(self: TestCase): cache_uri="file:///tmp/cache", ), ) + with pytest.raises( + TypeError, match="resolution argument must contain exactly two" + ): + self.mode.get_applicable_params(resolution=(1, "x")) with pytest.raises(TypeError, match="interp_methods"): self.mode.get_applicable_params(interp_methods="cubic") with pytest.raises(TypeError, match="footprint_scale_factor"): @@ -413,9 +417,12 @@ def test_is_not_valid_source(self): def test_get_groups(self): groups = self.mode._get_groups(self.dt) - self.assertEqual(("mode", "swath", "burst"), groups.dims) - self.assertEqual((2, 2, 1), groups.shape) - self.assertEqual("S1A_IW_SLC_TEST_VV_IW1_0", groups.sel(mode="VV").item(0)) + self.assertEqual(2, len(groups)) + self.assertEqual(2, len(groups[0])) + self.assertEqual(3, len(groups[0][0])) + self.assertEqual(1, len(groups[0][0][0])) + self.assertEqual("S1A_IW_SLC_TEST_VV_IW1_0", groups[0][0][0][0]) + self.assertEqual(["VH", "VV"], [str(mode) for mode in groups[1]]) def test_get_grid_parameters(self): params = sen1._get_grid_parameters( @@ -441,7 +448,7 @@ def test_open_data(self): self.assertCountEqual(["beta0_vv", "beta0_vh"], out.data_vars) self.assertNotIn("line", out.coords) self.assertNotIn("pixel", out.coords) - self.assertEqual({"azimuth_time": 2, "slant_range_time": 3}, out.sizes) + self.assertEqual({"azimuth_time": 2, "slant_range_time": 4}, out.sizes) def test_open_data_accepts_string_include(self): out = self.mode._open_data(self.dt, includes="vv") @@ -670,6 +677,10 @@ def test_get_applicable_params(self: TestCase): agg_methods="nearest", ), ) + with pytest.raises( + TypeError, match="resolution argument must contain exactly two" + ): + self.mode.get_applicable_params(resolution=(1, "x")) with pytest.raises(TypeError): self.mode.get_applicable_params(interp_methods="cubic") @@ -823,7 +834,7 @@ def test_get_dem_requires_credentials(self): with pytest.raises(ValueError, match="Missing AWS credentials"): sen1.get_dem([0, 50, 1, 51]) - def test_get_dem_resolution_required_if_crs_given(self): + def test_get_dem_with_projected_crs_uses_inferred_resolution(self): with patch.dict( os.environ, {"AWS_ACCESS_KEY_ID": "k", "AWS_SECRET_ACCESS_KEY": "s"}, @@ -832,6 +843,18 @@ def test_get_dem_resolution_required_if_crs_given(self): with ( patch.object(sen1.pystac_client.Client, "open") as client_open, patch.object(sen1.rioxarray, "open_rasterio") as open_rasterio, + patch.object(sen1, "transform_resolution", return_value=30.0) as tr, + patch.object( + sen1, + "resample_in_space", + return_value=SimpleNamespace( + dem=xr.DataArray( + np.ones((2, 2), dtype="float32"), + dims=("lat", "lon"), + coords={"lat": [1.0, 0.0], "lon": [0.0, 1.0]}, + ) + ), + ) as resample, ): fake_item = SimpleNamespace(assets={"data": SimpleNamespace(href="x")}) search = SimpleNamespace(items=lambda: [fake_item]) @@ -842,10 +865,12 @@ def test_get_dem_resolution_required_if_crs_given(self): coords={"band": [1], "y": [3, 2, 1, 0], "x": [0, 1, 2, 3]}, ) - with pytest.raises( - ValueError, match="Resolution must be provided if CRS is not None" - ): - sen1.get_dem([0, 0, 1, 1], crs=pyproj.CRS.from_epsg(32632)) + out = sen1.get_dem([0, 0, 900, 900], crs=pyproj.CRS.from_epsg(32632)) + + tr.assert_called_once() + resample.assert_called_once() + self.assertIsInstance(out, xr.DataArray) + self.assertEqual((2, 2), out.shape) def test_get_dem_reprojects_bbox_and_resamples(self): with patch.dict( diff --git a/tests/amodes/test_sentinel3.py b/tests/amodes/test_sentinel3.py index 135176b..7435c6a 100644 --- a/tests/amodes/test_sentinel3.py +++ b/tests/amodes/test_sentinel3.py @@ -49,6 +49,12 @@ def test_get_applicable_params(self: TestCase): agg_methods={"scl": "mode"}, ), ) + with pytest.raises( + TypeError, match="resolution argument must contain exactly two" + ): + self.mode.get_applicable_params(resolution=(1, "x")) + with pytest.raises(TypeError): + self.mode.get_applicable_params(interp_methods=["nearest"]) def test_process_metadata(self: TestCase): self.assertEqual({}, self.mode.process_metadata(xr.DataTree())) diff --git a/tests/helpers/sentinel1.py b/tests/helpers/sentinel1.py index e0fd3db..af46b42 100644 --- a/tests/helpers/sentinel1.py +++ b/tests/helpers/sentinel1.py @@ -128,14 +128,23 @@ def make_s1_slc_datatree() -> xr.DataTree: ], dtype="datetime64[ns]", ), + "IW3": np.array( + [ + "2024-01-01T00:00:00", + "2024-01-01T00:00:01", + "2024-01-01T00:00:02", + ], + dtype="datetime64[ns]", + ), } slant_range_time_by_swath = { "IW1": np.array([0.0, 1.0, 2.0, 3.0], dtype="float32"), "IW2": np.array([1.0, 2.0, 3.0, 4.0], dtype="float32"), + "IW3": np.array([2.0, 3.0, 4.0, 5.0], dtype="float32"), } axis = np.array(["x", "y", "z"]) polarizations = ("VV", "VH") - swaths = ("IW1", "IW2") + swaths = ("IW1", "IW2", "IW3") beta_nought = xr.Dataset( { diff --git a/xarray_eopf/amodes/sentinel1.py b/xarray_eopf/amodes/sentinel1.py index 6c8676f..1f942a9 100644 --- a/xarray_eopf/amodes/sentinel1.py +++ b/xarray_eopf/amodes/sentinel1.py @@ -47,6 +47,7 @@ _DEM_CHUNKSIZE = dict(lat=1800, lon=1800) _CHUNKSIZE = (2048, 2048) _SLC_SWATHS = ["IW1", "IW2", "IW3"] +_SENTINEL1_POLARZIATION_MODES = ["VV", "VH", "HV", "HH"] _REGISTERED_CACHE_URIS = [] @@ -240,7 +241,7 @@ def _open_data( dataset = None measurement_group = "" - for mode in ["VV", "VH", "HV", "HH"]: + for mode in _SENTINEL1_POLARZIATION_MODES: children = [x for x in datatree.children if mode in x] if children: if len(children) != 1: @@ -420,16 +421,15 @@ def _expand_names( @staticmethod def _get_groups(datatree: xr.DataTree) -> tuple[list[list[list[str]]], list[str]]: """Return child group names organized by polarization, swath, and burst.""" - modes = ["VV", "VH", "HV", "HH"] modes_sel = [] children = [] - for mode in modes: + for mode in _SENTINEL1_POLARZIATION_MODES: mode_children = [] for swath_i, swath in enumerate(_SLC_SWATHS): bursts = [x for x in datatree.children if f"_{mode}_{swath}" in x] if bursts: mode_children.append(bursts) - modes_sel.append(modes_sel) + modes_sel.append(mode) if mode_children: children.append(mode_children) return children, list(np.unique(modes_sel)) @@ -717,12 +717,12 @@ def _apply_valid_range(array, *, dtype=None, fill_value=None): bbox = source_gm.xy_bbox if resolution is None: if crs and not crs.is_geographic: - center = ( + ref_point = ( (source_gm.xy_bbox[0] + source_gm.xy_bbox[2]) / 2, (source_gm.xy_bbox[1] + source_gm.xy_bbox[3]) / 2, ) resolution = transform_resolution( - center, source_gm.xy_res, source_gm.crs, crs + ref_point, source_gm.xy_res, source_gm.crs, crs ) else: resolution = source_gm.xy_res diff --git a/xarray_eopf/backend.py b/xarray_eopf/backend.py index 217206b..198adf0 100644 --- a/xarray_eopf/backend.py +++ b/xarray_eopf/backend.py @@ -157,9 +157,11 @@ def open_dataset( group_sep: Separator string used to concatenate groups names to create prefixes for unique variable and dimension names. Defaults to the underscore character (`"_"`) - resolution: Target resolution for all spatial - data variables / bands. For Sentinel-2 products it be one of - `10`, `20`, or `60`. Only used if `op_mode="analysis"`. + resolution: Target spatial resolution for all spatial + data variables/bands. The resolution can be specified as a float, integer, + or a tuple in the form `(easting, northing)`. For Sentinel-2 products, + valid resolutions are `10`, `20`, and `60` meters. This parameter is only + used when `op_mode="analysis"`. bbox: Bounding box [west, south, east, north], used for subsetting. crs: coordinate reference system of output dataset. Can be provided as a `str` or a `pyproj.CRS` object. If a string is given, it will be parsed From b518fcf597c15be08516e77c471502164cbf8f99 Mon Sep 17 00:00:00 2001 From: konstntokas Date: Tue, 21 Jul 2026 14:53:39 +0200 Subject: [PATCH 4/5] ready for review --- docs/examples/sentinel_1_native.ipynb | 52040 +++++++----------------- examples/sentinel_1_native.ipynb | 6771 ++- 2 files changed, 19888 insertions(+), 38923 deletions(-) diff --git a/docs/examples/sentinel_1_native.ipynb b/docs/examples/sentinel_1_native.ipynb index 23ef624..49ddfe6 100644 --- a/docs/examples/sentinel_1_native.ipynb +++ b/docs/examples/sentinel_1_native.ipynb @@ -100,8 +100,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 22.5 ms, sys: 3.07 ms, total: 25.6 ms\n", - "Wall time: 4.1 s\n" + "CPU times: user 24.7 ms, sys: 1.97 ms, total: 26.6 ms\n", + "Wall time: 280 ms\n" ] }, { @@ -611,7 +611,7 @@ " \n", "
  • \n", " created\n", - " \"2026-03-16T18:51:20.575510Z\"\n", + " \"2026-06-16T06:03:39.889364Z\"\n", "
  • \n", " \n", " \n", @@ -629,7 +629,7 @@ " \n", "
  • \n", " updated\n", - " \"2026-03-16T18:51:20.575510Z\"\n", + " \"2026-06-16T06:03:39.991197Z\"\n", "
  • \n", " \n", " \n", @@ -848,7 +848,7 @@ " \n", "
  • \n", " published\n", - " \"2026-03-16T18:51:20.575510Z\"\n", + " \"2026-06-16T06:03:39.889364Z\"\n", "
  • \n", " \n", " \n", @@ -1051,7 +1051,7 @@ " \n", "
  • \n", " EOPF-CPM\n", - " \"2.6.2\"\n", + " \"2.7.0\"\n", "
  • \n", " \n", " \n", @@ -1073,24 +1073,6 @@ " \n", " \n", "
  • \n", - " sar:instrument_mode\n", - " \"IW\"\n", - "
  • \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
  • \n", - " eopf:instrument_mode\n", - " \"IW\"\n", - "
  • \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
  • \n", " sar:center_frequency\n", " 5.405\n", "
  • \n", @@ -1371,7 +1353,7 @@ " \n", "
  • \n", " href\n", - " \"https://objects.eodc.eu:443/e05ab01a9d56408d82ac32d69a5aae2a:202603-s01siwgrh-eu/16/products/cpm_v262/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VH/measurements\"\n", + " \"https://data.eodc.eu/collections/EOPF_ZARR/products/cpm_v270/S01SIWGRH/2026/03/16/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VH/measurements\"\n", "
  • \n", " \n", " \n", @@ -1491,7 +1473,7 @@ " \n", "
  • \n", " href\n", - " \"https://objects.eodc.eu:443/e05ab01a9d56408d82ac32d69a5aae2a:202603-s01siwgrh-eu/16/products/cpm_v262/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VV/measurements\"\n", + " \"https://data.eodc.eu/collections/EOPF_ZARR/products/cpm_v270/S01SIWGRH/2026/03/16/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VV/measurements\"\n", "
  • \n", " \n", " \n", @@ -1611,7 +1593,7 @@ " \n", "
  • \n", " href\n", - " \"https://objects.eodc.eu:443/e05ab01a9d56408d82ac32d69a5aae2a:202603-s01siwgrh-eu/16/products/cpm_v262/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr\"\n", + " \"https://data.eodc.eu/collections/EOPF_ZARR/products/cpm_v270/S01SIWGRH/2026/03/16/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr\"\n", "
  • \n", " \n", " \n", @@ -1731,7 +1713,7 @@ " \n", "
  • \n", " href\n", - " \"https://objects.eodc.eu:443/e05ab01a9d56408d82ac32d69a5aae2a:202603-s01siwgrh-eu/16/products/cpm_v262/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VH/quality/noise\"\n", + " \"https://data.eodc.eu/collections/EOPF_ZARR/products/cpm_v270/S01SIWGRH/2026/03/16/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VH/quality/noise\"\n", "
  • \n", " \n", " \n", @@ -1851,7 +1833,7 @@ " \n", "
  • \n", " href\n", - " \"https://objects.eodc.eu:443/e05ab01a9d56408d82ac32d69a5aae2a:202603-s01siwgrh-eu/16/products/cpm_v262/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VV/quality/noise\"\n", + " \"https://data.eodc.eu/collections/EOPF_ZARR/products/cpm_v270/S01SIWGRH/2026/03/16/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VV/quality/noise\"\n", "
  • \n", " \n", " \n", @@ -1971,7 +1953,7 @@ " \n", "
  • \n", " href\n", - " \"https://objects.eodc.eu:443/e05ab01a9d56408d82ac32d69a5aae2a:202603-s01siwgrh-eu/16/products/cpm_v262/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VH/quality/calibration\"\n", + " \"https://data.eodc.eu/collections/EOPF_ZARR/products/cpm_v270/S01SIWGRH/2026/03/16/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VH/quality/calibration\"\n", "
  • \n", " \n", " \n", @@ -2091,7 +2073,7 @@ " \n", "
  • \n", " href\n", - " \"https://objects.eodc.eu:443/e05ab01a9d56408d82ac32d69a5aae2a:202603-s01siwgrh-eu/16/products/cpm_v262/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VV/quality/calibration\"\n", + " \"https://data.eodc.eu/collections/EOPF_ZARR/products/cpm_v270/S01SIWGRH/2026/03/16/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/S01SIWGRD_20260316T164754_0025_C039_8EA0_00DBA6_VV/quality/calibration\"\n", "
  • \n", " \n", " \n", @@ -2211,7 +2193,7 @@ " \n", "
  • \n", " href\n", - " \"https://download.user.eopf.eodc.eu/zip/collections/sentinel-1-l1-grd/items/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zip\"\n", + " \"https://data.eodc.eu/collections/EOPF_ZARR/products/cpm_v270/S01SIWGRH/2026/03/16/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr.zip\"\n", "
  • \n", " \n", " \n", @@ -2304,7 +2286,7 @@ " \n", "
  • \n", " href\n", - " \"https://objects.eodc.eu:443/e05ab01a9d56408d82ac32d69a5aae2a:202603-s01siwgrh-eu/16/products/cpm_v262/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/.zmetadata\"\n", + " \"https://data.eodc.eu/collections/EOPF_ZARR/products/cpm_v270/S01SIWGRH/2026/03/16/S1C_IW_GRDH_1SDV_20260316T164754_20260316T164819_006792_00DBA6_8EA0.zarr/.zmetadata\"\n", "
  • \n", " \n", " \n", @@ -2414,8 +2396,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: user 718 ms, sys: 110 ms, total: 828 ms\n", - "Wall time: 5.17 s\n" + "CPU times: user 1.79 s, sys: 249 ms, total: 2.04 s\n", + "Wall time: 4.94 s\n" ] }, { @@ -2436,9 +2418,7 @@ "\n", "\n", "\n", - "
    <xarray.DatasetView> Size: 0B\n",
    -       "Dimensions:  ()\n",
    -       "Data variables:\n",
    -       "    *empty*\n",
    -       "Attributes: (2)