diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 9d91f4104..ab341a020 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -109,6 +109,11 @@ nav: - errors: api/titiler/mosaic/errors.md - models: - responses: api/titiler/mosaic/models/responses.md + - titiler.xarray: + - io: api/titiler/xarray/io.md + - dependencies: api/titiler/xarray/dependencies.md + - extensions: api/titiler/xarray/extensions.md + - factory: api/titiler/xarray/factory.md - Deployment: - Amazon Web Services: diff --git a/docs/src/advanced/endpoints_factories.md b/docs/src/advanced/endpoints_factories.md index 9d5bcdf1a..033186d1e 100644 --- a/docs/src/advanced/endpoints_factories.md +++ b/docs/src/advanced/endpoints_factories.md @@ -7,8 +7,9 @@ TiTiler's endpoints factories are helper functions that let users create a FastA Factories classes use [dependencies injection](dependencies.md) to define most of the endpoint options. +## titiler.core -## BaseFactory +### BaseFactory class: `titiler.core.factory.BaseFactory` @@ -27,7 +28,7 @@ Most **Factories** are built from this [abstract based class](https://docs.pytho - **url_for**: Method to construct endpoint URL - **add_route_dependencies**: Add dependencies to routes. -## TilerFactory +### TilerFactory class: `titiler.core.factory.TilerFactory` @@ -40,7 +41,7 @@ Factory meant to create endpoints for single dataset using [*rio-tiler*'s `Reade - **path_dependency**: Dependency to use to define the dataset url. Defaults to `titiler.core.dependencies.DatasetPathParams`. - **layer_dependency**: Dependency to define band indexes or expression. Defaults to `titiler.core.dependencies.BidxExprParams`. - **dataset_dependency**: Dependency to overwrite `nodata` value, apply `rescaling` and change the `I/O` or `Warp` resamplings. Defaults to `titiler.core.dependencies.DatasetParams`. -- **tile_dependency**: Dependency to defile `buffer` and `padding` to apply at tile creation. Defaults to `titiler.core.dependencies.TileParams`. +- **tile_dependency**: Dependency to define `buffer` and `padding` to apply at tile creation. Defaults to `titiler.core.dependencies.TileParams`. - **stats_dependency**: Dependency to define options for *rio-tiler*'s statistics method used in `/statistics` endpoints. Defaults to `titiler.core.dependencies.StatisticsParams`. - **histogram_dependency**: Dependency to define *numpy*'s histogram options used in `/statistics` endpoints. Defaults to `titiler.core.dependencies.HistogramParams`. - **img_preview_dependency**: Dependency to define image size for `/preview` and `/statistics` endpoints. Defaults to `titiler.core.dependencies.PreviewParams`. @@ -50,7 +51,7 @@ Factory meant to create endpoints for single dataset using [*rio-tiler*'s `Reade - **color_formula_dependency**: Dependency to define the Color Formula. Defaults to `titiler.core.dependencies.ColorFormulaParams`. - **colormap_dependency**: Dependency to define the Colormap options. Defaults to `titiler.core.dependencies.ColorMapParams` - **render_dependency**: Dependency to control output image rendering options. Defaults to `titiler.core.dependencies.ImageRenderingParams` -- **environment_dependency**: Dependency to defile GDAL environment at runtime. Default to `lambda: {}`. +- **environment_dependency**: Dependency to define GDAL environment at runtime. Default to `lambda: {}`. - **supported_tms**: List of available TileMatrixSets. Defaults to `morecantile.tms`. - **templates**: *Jinja2* templates to use in endpoints. Defaults to `titiler.core.factory.DEFAULT_TEMPLATES`. - **add_preview**: . Add `/preview` endpoint to the router. Defaults to `True`. @@ -97,7 +98,7 @@ app.include_router(cog.router) | `POST` | `/feature[/{width}x{height}][.{format}]` | image/bin | create an image from a GeoJSON feature **Optional** -## MultiBaseTilerFactory +### MultiBaseTilerFactory class: `titiler.core.factory.MultiBaseTilerFactory` @@ -143,8 +144,7 @@ app.include_router(stac.router) | `GET` | `/bbox/{minx},{miny},{maxx},{maxy}[/{width}x{height}].{format}` | image/bin | create an image from part of assets **Optional** | `POST` | `/feature[/{width}x{height}][.{format}]` | image/bin | create an image from a geojson feature intersecting assets **Optional** - -## MultiBandTilerFactory +### MultiBandTilerFactory class: `titiler.core.factory.MultiBandTilerFactory` @@ -201,56 +201,7 @@ app.include_router(landsat.router) | `POST` | `/feature[/{width}x{height}][.{format}]` | image/bin | create an image from a geojson feature **Optional** -## MosaicTilerFactory - -class: `titiler.mosaic.factory.MosaicTilerFactory` - -Endpoints factory for mosaics, built on top of [MosaicJSON](https://github.com/developmentseed/mosaicjson-spec). - -#### Attributes - -- **backend**: `cogeo_mosaic.backends.BaseBackend` Mosaic backend. Defaults to `cogeo_mosaic.backend.MosaicBackend`. -- **backend_dependency**: Dependency to control options passed to the backend instance init. Defaults to `titiler.core.dependencies.DefaultDependency` -- **dataset_reader**: Dataset Reader. Defaults to `rio_tiler.io.Reader` -- **reader_dependency**: Dependency to control options passed to the reader instance init. Defaults to `titiler.core.dependencies.DefaultDependency` -- **path_dependency**: Dependency to use to define the dataset url. Defaults to `titiler.mosaic.factory.DatasetPathParams`. -- **layer_dependency**: Dependency to define band indexes or expression. Defaults to `titiler.core.dependencies.BidxExprParams`. -- **dataset_dependency**: Dependency to overwrite `nodata` value, apply `rescaling` and change the `I/O` or `Warp` resamplings. Defaults to `titiler.core.dependencies.DatasetParams`. -- **tile_dependency**: Dependency to defile `buffer` and `padding` to apply at tile creation. Defaults to `titiler.core.dependencies.TileParams`. -- **process_dependency**: Dependency to control which `algorithm` to apply to the data. Defaults to `titiler.core.algorithm.algorithms.dependency`. -- **rescale_dependency**: Dependency to set Min/Max values to rescale from, to 0 -> 255. Defaults to `titiler.core.dependencies.RescalingParams`. -- **color_formula_dependency**: Dependency to define the Color Formula. Defaults to `titiler.core.dependencies.ColorFormulaParams`. -- **colormap_dependency**: Dependency to define the Colormap options. Defaults to `titiler.core.dependencies.ColorMapParams` -- **render_dependency**: Dependency to control output image rendering options. Defaults to `titiler.core.dependencies.ImageRenderingParams` -- **pixel_selection_dependency**: Dependency to select the `pixel_selection` method. Defaults to `titiler.mosaic.factory.PixelSelectionParams`. -- **environment_dependency**: Dependency to defile GDAL environment at runtime. Default to `lambda: {}`. -- **supported_tms**: List of available TileMatrixSets. Defaults to `morecantile.tms`. -- **supported_tms**: List of available TileMatrixSets. Defaults to `morecantile.tms`. -- **templates**: *Jinja2* templates to use in endpoints. Defaults to `titiler.core.factory.DEFAULT_TEMPLATES`. -- **optional_headers**: List of OptionalHeader which endpoints could add (if implemented). Defaults to `[]`. -- **add_viewer**: . Add `/map` endpoints to the router. Defaults to `True`. - -#### Endpoints - -| Method | URL | Output | Description -| ------ | --------------------------------------------------------------- |--------------------------------------------------- |-------------- -| `GET` | `/` | JSON [MosaicJSON][mosaic_model] | return a MosaicJSON document -| `GET` | `/bounds` | JSON ([Bounds][bounds_model]) | return mosaic's bounds -| `GET` | `/info` | JSON ([Info][mosaic_info_model]) | return mosaic's basic info -| `GET` | `/info.geojson` | GeoJSON ([InfoGeoJSON][mosaic_geojson_info_model]) | return mosaic's basic info as a GeoJSON feature -| `GET` | `/tiles` | JSON | List of OGC Tilesets available -| `GET` | `/tiles/{tileMatrixSetId}` | JSON | OGC Tileset metadata -| `GET` | `/tiles/{tileMatrixSetId}/{z}/{x}/{y}[@{scale}x][.{format}]` | image/bin | create a web map tile image from a MosaicJSON -| `GET` | `/{tileMatrixSetId}/map` | HTML | return a simple map viewer **Optional** -| `GET` | `/{tileMatrixSetId}/tilejson.json` | JSON ([TileJSON][tilejson_model]) | return a Mapbox TileJSON document -| `GET` | `/{tileMatrixSetId}/WMTSCapabilities.xml` | XML | return OGC WMTS Get Capabilities -| `GET` | `/point/{lon},{lat}` | JSON ([Point][mosaic_point]) | return pixel value from a MosaicJSON dataset -| `GET` | `/{z}/{x}/{y}/assets` | JSON | return list of assets intersecting a XYZ tile -| `GET` | `/{lon},{lat}/assets` | JSON | return list of assets intersecting a point -| `GET` | `/{minx},{miny},{maxx},{maxy}/assets` | JSON | return list of assets intersecting a bounding box - - -## TMSFactory +### TMSFactory class: `titiler.core.factory.TMSFactory` @@ -278,7 +229,7 @@ app.include_router(tms.router) | `GET` | `/tileMatrixSets/{tileMatrixSetId}` | JSON ([TileMatrixSet][tilematrixset]) | retrieve the definition of the specified tiling scheme (tile matrix set) -## AlgorithmFactory +### AlgorithmFactory class: `titiler.core.factory.AlgorithmFactory` @@ -306,7 +257,7 @@ app.include_router(algo.router) | `GET` | `/algorithms/{algorithmId}` | JSON ([Algorithm Metadata][algorithm_metadata]) | retrieve the metadata of the specified algorithm. -## ColorMapFactory +### ColorMapFactory class: `titiler.core.factory.ColorMapFactory` @@ -334,6 +285,121 @@ app.include_router(colormap.router) | `GET` | `/colorMaps/{colorMapId}` | JSON ([colorMap][colormap]) | retrieve the metadata or image of the specified colorMap. +## titiler.mosaic + +### MosaicTilerFactory + +class: `titiler.mosaic.factory.MosaicTilerFactory` + +Endpoints factory for mosaics, built on top of [MosaicJSON](https://github.com/developmentseed/mosaicjson-spec). + +#### Attributes + +- **backend**: `cogeo_mosaic.backends.BaseBackend` Mosaic backend. Defaults to `cogeo_mosaic.backend.MosaicBackend`. +- **backend_dependency**: Dependency to control options passed to the backend instance init. Defaults to `titiler.core.dependencies.DefaultDependency` +- **dataset_reader**: Dataset Reader. Defaults to `rio_tiler.io.Reader` +- **reader_dependency**: Dependency to control options passed to the reader instance init. Defaults to `titiler.core.dependencies.DefaultDependency` +- **path_dependency**: Dependency to use to define the dataset url. Defaults to `titiler.mosaic.factory.DatasetPathParams`. +- **layer_dependency**: Dependency to define band indexes or expression. Defaults to `titiler.core.dependencies.BidxExprParams`. +- **dataset_dependency**: Dependency to overwrite `nodata` value, apply `rescaling` and change the `I/O` or `Warp` resamplings. Defaults to `titiler.core.dependencies.DatasetParams`. +- **tile_dependency**: Dependency to define `buffer` and `padding` to apply at tile creation. Defaults to `titiler.core.dependencies.TileParams`. +- **process_dependency**: Dependency to control which `algorithm` to apply to the data. Defaults to `titiler.core.algorithm.algorithms.dependency`. +- **rescale_dependency**: Dependency to set Min/Max values to rescale from, to 0 -> 255. Defaults to `titiler.core.dependencies.RescalingParams`. +- **color_formula_dependency**: Dependency to define the Color Formula. Defaults to `titiler.core.dependencies.ColorFormulaParams`. +- **colormap_dependency**: Dependency to define the Colormap options. Defaults to `titiler.core.dependencies.ColorMapParams` +- **render_dependency**: Dependency to control output image rendering options. Defaults to `titiler.core.dependencies.ImageRenderingParams` +- **pixel_selection_dependency**: Dependency to select the `pixel_selection` method. Defaults to `titiler.mosaic.factory.PixelSelectionParams`. +- **environment_dependency**: Dependency to define GDAL environment at runtime. Default to `lambda: {}`. +- **supported_tms**: List of available TileMatrixSets. Defaults to `morecantile.tms`. +- **supported_tms**: List of available TileMatrixSets. Defaults to `morecantile.tms`. +- **templates**: *Jinja2* templates to use in endpoints. Defaults to `titiler.core.factory.DEFAULT_TEMPLATES`. +- **optional_headers**: List of OptionalHeader which endpoints could add (if implemented). Defaults to `[]`. +- **add_viewer**: . Add `/map` endpoints to the router. Defaults to `True`. + +#### Endpoints + +| Method | URL | Output | Description +| ------ | --------------------------------------------------------------- |--------------------------------------------------- |-------------- +| `GET` | `/` | JSON [MosaicJSON][mosaic_model] | return a MosaicJSON document +| `GET` | `/bounds` | JSON ([Bounds][bounds_model]) | return mosaic's bounds +| `GET` | `/info` | JSON ([Info][mosaic_info_model]) | return mosaic's basic info +| `GET` | `/info.geojson` | GeoJSON ([InfoGeoJSON][mosaic_geojson_info_model]) | return mosaic's basic info as a GeoJSON feature +| `GET` | `/tiles` | JSON | List of OGC Tilesets available +| `GET` | `/tiles/{tileMatrixSetId}` | JSON | OGC Tileset metadata +| `GET` | `/tiles/{tileMatrixSetId}/{z}/{x}/{y}[@{scale}x][.{format}]` | image/bin | create a web map tile image from a MosaicJSON +| `GET` | `/{tileMatrixSetId}/map` | HTML | return a simple map viewer **Optional** +| `GET` | `/{tileMatrixSetId}/tilejson.json` | JSON ([TileJSON][tilejson_model]) | return a Mapbox TileJSON document +| `GET` | `/{tileMatrixSetId}/WMTSCapabilities.xml` | XML | return OGC WMTS Get Capabilities +| `GET` | `/point/{lon},{lat}` | JSON ([Point][mosaic_point]) | return pixel value from a MosaicJSON dataset +| `GET` | `/{z}/{x}/{y}/assets` | JSON | return list of assets intersecting a XYZ tile +| `GET` | `/{lon},{lat}/assets` | JSON | return list of assets intersecting a point +| `GET` | `/{minx},{miny},{maxx},{maxy}/assets` | JSON | return list of assets intersecting a bounding box + +## titiler.xarray + +### TilerFactory + +class: `titiler.xarray.factory.TilerFactory` + +#### Attributes + +- **reader**: Dataset Reader **required**. +- **path_dependency**: Dependency to use to define the dataset url. Defaults to `titiler.core.dependencies.DatasetPathParams`. +- **reader_dependency**: Dependency to control options passed to the reader instance init. Defaults to `titiler.xarray.dependencies.XarrayParams` +- **layer_dependency**: Dependency to define band indexes or expression. Defaults to `titiler.core.dependencies.DefaultDependency`. +- **dataset_dependency**: Dependency to overwrite `nodata` value and change the `Warp` resamplings. Defaults to `titiler.xarray.dependencies.DatasetParams`. +- **tile_dependency**: Dependency for tile creation options. Defaults to `titiler.core.dependencies.DefaultDependency`. +- **stats_dependency**: Dependency to define options for *rio-tiler*'s statistics method used in `/statistics` endpoints. Defaults to `titiler.core.dependencies.StatisticsParams`. +- **histogram_dependency**: Dependency to define *numpy*'s histogram options used in `/statistics` endpoints. Defaults to `titiler.core.dependencies.HistogramParams`. +- **img_part_dependency**: Dependency to define image size for `/bbox` and `/feature` endpoints. Defaults to `titiler.xarray.dependencies.PartFeatureParams`. +- **process_dependency**: Dependency to control which `algorithm` to apply to the data. Defaults to `titiler.core.algorithm.algorithms.dependency`. +- **rescale_dependency**: Dependency to set Min/Max values to rescale from, to 0 -> 255. Defaults to `titiler.core.dependencies.RescalingParams`. +- **color_formula_dependency**: Dependency to define the Color Formula. Defaults to `titiler.core.dependencies.ColorFormulaParams`. +- **colormap_dependency**: Dependency to define the Colormap options. Defaults to `titiler.core.dependencies.ColorMapParams` +- **render_dependency**: Dependency to control output image rendering options. Defaults to `titiler.core.dependencies.ImageRenderingParams` +- **environment_dependency**: Dependency to define GDAL environment at runtime. Default to `lambda: {}`. +- **supported_tms**: List of available TileMatrixSets. Defaults to `morecantile.tms`. +- **templates**: *Jinja2* templates to use in endpoints. Defaults to `titiler.core.factory.DEFAULT_TEMPLATES`. +- **add_part**: . Add `/bbox` and `/feature` endpoints to the router. Defaults to `True`. +- **add_viewer**: . Add `/map` endpoints to the router. Defaults to `True`. + + +```python +from fastapi import FastAPI + +from titiler.xarray.factory import TilerFactory + +# Create FastAPI application +app = FastAPI() + +# Create router and register set of endpoints +md = TilerFactory( + add_part=True, + add_viewer=True, +) + +# add router endpoint to the main application +app.include_router(md.router) +``` + +#### Endpoints + +| Method | URL | Output | Description +| ------ | --------------------------------------------------------------- |-------------------------------------------- |-------------- +| `GET` | `/bounds` | JSON ([Bounds][bounds_model]) | return dataset's bounds +| `GET` | `/info` | JSON ([Info][info_model]) | return dataset's basic info +| `GET` | `/info.geojson` | GeoJSON ([InfoGeoJSON][info_geojson_model]) | return dataset's basic info as a GeoJSON feature +| `POST` | `/statistics` | GeoJSON ([Statistics][stats_geojson_model]) | return dataset's statistics for a GeoJSON +| `GET` | `/tiles` | JSON | List of OGC Tilesets available +| `GET` | `/tiles/{tileMatrixSetId}` | JSON | OGC Tileset metadata +| `GET` | `/tiles/{tileMatrixSetId}/{z}/{x}/{y}[@{scale}x][.{format}]` | image/bin | create a web map tile image from a dataset +| `GET` | `/{tileMatrixSetId}/map` | HTML | return a simple map viewer **Optional** +| `GET` | `/{tileMatrixSetId}/tilejson.json` | JSON ([TileJSON][tilejson_model]) | return a Mapbox TileJSON document +| `GET` | `/{tileMatrixSetId}/WMTSCapabilities.xml` | XML | return OGC WMTS Get Capabilities +| `GET` | `/point/{lon},{lat}` | JSON ([Point][point_model]) | return pixel values from a dataset +| `GET` | `/bbox/{minx},{miny},{maxx},{maxy}[/{width}x{height}].{format}` | image/bin | create an image from part of a dataset **Optional** +| `POST` | `/feature[/{width}x{height}][.{format}]` | image/bin | create an image from a GeoJSON feature **Optional** + [bounds_model]: https://github.com/cogeotiff/rio-tiler/blob/9aaa88000399ee8d36e71d176f67b6ea3ec53f2d/rio_tiler/models.py#L43-L46 [info_model]: https://github.com/cogeotiff/rio-tiler/blob/9aaa88000399ee8d36e71d176f67b6ea3ec53f2d/rio_tiler/models.py#L56-L72 diff --git a/docs/src/api/titiler/xarray/dependencies.md b/docs/src/api/titiler/xarray/dependencies.md new file mode 100644 index 000000000..6bb4bf4a6 --- /dev/null +++ b/docs/src/api/titiler/xarray/dependencies.md @@ -0,0 +1 @@ +::: titiler.xarray.dependencies diff --git a/docs/src/api/titiler/xarray/extensions.md b/docs/src/api/titiler/xarray/extensions.md new file mode 100644 index 000000000..e6b41dc30 --- /dev/null +++ b/docs/src/api/titiler/xarray/extensions.md @@ -0,0 +1 @@ +::: titiler.xarray.extensions diff --git a/docs/src/api/titiler/xarray/factory.md b/docs/src/api/titiler/xarray/factory.md new file mode 100644 index 000000000..74f2363b4 --- /dev/null +++ b/docs/src/api/titiler/xarray/factory.md @@ -0,0 +1 @@ +::: titiler.xarray.factory diff --git a/docs/src/api/titiler/xarray/io.md b/docs/src/api/titiler/xarray/io.md new file mode 100644 index 000000000..37ccfec84 --- /dev/null +++ b/docs/src/api/titiler/xarray/io.md @@ -0,0 +1 @@ +::: titiler.xarray.io diff --git a/src/titiler/xarray/README.md b/src/titiler/xarray/README.md index 143133544..23b912c39 100644 --- a/src/titiler/xarray/README.md +++ b/src/titiler/xarray/README.md @@ -8,29 +8,43 @@ Adds support for Xarray Dataset (NetCDF/Zarr) in Titiler. $ python -m pip install -U pip # From Pypi -$ python -m pip install titiler.xarray +$ python -m pip install "titiler.xarray[all]" # 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 +$ cd titiler && python -m pip install -e src/titiler/core -e "src/titiler/xarray[all]" ``` ## How To ```python from fastapi import FastAPI + +from titiler.xarray.extensions import VariablesExtension from titiler.xarray.factory import TilerFactory -# Create a FastAPI application app = FastAPI( - description="A lightweight Cloud Optimized GeoTIFF tile server", -) + openapi_url="/api", + docs_url="/api.html", + description="""Xarray based tiles server for MultiDimensional dataset (Zarr/NetCDF). + +--- + +**Documentation**: https://developmentseed.org/titiler/ -# Create a set of MosaicJSON endpoints -endpoint = TilerFactory() +**Source Code**: https://github.com/developmentseed/titiler -# Register the Mosaic endpoints to the application -app.include_router(endpoint.router) +--- + """, +) + +md = TilerFactory( + router_prefix="/md", + extensions=[ + VariablesExtension(), + ], +) +app.include_router(md.router, prefix="/md", tags=["Multi Dimensional"]) ``` ## Package structure @@ -41,6 +55,34 @@ titiler/ ├── tests/ - Tests suite └── titiler/xarray/ - `xarray` namespace package ├── dependencies.py - titiler-xarray dependencies + ├── extentions.py - titiler-xarray extensions ├── io.py - titiler-xarray Readers └── factory.py - endpoints factory ``` + +## Custom Dataset Opener + +A default Dataset IO is provided within `titiler.xarray.Reader` class but will require optional dependencies (fsspec, zarr, h5netcdf, ...) to be installed with `python -m pip install "titiler.xarray[all]"`. +Dependencies are optional so the entire package size can be optimized to only include dependencies required by a given application. + +```python +from titiler.xarray.io import Reader + +import xarray +import h5netcdf # noqa + +with Reader( + "tests/fixtures/dataset_2d.nc", + "dataset", + opener=xarray.open_dataset, +) as src: + print(src.ds) + +>>> Size: 16MB +Dimensions: (x: 2000, y: 1000) +Coordinates: + * x (x) float64 16kB -170.0 -169.8 -169.7 -169.5 ... 169.5 169.7 169.8 + * y (y) float64 8kB -80.0 -79.84 -79.68 -79.52 ... 79.52 79.68 79.84 +Data variables: + dataset (y, x) float64 16MB ... +``` diff --git a/src/titiler/xarray/notebooks/xarray_dataset_cache.ipynb b/src/titiler/xarray/notebooks/xarray_dataset_cache.ipynb new file mode 100644 index 000000000..23e87b7a4 --- /dev/null +++ b/src/titiler/xarray/notebooks/xarray_dataset_cache.ipynb @@ -0,0 +1,168 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Add Caching Layer for Xarray Dataset" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "import pickle\n", + "from typing import Any, Callable, List, Optional\n", + "\n", + "import attr\n", + "import xarray\n", + "from morecantile import TileMatrixSet\n", + "from rio_tiler.constants import WEB_MERCATOR_TMS\n", + "from rio_tiler.io.xarray import XarrayReader\n", + "\n", + "from titiler.xarray.io import xarray_open_dataset, get_variable\n", + "\n", + "from diskcache import Cache\n", + "\n", + "cache_client = Cache()\n", + "\n", + "\n", + "@attr.s\n", + "class CustomReader(XarrayReader):\n", + " \"\"\"Reader: Open Zarr file and access DataArray.\"\"\"\n", + "\n", + " src_path: str = attr.ib()\n", + " variable: str = attr.ib()\n", + "\n", + " # xarray.Dataset options\n", + " opener: Callable[..., xarray.Dataset] = attr.ib(default=xarray_open_dataset)\n", + "\n", + " group: Optional[Any] = attr.ib(default=None)\n", + " decode_times: bool = attr.ib(default=False)\n", + "\n", + " # xarray.DataArray options\n", + " datetime: Optional[str] = attr.ib(default=None)\n", + " drop_dim: Optional[str] = attr.ib(default=None)\n", + "\n", + " tms: TileMatrixSet = attr.ib(default=WEB_MERCATOR_TMS)\n", + "\n", + " ds: xarray.Dataset = attr.ib(init=False)\n", + " input: xarray.DataArray = attr.ib(init=False)\n", + "\n", + " _dims: List = attr.ib(init=False, factory=list)\n", + "\n", + " def __attrs_post_init__(self):\n", + " \"\"\"Set bounds and CRS.\"\"\"\n", + " ds = None\n", + " # Generate cache key and attempt to fetch the dataset from cache\n", + " cache_key = f\"{self.src_path}_group:{self.group}_time:{self.decode_times}\"\n", + " data_bytes = cache_client.get(cache_key)\n", + " if data_bytes:\n", + " print(f\"Found dataset in Cache {cache_key}\")\n", + " ds = pickle.loads(data_bytes)\n", + "\n", + " self.ds = ds or self.opener(\n", + " self.src_path,\n", + " group=self.group,\n", + " decode_times=self.decode_times,\n", + " )\n", + " if not ds:\n", + " # Serialize the dataset to bytes using pickle\n", + " cache_key = f\"{self.src_path}_group:{self.group}_time:{self.decode_times}\"\n", + " data_bytes = pickle.dumps(self.ds)\n", + " print(f\"Adding dataset in Cache: {cache_key}\")\n", + " cache_client.set(cache_key, data_bytes, tag=\"data\", expire=300)\n", + "\n", + " self.input = get_variable(\n", + " self.ds,\n", + " self.variable,\n", + " datetime=self.datetime,\n", + " drop_dim=self.drop_dim,\n", + " )\n", + " super().__attrs_post_init__()\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Adding dataset in Cache: ../tests/fixtures/dataset_2d.nc_group:None_time:False\n", + " Size: 16MB\n", + "Dimensions: (x: 2000, y: 1000)\n", + "Coordinates:\n", + " * x (x) float64 16kB -170.0 -169.8 -169.7 -169.5 ... 169.5 169.7 169.8\n", + " * y (y) float64 8kB -80.0 -79.84 -79.68 -79.52 ... 79.52 79.68 79.84\n", + "Data variables:\n", + " dataset (y, x) float64 16MB ...\n" + ] + }, + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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bHH5/X6de+65ksR1XWdB/fSM+ukZ9XN21FHPeqqfeFtap/F7XAAAqxBcAACrWBtcgUBAK1eu5KLlr0H99Iz66Rn1c3bUUc8Gsn6snJwBAHCXtS0uAICbCC4AgFUILgCAVQguAIBVrAyup59+Wueff77OOeccFRUV6e233051kVLiZz/7mXw+X8wyefLk6PqTJ0+qsrJSo0eP1siRI1VRUXHGj3h6zc6dO3XdddcpPz9fPp9PL730Usx6Y4wefvhh5eXladiwYSopKdHHH38cs83Ro0e1ePFiZWRkKCsrS7fffruOHz+exL0YOGern1tvvfWMz1RZWVnMNl6tn9raWs2aNUujRo1STk6O5s+fr4aGhphtevOdampq0rx58zR8+HDl5OTo3nvvVWdnZzJ3ZcD0po6uvPLKMz5Dd9xxR8w2/a0j64LrL3/5i6qrq7Vy5Uq99957mjFjhkpLS2N+mNIlF110kZqbm6PL66+/Hl1311136a9//as2bNigHTt26NNPP9XChQtTWNqBd+LECc2YMUNPP/10t+tXr16tJ598UmvWrNGuXbs0YsQIlZaW6uTJk9FtFi9erA8++EBbtmzRpk2btHPnTi1btixZuzCgzlY/klRWVhbzmXr++edj1nu1fnbs2KHKykq99dZb2rJlizo6OjR37lydOHEius3ZvlNdXV2aN2+eTp06pTfffFPPPvus1q1bp4cffjgVu5RwvakjSVq6dGnMZ2j16tXRdQmpI2OZSy+91FRWVkYfd3V1mfz8fFNbW5vCUqXGypUrzYwZM7pd19raaoYOHWo2bNgQfe7DDz80kkxdXV2SSphakszGjRujjyORiAkGg+ZXv/pV9LnW1lYTCATM888/b4wxZt++fUaSeeedd6LbvPLKK8bn85n//Oc/SSt7Mny1fowxZsmSJeb666//2te4VD+HDh0yksyOHTuMMb37Tv397383fr/fhEKh6DbPPPOMycjIMO3t7cndgST4ah0ZY8z3vvc985Of/ORrX5OIOrKqxXXq1CnV19erpKQk+pzf71dJSYnq6upSWLLU+fjjj5Wfn68JEyZo8eLFampqkiTV19ero6Mjpq4mT56scePGOVtXBw4cUCgUiqmTzMxMFRUVReukrq5OWVlZuuSSS6LblJSUyO/3a9euXUkvcyps375dOTk5uuCCC7R8+XIdOXIkus6l+mlra5MkZWdnS+rdd6qurk7Tpk1Tbm5udJvS0lKFw2F98MEHSSx9cny1jk577rnnNGbMGE2dOlU1NTX6/PPPo+sSUUdW3WT3s88+U1dXV8wOS1Jubq4++uijFJUqdYqKirRu3TpdcMEFam5u1qpVq3TFFVdo7969CoVCSk9PV1ZWVsxrcnNzFQqFUlPgFDu93919fk6vC4VCysnJiVk/ZMgQZWdnO1FvZWVlWrhwoQoLC7V//3498MADKi8vV11dndLS0pypn0gkojvvvFOXX365pk6dKkm9+k6FQqFuP1+n13lJd3UkSTfddJPGjx+v/Px87dmzR/fff78aGhr04osvSkpMHVkVXIhVXl4e/ff06dNVVFSk8ePH64UXXtCwYcNSWDLYatGiRdF/T5s2TdOnT9fEiRO1fft2zZkzJ4UlS67Kykrt3bs3ZswYsb6ujr483jlt2jTl5eVpzpw52r9/vyZOnJiQv21VV+GYMWOUlpZ2xiyelpYWBYPBFJVq8MjKytK3vvUtNTY2KhgM6tSpU2ptbY3ZxuW6Or3fPX1+gsHgGRN9Ojs7dfToUSfrbcKECRozZowaGxsluVE/VVVV2rRpk1577TWdd9550ed7850KBoPdfr5Or/OKr6uj7hQVFUlSzGeov3VkVXClp6dr5syZ2rp1a/S5SCSirVu3qri4OIUlGxyOHz+u/fv3Ky8vTzNnztTQoUNj6qqhoUFNTU3O1lVhYaGCwWBMnYTDYe3atStaJ8XFxWptbVV9fX10m23btikSiUS/gC755JNPdOTIEeXl5Unydv0YY1RVVaWNGzdq27ZtKiwsjFnfm+9UcXGx/vnPf8aE+5YtW5SRkaEpU6YkZ0cG0NnqqDu7d++WpJjPUL/rqI+TSVLmz3/+swkEAmbdunVm3759ZtmyZSYrKytmhoor7r77brN9+3Zz4MAB88Ybb5iSkhIzZswYc+jQIWOMMXfccYcZN26c2bZtm3n33XdNcXGxKS4uTnGpB9axY8fM+++/b95//30jyfz6178277//vvn3v/9tjDHml7/8pcnKyjIvv/yy2bNnj7n++utNYWGh+eKLL6LvUVZWZr797W+bXbt2mddff91MmjTJ3HjjjanapYTqqX6OHTtm7rnnHlNXV2cOHDhgXn31VfOd73zHTJo0yZw8eTL6Hl6tn+XLl5vMzEyzfft209zcHF0+//zz6DZn+051dnaaqVOnmrlz55rdu3ebzZs3m7Fjx5qamppU7FLCna2OGhsbzSOPPGLeffddc+DAAfPyyy+bCRMmmNmzZ0ffIxF1ZF1wGWPMU089ZcaNG2fS09PNpZdeat56661UFyklbrjhBpOXl2fS09PNueeea2644QbT2NgYXf/FF1+YH//4x+Yb3/iGGT58uFmwYIFpbm5OYYkH3muvvWYknbEsWbLEGPPfKfEPPfSQyc3NNYFAwMyZM8c0NDTEvMeRI0fMjTfeaEaOHGkyMjLMbbfdZo4dO5aCvUm8nurn888/N3PnzjVjx441Q4cONePHjzdLly4946TQq/XTXb1IMmvXro1u05vv1L/+9S9TXl5uhg0bZsaMGWPuvvtu09HRkeS9GRhnq6OmpiYze/Zsk52dbQKBgPnmN79p7r33XtPW1hbzPv2tI37WBABgFavGuAAAILgAAFYhuAAAViG4AABWIbgAAFYhuAAAViG4AABWIbgAAFYhuAAAViG4AABWIbgAAFYhuAAAVvn/DbXKOsc2f2sAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import matplotlib.pyplot as plt\n", + "\n", + "with CustomReader(\"../tests/fixtures/dataset_2d.nc\", \"dataset\") as src:\n", + " print(src.ds)\n", + " tile = src.tms.tile(src.bounds[0], src.bounds[1], src.minzoom)\n", + " img = src.tile(*tile)\n", + "\n", + "plt.imshow(img.data_as_image())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "py39", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.19" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/src/titiler/xarray/pyproject.toml b/src/titiler/xarray/pyproject.toml index 87bfc2ab4..a66187828 100644 --- a/src/titiler/xarray/pyproject.toml +++ b/src/titiler/xarray/pyproject.toml @@ -30,6 +30,7 @@ classifiers = [ dynamic = ["version"] dependencies = [ "titiler.core==0.19.0.dev", + "rio-tiler>=7.2,<8.0", "xarray", "rioxarray", ] diff --git a/src/titiler/xarray/titiler/xarray/io.py b/src/titiler/xarray/titiler/xarray/io.py index ed061e24d..39fe83552 100644 --- a/src/titiler/xarray/titiler/xarray/io.py +++ b/src/titiler/xarray/titiler/xarray/io.py @@ -1,7 +1,6 @@ """titiler.xarray.io""" -import pickle -from typing import Any, Callable, Dict, List, Optional, Protocol +from typing import Any, Callable, Dict, List, Optional from urllib.parse import urlparse import attr @@ -12,36 +11,16 @@ from rio_tiler.io.xarray import XarrayReader -class CacheClient(Protocol): - """CacheClient Protocol.""" - - def get(self, key: str) -> bytes: - """Get key.""" - ... - - def set(self, key: str, body: bytes) -> None: - """Set key.""" - ... - - def xarray_open_dataset( # noqa: C901 src_path: str, group: Optional[Any] = None, decode_times: Optional[bool] = True, - cache_client: Optional[CacheClient] = None, ) -> xarray.Dataset: """Open dataset.""" import aiohttp # noqa import fsspec # noqa import s3fs # noqa - # Generate cache key and attempt to fetch the dataset from cache - if cache_client: - cache_key = f"{src_path}_{group}" if group is not None else src_path - data_bytes = cache_client.get(cache_key) - if data_bytes: - return pickle.loads(data_bytes) - parsed = urlparse(src_path) protocol = parsed.scheme or "file" if protocol not in ["s3", "https", "http", "file"]: @@ -102,11 +81,6 @@ def xarray_open_dataset( # noqa: C901 ds = xarray.open_zarr(file_handler, **xr_open_args) - if cache_client: - # Serialize the dataset to bytes using pickle - data_bytes = pickle.dumps(ds) - cache_client.set(cache_key, data_bytes) - return ds @@ -219,7 +193,6 @@ class Reader(XarrayReader): group: Optional[Any] = attr.ib(default=None) decode_times: bool = attr.ib(default=False) - cache_client: Optional[CacheClient] = attr.ib(default=None) # xarray.DataArray options datetime: Optional[str] = attr.ib(default=None) @@ -238,7 +211,6 @@ def __attrs_post_init__(self): self.src_path, group=self.group, decode_times=self.decode_times, - cache_client=self.cache_client, ) self.input = get_variable( diff --git a/src/titiler/xarray/titiler/xarray/main.py b/src/titiler/xarray/titiler/xarray/main.py deleted file mode 100644 index 4fafaec01..000000000 --- a/src/titiler/xarray/titiler/xarray/main.py +++ /dev/null @@ -1,36 +0,0 @@ -"""titiler xarray app.""" - -from fastapi import FastAPI - -from titiler.xarray.extensions import VariablesExtension -from titiler.xarray.factory import TilerFactory - -############################################################################### - -app = FastAPI( - openapi_url="/api", - docs_url="/api.html", - description="""Xarray based tiles server for MultiDimensional dataset (Zarr/NetCDF). - ---- - -**Documentation**: https://developmentseed.org/titiler/ - -**Source Code**: https://github.com/developmentseed/titiler - ---- - """, -) - -md = TilerFactory( - router_prefix="/md", - extensions=[ - VariablesExtension(), - ], -) - -app.include_router( - md.router, - prefix="/md", - tags=["Multi Dimensional"], -)