From 6b536086c706f62e7f8b1253eb9e4db99bc7f631 Mon Sep 17 00:00:00 2001 From: clarasb Date: Tue, 12 Aug 2025 17:50:25 +0200 Subject: [PATCH 1/6] add resolutions and spatialUnits to mldataset --- xcube/webapi/datasets/controllers.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xcube/webapi/datasets/controllers.py b/xcube/webapi/datasets/controllers.py index a2080c2e5..52dc4335e 100644 --- a/xcube/webapi/datasets/controllers.py +++ b/xcube/webapi/datasets/controllers.py @@ -224,6 +224,8 @@ def get_dataset( dataset_dict["bbox"] = list(ds_bbox) dataset_dict["geometry"] = get_bbox_geometry(dataset_bounds, transformer) dataset_dict["spatialRef"] = crs.to_string() + dataset_dict["resolutions"] = list(ml_ds.avg_resolutions) + dataset_dict["spatialUnits"] = ml_ds.grid_mapping.spatial_unit_name variable_dicts = [] dim_names = set() From 37e39938f53d34d4f2c6f2ab9f106b184e8f68b4 Mon Sep 17 00:00:00 2001 From: clarasb Date: Fri, 19 Sep 2025 14:25:45 +0200 Subject: [PATCH 2/6] update CHANGES.md and extend unit test --- CHANGES.md | 7 +++++++ test/webapi/datasets/test_controllers.py | 2 ++ 2 files changed, 9 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 5117dad2f..993964e8c 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +5,14 @@ * Added two new versions of the xcube logo, one for dark and one for light themes, and replaced the logo in the documentation with the light logo. +### Enhancements +* `xcube serve` now provides new metadata details of a multilevel dataset: + - The spatial unit of the dataset is now given by property `spatialUnits` + - The resolutions are now given by property `resolutions` and provide + the average x,y resolutions for each level of the dataset given in + the spatial units of the dataset's CRS. + ## Changes in 1.11.1 * Improved the demos for the xcube Viewer server-side extensions in various ways (#1134): diff --git a/test/webapi/datasets/test_controllers.py b/test/webapi/datasets/test_controllers.py index fcdfc2db3..82375830d 100644 --- a/test/webapi/datasets/test_controllers.py +++ b/test/webapi/datasets/test_controllers.py @@ -71,6 +71,8 @@ def test_dataset_with_details(self): self.assertIsInstance(dataset.get("bbox"), (tuple, list)) self.assertIsInstance(dataset.get("geometry"), dict) self.assertIsInstance(dataset.get("spatialRef"), str) + self.assertIsInstance(dataset.get("resolutions"), (tuple, list)) + self.assertIsInstance(dataset.get("spatialUnits"), str) self.assertNotIn("rgbSchema", dataset) if dataset["id"] == "demo": demo_dataset = dataset From fd4f5bc906f908ffb38fe13b7b9e3e6218fc055a Mon Sep 17 00:00:00 2001 From: clarasb Date: Mon, 20 Oct 2025 18:31:45 +0200 Subject: [PATCH 3/6] update demo json --- test/webapi/ows/stac/demo-collection.json | 6 ++++++ test/webapi/ows/stac/stac-datacubes-item.json | 6 ++++++ test/webapi/ows/stac/stac-single-item.json | 6 ++++++ 3 files changed, 18 insertions(+) diff --git a/test/webapi/ows/stac/demo-collection.json b/test/webapi/ows/stac/demo-collection.json index d0774b830..b9bdf09fd 100644 --- a/test/webapi/ows/stac/demo-collection.json +++ b/test/webapi/ows/stac/demo-collection.json @@ -524,6 +524,12 @@ ] }, "spatialRef": "EPSG:4326", + "resolutions": [ + 0.0025, + 0.005, + 0.01 + ], + "spatialUnits": "degree", "variables": [ { "id": "demo.conc_chl", diff --git a/test/webapi/ows/stac/stac-datacubes-item.json b/test/webapi/ows/stac/stac-datacubes-item.json index d2a4e4b25..dbe12047e 100644 --- a/test/webapi/ows/stac/stac-datacubes-item.json +++ b/test/webapi/ows/stac/stac-datacubes-item.json @@ -362,6 +362,12 @@ ] }, "spatialRef": "EPSG:4326", + "resolutions": [ + 0.0025, + 0.005, + 0.01 + ], + "spatialUnits": "degree", "variables": [ { "id": "demo-1w.conc_chl", diff --git a/test/webapi/ows/stac/stac-single-item.json b/test/webapi/ows/stac/stac-single-item.json index 15c3ad755..7f6c651b9 100644 --- a/test/webapi/ows/stac/stac-single-item.json +++ b/test/webapi/ows/stac/stac-single-item.json @@ -362,6 +362,12 @@ ] }, "spatialRef": "EPSG:4326", + "resolutions": [ + 0.0025, + 0.005, + 0.01 + ], + "spatialUnits": "degree", "variables": [ { "id": "demo-1w.conc_chl", From de2906dfbccb3cdc49c0150dca7ee896d3f80fe0 Mon Sep 17 00:00:00 2001 From: clarasb Date: Tue, 21 Oct 2025 11:23:08 +0200 Subject: [PATCH 4/6] fix test --- test/core/resampling/test_reproject.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/core/resampling/test_reproject.py b/test/core/resampling/test_reproject.py index a63ebed6a..5c6d96c94 100644 --- a/test/core/resampling/test_reproject.py +++ b/test/core/resampling/test_reproject.py @@ -217,8 +217,8 @@ def test_reproject_complex_dask_array(self): source_ds, target_gm=target_gm, interpolation="bilinear" ) self.assertCountEqual(["temperature"], list(target_ds.data_vars)) - self.assertEqual(target_ds.temperature.values[0, 0, 0], 6427.718652710034) - self.assertEqual(target_ds.temperature.values[0, -1, -1], 3085.9507290783004) + self.assertAlmostEqual(target_ds.temperature.values[0, 0, 0], 6427.718652710034) + self.assertAlmostEqual(target_ds.temperature.values[0, -1, -1], 3085.9507290783004) self.assertEqual( [2, 5, 5], [ From cb2709543e9471b5edd181b752253fd9f4d8244a Mon Sep 17 00:00:00 2001 From: clarasb Date: Tue, 21 Oct 2025 12:39:32 +0200 Subject: [PATCH 5/6] fix test --- test/cli/test_dump.py | 43 +++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/test/cli/test_dump.py b/test/cli/test_dump.py index 96ba7701d..caed0f1d7 100644 --- a/test/cli/test_dump.py +++ b/test/cli/test_dump.py @@ -13,34 +13,21 @@ def test_dump_ds(self): with xr.set_options(display_width=80): result = self.invoke_cli(["dump", TEST_NC_FILE]) - # Use a regex to accommodate the differing output formats produced by - # various xarray versions. - output_regex = r"""( Size: 8MB)? -Dimensions:\s+\((?=.*lon: 360)(?=.*lat: 180)(?=.*time: 5)(?=.*bnds: 2).*?\) -Coordinates: - \* lon \(lon\) float64 (3kB )?-179\.5 -178\.5 -177\.5 \.\.\. 177\.5 178\.5 179\.5 - \* lat \(lat\) float64 (1kB )?-89\.5 -88\.5 -87\.5 -86\.5 \.\.\. (86\.5 )?87\.5 88\.5 89\.5 - \* time \(time\) datetime64\[ns\] (40B )?2010-01-01T12:00:00 \.\.\. 2010-01-(05T1)?\.\.\. - lon_bnds \(lon, bnds\) float64 (6kB )?\.\.\. - lat_bnds \(lat, bnds\) float64 (3kB )?\.\.\. - time_bnds \(time, bnds\) datetime64\[ns\] (80B )?\.\.\. -Dimensions without coordinates: bnds + self.assertIn("", result.output) + self.assertIn("Dimensions: (lon: 360, lat: 180, time: 5, bnds: 2)\n", result.output) + self.assertIn("Coordinates:\n", result.output) + self.assertIn(" * lon (lon) float64 ", result.output) + self.assertIn("Data variables:\n", result.output) + self.assertIn(" precipitation (time, lat, lon) float64 ", result.output) + self.assertIn("Attributes:\n", result.output) + self.assertIn("title: Test Cube", result.output) + + variables_regex = r""" Data variables: - precipitation \(time, lat, lon\) float64 (3MB )?... - temperature \(time, lat, lon\) float64 (3MB )?... - soil_moisture \(time, lat, lon\) float64 (3MB )?... -Attributes: - Conventions: CF-1.7 - title: Test Cube - time_coverage_start: 2010-01-01T00:00:00.000000000 - time_coverage_end: 2010-01-06T00:00:00.000000000 - geospatial_lon_min: -180.0 - geospatial_lon_max: 180.0 - geospatial_lon_units: degrees_east - geospatial_lat_min: -90.0 - geospatial_lat_max: 90.0 - geospatial_lat_units: degrees_north + precipitation \(time, lat, lon\) float64 (3MB)? ... + temperature \(time, lat, lon\) float64 (3MB)? ... + soil_moisture \(time, lat, lon\) float64 (3MB)? ... """ - - self.assertRegex(result.output, output_regex) + + self.assertRegex(result.output, variables_regex) self.assertEqual(0, result.exit_code) From 73abdc43ca14a1741fdbb2dd2f9c9e3103dd632c Mon Sep 17 00:00:00 2001 From: clarasb Date: Tue, 21 Oct 2025 12:51:42 +0200 Subject: [PATCH 6/6] fix test --- test/cli/test_dump.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test/cli/test_dump.py b/test/cli/test_dump.py index caed0f1d7..503e1d3c0 100644 --- a/test/cli/test_dump.py +++ b/test/cli/test_dump.py @@ -14,7 +14,6 @@ def test_dump_ds(self): result = self.invoke_cli(["dump", TEST_NC_FILE]) self.assertIn("", result.output) - self.assertIn("Dimensions: (lon: 360, lat: 180, time: 5, bnds: 2)\n", result.output) self.assertIn("Coordinates:\n", result.output) self.assertIn(" * lon (lon) float64 ", result.output) self.assertIn("Data variables:\n", result.output) @@ -28,6 +27,6 @@ def test_dump_ds(self): temperature \(time, lat, lon\) float64 (3MB)? ... soil_moisture \(time, lat, lon\) float64 (3MB)? ... """ - + self.assertRegex(result.output, variables_regex) self.assertEqual(0, result.exit_code)