Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/src/advanced/Extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class FactoryExtension(metaclass=abc.ABCMeta):

#### titiler.xarray.DatasetMetadataExtension

- Goal: adds `/dataset/`, `/dataset/keys` and `/datasets/dict` endpoints which return metadata about a multidimensional Dataset (not a DataArray)
- Goal: adds `/dataset/`, `/dataset/keys`, `/dataset/dict`, `/dataset/dimensions` and `/dataset/coordinates/{name}` endpoints which return metadata about a multidimensional Dataset (not a DataArray)

#### titiler.mosaic.extensions.wmts.wmtsExtension

Expand Down
48 changes: 44 additions & 4 deletions src/titiler/xarray/tests/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ def test_tiler_factory():

md = TilerFactory(
router_prefix="/md",
# /dataset, /dataset/dict, /dataset/keys
# /dataset, /dataset/dict, /dataset/keys, /dataset/dimensions,
# /dataset/coordinates/{name}
extensions=[DatasetMetadataExtension()],
)
assert len(md.router.routes) == 18
assert len(md.router.routes) == 20

app = FastAPI()
app.include_router(md.router, prefix="/md")
Expand All @@ -69,7 +70,7 @@ def app():
],
reader=FsReader,
)
assert len(md.router.routes) == 18
assert len(md.router.routes) == 20

app = FastAPI()
app.include_router(md.router, prefix="/md")
Expand All @@ -86,7 +87,7 @@ def app_zarr():
DatasetMetadataExtension(),
],
)
assert len(md.router.routes) == 18
assert len(md.router.routes) == 20

app = FastAPI()
app.include_router(md.router, prefix="/md")
Expand Down Expand Up @@ -115,6 +116,45 @@ def test_dataset_extension(filename, app):
assert "text/html" in resp.headers["content-type"]


def test_dataset_dimensions(app):
"""Test dimension names and sizes."""
resp = app.get("/md/dataset/dimensions", params={"url": dataset_2d_nc})
assert resp.status_code == 200
assert resp.headers["content-type"] == "application/json"
assert resp.json() == {"y": 1000, "x": 2000}


def test_dataset_coordinate(app):
"""Test coordinate metadata and values."""
resp = app.get(
"/md/dataset/coordinates/x",
params={"url": dataset_2d_nc},
)
assert resp.status_code == 200
assert resp.headers["content-type"] == "application/json"
coordinate = resp.json()
assert coordinate["name"] == "x"
assert coordinate["dims"] == ["x"]
assert coordinate["data"][:2] == [-170.0, -169.83]

resp = app.get(
"/md/dataset/coordinates/time",
params={"url": dataset_3d_nc},
)
assert resp.status_code == 200
assert resp.json()["data"] == [
"2022-01-01T00:00:00",
"2023-01-01T00:00:00",
]

resp = app.get(
"/md/dataset/coordinates/missing",
params={"url": dataset_2d_nc},
)
assert resp.status_code == 404
assert resp.json() == {"detail": "Coordinate 'missing' not found"}


@pytest.mark.parametrize(
"filename",
[dataset_2d_nc, dataset_3d_nc, dataset_3d_zarr],
Expand Down
35 changes: 34 additions & 1 deletion src/titiler/xarray/titiler/xarray/extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

import xarray
from attrs import define
from fastapi import Depends, Query
from fastapi import Depends, HTTPException, Query
from rio_tiler.constants import WGS84_CRS
from starlette.responses import HTMLResponse

Expand Down Expand Up @@ -102,6 +102,39 @@ def dataset_metadata_dict(
with self.dataset_opener(src_path, **io_params.as_dict()) as ds:
return ds.to_dict(data=False)

@factory.router.get(
"/dataset/dimensions",
response_model=dict[str, int],
responses={200: {"description": "Returns the Dataset dimension sizes."}},
)
def dataset_dimensions(
src_path=Depends(factory.path_dependency),
io_params=Depends(self.io_dependency),
):
"""Returns the Dataset dimension sizes."""
with self.dataset_opener(src_path, **io_params.as_dict()) as ds:
return dict(ds.sizes)

@factory.router.get(
"/dataset/coordinates/{name}",
responses={
200: {"description": "Returns a Dataset coordinate and its values."}
},
)
def dataset_coordinate(
name: str,
src_path=Depends(factory.path_dependency),
io_params=Depends(self.io_dependency),
):
"""Returns a Dataset coordinate and its values."""
with self.dataset_opener(src_path, **io_params.as_dict()) as ds:
try:
return ds.coords[name].to_dict(data=True)
except KeyError as e:
raise HTTPException(
status_code=404, detail=f"Coordinate '{name}' not found"
) from e

@factory.router.get(
"/dataset/keys",
response_model=list[str],
Expand Down
104 changes: 52 additions & 52 deletions uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading