Skip to content

Commit 60fdc90

Browse files
authored
Merge pull request #1213 from xcube-dev/clarasb-xxx-239-add_additional_dimension
Extend WebAPI to derive time series and statistics for datasets with multiple non-spatial dimensions
2 parents d94ebe7 + d2ea5a1 commit 60fdc90

17 files changed

Lines changed: 408 additions & 56 deletions

File tree

CHANGES.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,11 @@
55
that comes with, e.g., several bug fixes including issues with the time series legend,
66
the zoom information box, persisted states, and the progress bar, as well as updated
77
dependencies such as `chartlets ^0.2.0`.
8+
* WebAPI now supports statistics and time series for dataset variables with
9+
additional non-spatial dimensions beyond `time`, `lat`, and `lon` (e.g. `depth`).
10+
Dimension values can be passed as query parameters; if omitted, a value is selected
11+
automatically. As part of this, `xcube.core.tile.get_non_spatial_labels` has been
12+
made a public function. (#1213)
813

914
### Fixes
1015
* Adapted to xarray version 2026.04.0 (#1211)

test/webapi/datasets/test_controllers.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def assertDatasetsOk(self, response: Any, expected_count: Optional[int] = None):
4242
class DatasetsControllerTest(DatasetsControllerTestBase):
4343
def test_datasets(self):
4444
response = get_datasets(get_datasets_ctx())
45-
datasets = self.assertDatasetsOk(response, expected_count=2)
45+
datasets = self.assertDatasetsOk(response, expected_count=3)
4646
for dataset in datasets:
4747
self.assertIsInstance(dataset, dict)
4848
self.assertIn("id", dataset)
@@ -55,7 +55,7 @@ def test_dataset_with_details(self):
5555
response = get_datasets(
5656
get_datasets_ctx(), details=True, base_url="http://test"
5757
)
58-
datasets = self.assertDatasetsOk(response, expected_count=2)
58+
datasets = self.assertDatasetsOk(response, expected_count=3)
5959

6060
demo_dataset = None
6161
demo_1w_dataset = None
@@ -97,7 +97,7 @@ def test_dataset_with_point(self):
9797
response = get_datasets(
9898
get_datasets_ctx(), point=(1.7, 51.2), base_url="http://test"
9999
)
100-
datasets = self.assertDatasetsOk(response, expected_count=2)
100+
datasets = self.assertDatasetsOk(response, expected_count=3)
101101
dataset = datasets[0]
102102
self.assertIsInstance(dataset, dict)
103103
self.assertIn("id", dataset)
@@ -114,7 +114,7 @@ def test_dataset_with_point_and_details(self):
114114
response = get_datasets(
115115
get_datasets_ctx(), point=(1.7, 51.2), details=True, base_url="http://test"
116116
)
117-
datasets = self.assertDatasetsOk(response, expected_count=2)
117+
datasets = self.assertDatasetsOk(response, expected_count=3)
118118
dataset = datasets[0]
119119
self.assertIsInstance(dataset, dict)
120120
self.assertIn("id", dataset)

test/webapi/ows/stac/test_controllers.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@
136136
"title": f'Feature for the dataset "{dsid}"',
137137
"type": "application/geo+json",
138138
}
139-
for dsid in ["demo", "demo-1w"]
139+
for dsid in ["demo", "demo-1w", "demo-multidimensional"]
140140
],
141141
"providers": [],
142142
"stac_version": STAC_VERSION,
@@ -234,7 +234,7 @@ def test_get_datasets_collection_items(self):
234234

235235
features = result["features"]
236236
self.assertIsInstance(features, list)
237-
self.assertEqual(2, len(features))
237+
self.assertEqual(3, len(features))
238238

239239
for feature in features:
240240
self.assertIsInstance(feature, dict)
@@ -245,7 +245,7 @@ def test_get_datasets_collection_items(self):
245245
self.assertIsInstance(feature.get("geometry"), dict)
246246
self.assertIsInstance(feature.get("assets"), dict)
247247
self.assertIsInstance(feature.get("id"), str)
248-
self.assertIn(feature["id"], {"demo", "demo-1w"})
248+
self.assertIn(feature["id"], {"demo", "demo-1w","demo-multidimensional"})
249249
# TODO (forman): add more assertions
250250
# import pprint
251251
# pprint.pprint(feature)
@@ -300,7 +300,7 @@ def test_get_collections(self):
300300
)
301301
self.assertEqual(EXPECTED_DATASETS_COLLECTION, result["collections"][0])
302302
self.assertEqual(
303-
["datacubes", "demo", "demo-1w"],
303+
["datacubes", "demo", "demo-1w", "demo-multidimensional"],
304304
[collection["id"] for collection in result["collections"]],
305305
)
306306

test/webapi/ows/wmts/res/WMTSCapabilities-CRS84.xml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,45 @@
493493
<Value>2017-02-05T00:00:00.000000000</Value>
494494
</Dimension>
495495
</Layer>
496+
<Layer>
497+
<ows:Identifier>demo-multidimensional.conc_chl</ows:Identifier>
498+
<ows:Title>demo-multidimensional/Chlorophyll concentration</ows:Title>
499+
<ows:Abstract/>
500+
<ows:WGS84BoundingBox>
501+
<ows:LowerCorner>0 50</ows:LowerCorner>
502+
<ows:UpperCorner>5 52.5</ows:UpperCorner>
503+
</ows:WGS84BoundingBox>
504+
<Style isDefault="true">
505+
<ows:Identifier>Default</ows:Identifier>
506+
</Style>
507+
<Format>image/png</Format>
508+
<TileMatrixSetLink>
509+
<TileMatrixSet>WorldCRS84Quad</TileMatrixSet>
510+
</TileMatrixSetLink>
511+
<ResourceURL format="image/png" resourceType="tile" template="http://bibo/wmts/1.0.0/tile/demo-multidimensional/conc_chl/WorldCRS84Quad/{TileMatrix}/{TileRow}/{TileCol}.png"/>
512+
<Dimension>
513+
<ows:Identifier>depth</ows:Identifier>
514+
<ows:Title>depth</ows:Title>
515+
<ows:UOM/>
516+
<Default>0</Default>
517+
<Current>false</Current>
518+
<Value>0.0</Value>
519+
<Value>10.0</Value>
520+
<Value>20.0</Value>
521+
</Dimension>
522+
<Dimension>
523+
<ows:Identifier>time</ows:Identifier>
524+
<ows:Title>time</ows:Title>
525+
<ows:UOM>ISO8601</ows:UOM>
526+
<Default>current</Default>
527+
<Current>true</Current>
528+
<Value>2017-01-16T10:09:21.834255872</Value>
529+
<Value>2017-01-25T09:35:51.060063488</Value>
530+
<Value>2017-01-26T10:50:16.686192896</Value>
531+
<Value>2017-01-28T09:58:11.350386176</Value>
532+
<Value>2017-01-30T10:46:33.836892416</Value>
533+
</Dimension>
534+
</Layer>
496535
<TileMatrixSet>
497536
<ows:Identifier>WorldCRS84Quad</ows:Identifier>
498537
<ows:Title>CRS84 for the World</ows:Title>
@@ -632,6 +671,17 @@
632671
<LayerRef>demo-1w.quality_flags_stdev</LayerRef>
633672
</Theme>
634673
</Theme>
674+
<Theme>
675+
<ows:Identifier>demo-multidimensional</ows:Identifier>
676+
<ows:Title>Demonstration Multidimensional Cube</ows:Title>
677+
<ows:Abstract/>
678+
<Theme>
679+
<ows:Identifier>demo-multidimensional.conc_chl</ows:Identifier>
680+
<ows:Title>demo-multidimensional/Chlorophyll concentration</ows:Title>
681+
<ows:Abstract/>
682+
<LayerRef>demo-multidimensional.conc_chl</LayerRef>
683+
</Theme>
684+
</Theme>
635685
</Themes>
636686
<ServiceMetadataURL xlink:href="http://bibo/wmts/1.0.0/WorldCRS84Quad/WMTSCapabilities.xml"/>
637687
</Capabilities>

test/webapi/ows/wmts/res/WMTSCapabilities-OSM.xml

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -493,6 +493,45 @@
493493
<Value>2017-02-05T00:00:00.000000000</Value>
494494
</Dimension>
495495
</Layer>
496+
<Layer>
497+
<ows:Identifier>demo-multidimensional.conc_chl</ows:Identifier>
498+
<ows:Title>demo-multidimensional/Chlorophyll concentration</ows:Title>
499+
<ows:Abstract/>
500+
<ows:WGS84BoundingBox>
501+
<ows:LowerCorner>0 50</ows:LowerCorner>
502+
<ows:UpperCorner>5 52.5</ows:UpperCorner>
503+
</ows:WGS84BoundingBox>
504+
<Style isDefault="true">
505+
<ows:Identifier>Default</ows:Identifier>
506+
</Style>
507+
<Format>image/png</Format>
508+
<TileMatrixSetLink>
509+
<TileMatrixSet>WorldWebMercatorQuad</TileMatrixSet>
510+
</TileMatrixSetLink>
511+
<ResourceURL format="image/png" resourceType="tile" template="http://bibo/wmts/1.0.0/tile/demo-multidimensional/conc_chl/WorldWebMercatorQuad/{TileMatrix}/{TileRow}/{TileCol}.png"/>
512+
<Dimension>
513+
<ows:Identifier>depth</ows:Identifier>
514+
<ows:Title>depth</ows:Title>
515+
<ows:UOM/>
516+
<Default>0</Default>
517+
<Current>false</Current>
518+
<Value>0.0</Value>
519+
<Value>10.0</Value>
520+
<Value>20.0</Value>
521+
</Dimension>
522+
<Dimension>
523+
<ows:Identifier>time</ows:Identifier>
524+
<ows:Title>time</ows:Title>
525+
<ows:UOM>ISO8601</ows:UOM>
526+
<Default>current</Default>
527+
<Current>true</Current>
528+
<Value>2017-01-16T10:09:21.834255872</Value>
529+
<Value>2017-01-25T09:35:51.060063488</Value>
530+
<Value>2017-01-26T10:50:16.686192896</Value>
531+
<Value>2017-01-28T09:58:11.350386176</Value>
532+
<Value>2017-01-30T10:46:33.836892416</Value>
533+
</Dimension>
534+
</Layer>
496535
<TileMatrixSet>
497536
<ows:Identifier>WorldWebMercatorQuad</ows:Identifier>
498537
<ows:Title>Google Maps Compatible for the World</ows:Title>
@@ -632,6 +671,17 @@
632671
<LayerRef>demo-1w.quality_flags_stdev</LayerRef>
633672
</Theme>
634673
</Theme>
674+
<Theme>
675+
<ows:Identifier>demo-multidimensional</ows:Identifier>
676+
<ows:Title>Demonstration Multidimensional Cube</ows:Title>
677+
<ows:Abstract/>
678+
<Theme>
679+
<ows:Identifier>demo-multidimensional.conc_chl</ows:Identifier>
680+
<ows:Title>demo-multidimensional/Chlorophyll concentration</ows:Title>
681+
<ows:Abstract/>
682+
<LayerRef>demo-multidimensional.conc_chl</LayerRef>
683+
</Theme>
684+
</Theme>
635685
</Themes>
636686
<ServiceMetadataURL xlink:href="http://bibo/wmts/1.0.0/WorldWebMercatorQuad/WMTSCapabilities.xml"/>
637687
</Capabilities>

test/webapi/res/config-stats.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,22 @@ Datasets:
4444
Path: ../../../examples/serve/demo/sample-cog.tif
4545
Style: tif_style
4646

47+
- Identifier: demo-multidimensional
48+
Title: Demonstration Multidimensional Cube
49+
GroupTitle: Multidimensional Demo
50+
Tags: ["demo", "computed"]
51+
FileSystem: memory
52+
Path: script.py
53+
Variables:
54+
- "conc_chl"
55+
Function: simulate_multidimensional_dataset
56+
InputDatasets: ["demo"]
57+
InputParameters:
58+
variables: ["conc_chl"]
59+
depths: [0.0, 10.0, 20.0]
60+
factor: 0.2
61+
Style: default
62+
4763
PlaceGroups:
4864
- Identifier: inside-cube
4965
Title: Points inside the cube

test/webapi/res/config.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,22 @@ Datasets:
3737
incl_stdev: True
3838
Style: default
3939

40+
- Identifier: demo-multidimensional
41+
Title: Demonstration Multidimensional Cube
42+
GroupTitle: Multidimensional Demo
43+
Tags: ["demo", "computed"]
44+
FileSystem: memory
45+
Path: script.py
46+
Variables:
47+
- "conc_chl"
48+
Function: simulate_multidimensional_dataset
49+
InputDatasets: ["demo"]
50+
InputParameters:
51+
variables: ["conc_chl"]
52+
depths: [0.0, 10.0, 20.0]
53+
factor: 0.2
54+
Style: default
55+
4056
PlaceGroups:
4157
- Identifier: inside-cube
4258
Title: Points inside the cube

test/webapi/res/script.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
# Permissions are hereby granted under the terms of the MIT License:
33
# https://opensource.org/licenses/MIT.
44

5+
import pandas as pd
56
import numpy as np
67
import xarray as xr
78

@@ -62,3 +63,31 @@ def broken_ml_dataset_factory_1():
6263
def broken_ml_dataset_factory_2(ml_dataset: MultiLevelDataset):
6364
"""Example for a custom, broken MultiLevelDataset class."""
6465
return xr.Dataset()
66+
67+
68+
def simulate_multidimensional_dataset(ds, variables, depths, factor):
69+
dim_name = "depth"
70+
71+
depth_coord = xr.DataArray(depths, dims=(dim_name,), coords={dim_name: depths})
72+
depth_factors = xr.DataArray(
73+
[factor**i for i in range(len(depths))],
74+
dims=(dim_name,),
75+
coords={dim_name: depths},
76+
)
77+
78+
data_vars = {}
79+
80+
for name in variables:
81+
data = ds[name]
82+
83+
if dim_name in data.dims:
84+
data_vars[name] = data
85+
continue
86+
87+
data_expanded = data.expand_dims({dim_name: depths})
88+
data_scaled = data_expanded * depth_factors
89+
90+
data_scaled.attrs.update(data.attrs)
91+
data_vars[name] = data_scaled
92+
93+
return xr.Dataset(data_vars, coords={dim_name: depth_coord})

0 commit comments

Comments
 (0)