-
Notifications
You must be signed in to change notification settings - Fork 10
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
use factory from titiler.xarray #72
base: dev
Are you sure you want to change the base?
Changes from 3 commits
039b8c8
4bfe7cb
4f443aa
b92e068
74d46b9
6d51287
a389a45
144e9b2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
"""titiler.xarray_api""" | ||
|
||
__version__ = "0.3.0" |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
"""TiTiler.xarray factory.""" | ||
|
||
from typing import List, Literal, Optional, Type, Union | ||
from urllib.parse import urlencode | ||
|
||
import jinja2 | ||
import numpy as np | ||
from attrs import define | ||
from fastapi import Depends, Query | ||
from starlette.requests import Request | ||
from starlette.responses import HTMLResponse | ||
from starlette.templating import Jinja2Templates | ||
from typing_extensions import Annotated | ||
|
||
from titiler.core.dependencies import ColorFormulaParams, DefaultDependency | ||
from titiler.core.resources.enums import ImageType | ||
from titiler.core.resources.responses import JSONResponse | ||
from titiler.xarray.dependencies import XarrayIOParams, XarrayParams | ||
from titiler.xarray.factory import TilerFactory as BaseTilerFactory | ||
from titiler.xarray_api.reader import XarrayReader | ||
|
||
|
||
def nodata_dependency( | ||
nodata: Annotated[ | ||
Optional[Union[str, int, float]], | ||
Query( | ||
title="Nodata value", | ||
description="Overwrite internal Nodata value", | ||
), | ||
] = None, | ||
) -> Optional[float]: | ||
"""Nodata dependency.""" | ||
if nodata is not None: | ||
nodata = np.nan if nodata == "nan" else float(nodata) | ||
|
||
return None | ||
hrodmn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
|
||
@define(kw_only=True) | ||
class XarrayTilerFactory(BaseTilerFactory): | ||
"""Xarray Tiler Factory.""" | ||
|
||
reader: Type[XarrayReader] = XarrayReader | ||
reader_dependency: Type[DefaultDependency] = XarrayParams | ||
|
||
def register_routes(self) -> None: # noqa: C901 | ||
"""Register Info / Tiles / TileJSON endoints.""" | ||
super().register_routes() | ||
self.variables() | ||
|
||
def variables(self) -> None: | ||
"""Register /variables endpoint""" | ||
|
||
@self.router.get( | ||
"/variables", | ||
response_class=JSONResponse, | ||
responses={200: {"description": "Return dataset's Variables."}}, | ||
) | ||
def get_variables( | ||
src_path=Depends(self.path_dependency), | ||
io_params=Depends(XarrayIOParams), | ||
) -> List[str]: | ||
"""return available variables.""" | ||
return XarrayReader.list_variables( | ||
hrodmn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
src_path=src_path, | ||
group=io_params.group, | ||
decode_times=io_params.decode_times, | ||
) | ||
|
||
def statistics(self) -> None: | ||
"""Register /statistics and /histogram endpoints""" | ||
super().statistics() | ||
|
||
@self.router.get( | ||
"/histogram", | ||
response_class=JSONResponse, | ||
responses={200: {"description": "Return histogram for this data variable"}}, | ||
response_model_exclude_none=True, | ||
) | ||
def histogram( | ||
src_path=Depends(self.path_dependency), | ||
reader_params=Depends(self.reader_dependency), | ||
): | ||
with self.reader( | ||
src_path=src_path, | ||
variable=reader_params.variable, | ||
group=reader_params.group, | ||
decode_times=reader_params.decode_times, | ||
datetime=reader_params.datetime, | ||
) as src_dst: | ||
boolean_mask = ~np.isnan(src_dst.input) | ||
data_values = src_dst.input.values[boolean_mask] | ||
counts, values = np.histogram(data_values, bins=10) | ||
counts, values = counts.tolist(), values.tolist() | ||
buckets = list( | ||
zip(values, [values[i + 1] for i in range(len(values) - 1)]) | ||
) | ||
hist_dict = [] | ||
for idx, bucket in enumerate(buckets): | ||
hist_dict.append({"bucket": bucket, "value": counts[idx]}) | ||
return hist_dict | ||
|
||
def map_viewer(self) -> None: | ||
"""Register /map endpoints""" | ||
|
||
@self.router.get("/{tileMatrixSetId}/map", response_class=HTMLResponse) | ||
def map_viewer( | ||
request: Request, | ||
tileMatrixSetId: Annotated[ # type: ignore | ||
Literal[tuple(self.supported_tms.list())], | ||
"Identifier selecting one of the supported TileMatrixSetIds", | ||
], | ||
url: Annotated[Optional[str], Query(description="Dataset URL")] = None, | ||
variable: Annotated[ | ||
Optional[str], | ||
Query(description="Xarray Variable"), | ||
] = None, | ||
group: Annotated[ | ||
Optional[int], | ||
Query( | ||
description="Select a specific zarr group from a zarr hierarchy, can be for pyramids or datasets. Can be used to open a dataset in HDF5 files." | ||
), | ||
] = None, | ||
decode_times: Annotated[ | ||
bool, | ||
Query( | ||
title="decode_times", | ||
description="Whether to decode times", | ||
), | ||
] = True, | ||
drop_dim: Annotated[ | ||
Optional[str], | ||
Query(description="Dimension to drop"), | ||
] = None, | ||
datetime: Annotated[ | ||
Optional[str], Query(description="Slice of time to read (if available)") | ||
] = None, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is within the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yes but I need the when no There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes but not if it's only for a specific endpoint, you'll need to hardcode things directly at the endpoint level I wasn't talking about the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah it might make more sense to have two endpoints like |
||
tile_format: Annotated[ | ||
Optional[ImageType], | ||
Query( | ||
description="Default will be automatically defined if the output image needs a mask (png) or not (jpeg).", | ||
), | ||
] = None, | ||
tile_scale: Annotated[ | ||
int, | ||
Query( | ||
gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..." | ||
), | ||
] = 1, | ||
minzoom: Annotated[ | ||
Optional[int], | ||
Query(description="Overwrite default minzoom."), | ||
] = None, | ||
maxzoom: Annotated[ | ||
Optional[int], | ||
Query(description="Overwrite default maxzoom."), | ||
] = None, | ||
post_process=Depends(self.process_dependency), | ||
rescale=Depends(self.rescale_dependency), | ||
color_formula=Depends(ColorFormulaParams), | ||
colormap=Depends(self.colormap_dependency), | ||
render_params=Depends(self.render_dependency), | ||
nodata=Depends(nodata_dependency), | ||
hrodmn marked this conversation as resolved.
Show resolved
Hide resolved
|
||
): | ||
"""Return map Viewer.""" | ||
jinja2_env = jinja2.Environment( | ||
loader=jinja2.ChoiceLoader([jinja2.PackageLoader(__package__, ".")]) | ||
) | ||
templates = Jinja2Templates(env=jinja2_env) | ||
|
||
if url: | ||
tilejson_url = self.url_for( | ||
request, "tilejson", tileMatrixSetId=tileMatrixSetId | ||
) | ||
if request.query_params._list: | ||
tilejson_url += f"?{urlencode(request.query_params._list)}" | ||
|
||
tms = self.supported_tms.get(tileMatrixSetId) | ||
return templates.TemplateResponse( | ||
name="map.html", | ||
context={ | ||
"request": request, | ||
"tilejson_endpoint": tilejson_url, | ||
"tms": tms, | ||
"resolutions": [matrix.cellSize for matrix in tms], | ||
}, | ||
media_type="text/html", | ||
) | ||
else: | ||
return templates.TemplateResponse( | ||
name="map-form.html", | ||
context={ | ||
"request": request, | ||
}, | ||
media_type="text/html", | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
?