Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Xarray sub-package #1013

Merged
merged 19 commits into from
Nov 7, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
5 changes: 5 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ jobs:
python -m pip install -e src/titiler/extensions["test,cogeo,stac"]
python -m pytest src/titiler/extensions --cov=titiler.extensions --cov-report=xml --cov-append --cov-report=term-missing

- name: Test titiler.xarray
run: |
python -m pip install -e src/titiler/xarray["test"]
python -m pytest src/titiler/xarray --cov=titiler.xarray --cov-report=xml --cov-append --cov-report=term-missing

- name: Test titiler.mosaic
run: |
python -m pip install -e src/titiler/mosaic["test"]
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy_mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install src/titiler/core src/titiler/extensions["cogeo,stac"] src/titiler/mosaic src/titiler/application
python -m pip install src/titiler/core src/titiler/extensions["cogeo,stac"] src/titiler/xarray src/titiler/mosaic src/titiler/application
python -m pip install -r requirements/requirements-docs.txt


Expand Down
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ Starting with version `0.3.0`, the `TiTiler` python module has been split into a
| Package | Version | Description
| ------- | ------- |-------------
[**titiler.core**](https://github.com/developmentseed/titiler/tree/main/src/titiler/core) | [![titiler.core](https://img.shields.io/pypi/v/titiler.core?color=%2334D058&label=pypi)](https://pypi.org/project/titiler.core) | The `Core` package contains libraries to help create a dynamic tiler for COG and STAC
[**titiler.xarray**](https://github.com/developmentseed/titiler/tree/main/src/titiler/xarray) | [![titiler.xarray](https://img.shields.io/pypi/v/titiler.xarray?color=%2334D058&label=pypi)](https://pypi.org/project/titiler.xarray) | The `xarray` package contains libraries to help create a dynamic tiler for Zarr/NetCDF datasets
[**titiler.extensions**](https://github.com/developmentseed/titiler/tree/main/src/titiler/extensions) | [![titiler.extensions](https://img.shields.io/pypi/v/titiler.extensions?color=%2334D058&label=pypi)](https://pypi.org/project/titiler.extensions) | TiTiler's extensions package. Contains extensions for Tiler Factories.
[**titiler.mosaic**](https://github.com/developmentseed/titiler/tree/main/src/titiler/mosaic) | [![titiler.mosaic](https://img.shields.io/pypi/v/titiler.mosaic?color=%2334D058&label=pypi)](https://pypi.org/project/titiler.mosaic) | The `mosaic` package contains libraries to help create a dynamic tiler for MosaicJSON (adds `cogeo-mosaic` requirement)
[**titiler.application**](https://github.com/developmentseed/titiler/tree/main/src/titiler/application) | [![titiler.application](https://img.shields.io/pypi/v/titiler.application?color=%2334D058&label=pypi)](https://pypi.org/project/titiler.application) | TiTiler's `demo` package. Contains a FastAPI application with full support of COG, STAC and MosaicJSON
Expand All @@ -71,6 +72,7 @@ python -m pip install -U pip
python -m pip install titiler.{package}
# e.g.,
# python -m pip install titiler.core
# python -m pip install titiler.xarray
# python -m pip install titiler.extensions
# python -m pip install titiler.mosaic
# python -m pip install titiler.application (also installs core, extensions and mosaic)
Expand All @@ -89,7 +91,7 @@ git clone https://github.com/developmentseed/titiler.git
cd titiler

python -m pip install -U pip
python -m pip install -e src/titiler/core -e src/titiler/extensions -e src/titiler/mosaic -e src/titiler/application
python -m pip install -e src/titiler/core -e src/titiler/xarray -e src/titiler/extensions -e src/titiler/mosaic -e src/titiler/application
python -m pip install uvicorn

uvicorn titiler.application.main:app --reload
Expand Down Expand Up @@ -125,6 +127,7 @@ Some options can be set via environment variables, see: https://github.com/tiang
src/titiler/ - titiler modules.
├── application/ - Titiler's `Application` package
├── extensions/ - Titiler's `Extensions` package
├── xarray/ - Titiler's `Xarray` package
├── core/ - Titiler's `Core` package
└── mosaic/ - Titiler's `Mosaic` package
```
Expand Down
1 change: 1 addition & 0 deletions scripts/publish
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

SUBPACKAGE_DIRS=(
"core"
"xarray"
"mosaic"
"application"
"extensions"
Expand Down
46 changes: 46 additions & 0 deletions src/titiler/xarray/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
## titiler.xarray

Adds support for Xarray Dataset (NetCDF/Zarr) in Titiler.

## Installation

```bash
$ python -m pip install -U pip

# From Pypi
$ python -m pip install titiler.xarray

# Or from sources
$ git clone https://github.com/developmentseed/titiler.git
$ cd titiler && python -m pip install -e src/titiler/core -e src/titiler/xarray
```

## How To

```python
from fastapi import FastAPI
from titiler.xarray.factory import TilerFactory

# Create a FastAPI application
app = FastAPI(
description="A lightweight Cloud Optimized GeoTIFF tile server",
)

# Create a set of MosaicJSON endpoints
endpoint = TilerFactory()

# Register the Mosaic endpoints to the application
app.include_router(endpoint.router)
```

## Package structure

```
titiler/
└── xarray/
├── tests/ - Tests suite
└── titiler/xarray/ - `xarray` namespace package
├── dependencies.py - titiler-xarray dependencies
├── io.py - titiler-xarray Readers
└── factory.py - endpoints factory
```
70 changes: 70 additions & 0 deletions src/titiler/xarray/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
[project]
name = "titiler.xarray"
description = "Xarray plugin for TiTiler."
readme = "README.md"
requires-python = ">=3.8"
authors = [
{name = "Vincent Sarago", email = "[email protected]"},
{name = "Aimee Barciauskas", email = "[email protected]"},
]
license = {text = "MIT"}
keywords = [
"TiTiler",
"Xarray",
"Zarr",
"NetCDF",
"HDF",
]
classifiers = [
"Intended Audience :: Information Technology",
vincentsarago marked this conversation as resolved.
Show resolved Hide resolved
"Intended Audience :: Science/Research",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Scientific/Engineering :: GIS",
]
dynamic = ["version"]
dependencies = [
"titiler.core==0.19.0.dev",
"cftime",
"h5netcdf",
"xarray",
"rioxarray",
"zarr",
"fsspec",
"s3fs",
"aiohttp",
"pandas",
"httpx",
]
vincentsarago marked this conversation as resolved.
Show resolved Hide resolved

[project.optional-dependencies]
test = [
"pytest",
"pytest-cov",
"pytest-asyncio",
"httpx",
]

[project.urls]
Homepage = "https://developmentseed.org/titiler/"
Documentation = "https://developmentseed.org/titiler/"
Issues = "https://github.com/developmentseed/titiler/issues"
Source = "https://github.com/developmentseed/titiler"
Changelog = "https://developmentseed.org/titiler/release-notes/"

[build-system]
requires = ["pdm-pep517"]
build-backend = "pdm.pep517.api"

[tool.pdm.version]
source = "file"
path = "titiler/xarray/__init__.py"

[tool.pdm.build]
includes = ["titiler/xarray"]
excludes = ["tests/", "**/.mypy_cache", "**/.DS_Store"]
1 change: 1 addition & 0 deletions src/titiler/xarray/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""titiler.xarray test configuration."""
51 changes: 51 additions & 0 deletions src/titiler/xarray/tests/test_dependencies.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""test dependencies."""


from fastapi import Depends, FastAPI, Path
from starlette.testclient import TestClient
from typing_extensions import Annotated

from titiler.xarray import dependencies


def test_xarray_tile():
"""Create App."""
app = FastAPI()

@app.get("/tiles/{z}/{x}/{y}")
def tiles(
z: Annotated[
int,
Path(
description="Identifier (Z) selecting one of the scales defined in the TileMatrixSet and representing the scaleDenominator the tile.",
),
],
x: Annotated[
int,
Path(
description="Column (X) index of the tile on the selected TileMatrix. It cannot exceed the MatrixHeight-1 for the selected TileMatrix.",
),
],
y: Annotated[
int,
Path(
description="Row (Y) index of the tile on the selected TileMatrix. It cannot exceed the MatrixWidth-1 for the selected TileMatrix.",
),
],
params=Depends(dependencies.CompatXarrayParams),
):
"""return params."""
return params.as_dict()

with TestClient(app) as client:
response = client.get("/tiles/1/2/3")
params = response.json()
assert params == {}

response = client.get("/tiles/1/2/3", params={"variable": "yo"})
params = response.json()
assert params == {"variable": "yo"}

response = client.get("/tiles/1/2/3", params={"multiscale": True})
params = response.json()
assert params == {"group": 1}
105 changes: 105 additions & 0 deletions src/titiler/xarray/tests/test_io_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""test titiler.xarray.io utility functions."""

from datetime import datetime

import numpy
import pytest
import xarray

from titiler.xarray.io import get_variable


def test_get_variable():
"""test io.get_variable."""
arr = numpy.arange(0, 33 * 35 * 2).reshape(2, 33, 35)
data = xarray.DataArray(
arr,
dims=("time", "y", "x"),
coords={
"x": numpy.arange(-170, 180, 10),
"y": numpy.arange(-80, 85, 5),
"time": [datetime(2022, 1, 1), datetime(2023, 1, 1)],
},
)
data.attrs.update({"valid_min": arr.min(), "valid_max": arr.max()})
assert not data.rio.crs
assert data.dims == ("time", "y", "x")

ds = data.to_dataset(name="dataset")
da = get_variable(ds, "dataset")
assert da.rio.crs
assert da.dims == ("y", "x")
# Default to the first Time value
assert da["time"] == numpy.datetime64("2022-01-01")

da = get_variable(ds, "dataset", datetime="2023-01-01T01:00:00.000Z")
assert da.rio.crs
assert da.dims == ("y", "x")
assert da["time"] == numpy.datetime64("2023-01-01")

# Select the Nearest Time
da = get_variable(ds, "dataset", datetime="2024-01-01T01:00:00.000Z")
assert da.rio.crs
assert da.dims == ("y", "x")
assert da["time"] == numpy.datetime64("2023-01-01")

data = data.rename({"y": "Lat", "x": "Lon"})
assert data.dims == ("time", "Lat", "Lon")
ds = data.to_dataset(name="dataset")
da = get_variable(ds, "dataset")
assert da.rio.crs
assert da.dims == ("y", "x")

# 4D dataset
arr = numpy.arange(0, 33 * 35 * 2).reshape(2, 1, 33, 35)
data = xarray.DataArray(
arr,
dims=("time", "z", "y", "x"),
coords={
"x": numpy.arange(-170, 180, 10),
"y": numpy.arange(-80, 85, 5),
"z": [0],
"time": [datetime(2022, 1, 1), datetime(2023, 1, 1)],
},
)
ds = data.to_dataset(name="dataset")
da = get_variable(ds, "dataset")
assert da.rio.crs
assert da.dims == ("z", "y", "x")

# 5D dataset
arr = numpy.arange(0, 33 * 35 * 2).reshape(2, 1, 1, 33, 35)
data = xarray.DataArray(
arr,
dims=("time", "universe", "z", "y", "x"),
coords={
"x": numpy.arange(-170, 180, 10),
"y": numpy.arange(-80, 85, 5),
"z": [0],
"universe": ["somewhere"],
"time": [datetime(2022, 1, 1), datetime(2023, 1, 1)],
},
)
ds = data.to_dataset(name="dataset")
with pytest.raises(AssertionError):
get_variable(ds, "dataset")

da = get_variable(ds, "dataset", drop_dim="universe=somewhere")
assert da.rio.crs
assert da.dims == ("z", "y", "x")

# 5D dataset
arr = numpy.arange(0, 33 * 35 * 2).reshape(2, 33, 35)
data = xarray.DataArray(
arr,
dims=("time", "haut_bas", "gauche_droite"),
coords={
"gauche_droite": numpy.arange(-170, 180, 10),
"haut_bas": numpy.arange(-80, 85, 5),
"time": [datetime(2022, 1, 1), datetime(2023, 1, 1)],
},
)

ds = data.to_dataset(name="dataset")
with pytest.raises(ValueError):
da = get_variable(ds, "dataset")
3 changes: 3 additions & 0 deletions src/titiler/xarray/titiler/xarray/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""titiler.xarray"""

__version__ = "0.19.0.dev"
Loading
Loading