Skip to content
Open
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
1 change: 1 addition & 0 deletions src/titiler/application/titiler/application/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ def validate_access_token(access_token: str = Security(api_key_query)):
reader=STACReader,
router_prefix="/stac",
add_ogc_maps=True,
get_renders=lambda obj: obj.item.properties.get("renders", {}), # type: ignore
extensions=[stacViewerExtension(), stacRenderExtension(), wmtsExtension()],
enable_telemetry=api_settings.telemetry_enabled,
templates=titiler_templates,
Expand Down
4 changes: 2 additions & 2 deletions src/titiler/core/titiler/core/algorithm/dem.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ def __call__(self, img: ImageData) -> ImageData:
"""Encode DEM into RGB."""
data = numpy.clip(img.array[0] + 32768.0, 0.0, 65535.0)
if self.nodata_height is not None:
data[img.array.mask[0]] = numpy.clip( # type: ignore [index]
data[img.array.mask[0]] = numpy.clip( # type: ignore [call-overload]
self.nodata_height + 32768.0, 0.0, 65535.0
)
r = data / 256
Expand Down Expand Up @@ -239,7 +239,7 @@ def _range_check(datarange):
raise ValueError(f"Data of {datarange} larger than 256 ** 3")

if self.nodata_height is not None:
data[img.array.mask[0]] = ( # type: ignore [index]
data[img.array.mask[0]] = ( # type: ignore [call-overload]
self.nodata_height - self.baseval
) / self.interval

Expand Down
20 changes: 19 additions & 1 deletion src/titiler/core/titiler/core/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,10 @@ class TilerFactory(BaseFactory):

render_func: Callable[..., tuple[bytes, str]] = render_image

get_renders: Callable[[BaseReader], dict[str, dict[str, Any]]] = field(
default=lambda obj: {}
)

# Add/Remove some endpoints
add_preview: bool = True
add_part: bool = True
Expand Down Expand Up @@ -1015,7 +1019,7 @@ def tilejson(
with self.reader(
src_path, tms=tms, **reader_params.as_dict()
) as src_dst:
return {
body = {
"bounds": src_dst.get_geographic_bounds(
tms.rasterio_geographic_crs
),
Expand All @@ -1025,6 +1029,16 @@ def tilejson(
"attribution": os.environ.get("TITILER_DEFAULT_ATTRIBUTION"),
}

# Custom TiTiler tilejson fields
body["raster_layers"] = self.get_renders(src_dst)

info = src_dst.info()
body["band_descriptions"] = getattr(info, "band_descriptions", None)
body["data_type"] = getattr(info, "dtype", None)
body["minmax"] = getattr(info, "minmax", None)

return body

def map_viewer(self): # noqa: C901
"""Register /map.html endpoint."""

Expand Down Expand Up @@ -1472,6 +1486,10 @@ class MultiBaseTilerFactory(TilerFactory):
# Assets dependency
assets_dependency: type[DefaultDependency] = AssetsParams

get_renders: Callable[[MultiBaseReader], dict[str, dict[str, Any]]] = field( # type: ignore
default=lambda obj: {}
)

# Overwrite the `/info` endpoint to return the list of assets when no assets is passed.
def info(self):
"""Register /info endpoint."""
Expand Down
10 changes: 10 additions & 0 deletions src/titiler/core/titiler/core/models/mapbox.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,13 @@ def compute_center(self):
self.minzoom,
)
return self


class TiTilerJSON(TileJSON):
"""TiTiler custom TileJSON model."""

raster_layers: list[dict] | None = None

band_descriptions: list[tuple[str, str]] | None = None
dtype: str | None = None
minmax: list[tuple[float, float]] | None = None
4 changes: 3 additions & 1 deletion src/titiler/extensions/tests/test_stac_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ def test_stacExtension():
stac_tiler = MultiBaseTilerFactory(reader=STACReader)

stac_tiler_plus_stac_render = MultiBaseTilerFactory(
reader=STACReader, extensions=[stacRenderExtension(), wmtsExtension()]
reader=STACReader,
extensions=[stacRenderExtension(), wmtsExtension()],
get_renders=lambda obj: obj.item.properties.get("renders", {}),
)
# Check that we added two routes (/renders & /renders/{render_id}) and `/WMTSCapabilities.xml`
assert (
Expand Down
31 changes: 14 additions & 17 deletions src/titiler/extensions/tests/test_wmts.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,24 +118,21 @@ def test_wmtsExtension():
def test_wmtsExtension_with_renders():
"""Test wmtsExtension class with Renders."""
tiler_plus_wmts = TilerFactory(
extensions=[
wmtsExtension(
get_renders=lambda obj: {
"one_band_limit": {
"tilematrixsets": {
"WebMercatorQuad": [0, 1],
},
"spatial_extent": (-180, -90, 180, 90),
"bidx": [1, 2, 3],
"rescale": [[0, 10000], [0, 5000], [0, 1000]],
},
"one_band": {
"bidx": [1],
"rescale": ["0,10000"],
},
get_renders=lambda obj: {
"one_band_limit": {
"tilematrixsets": {
"WebMercatorQuad": [0, 1],
},
)
],
"spatial_extent": (-180, -90, 180, 90),
"bidx": [1, 2, 3],
"rescale": [[0, 10000], [0, 5000], [0, 1000]],
},
"one_band": {
"bidx": [1],
"rescale": ["0,10000"],
},
},
extensions=[wmtsExtension()],
)

app = FastAPI()
Expand Down
4 changes: 2 additions & 2 deletions src/titiler/extensions/titiler/extensions/render.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ def _prepare_render_item(
)
def render_list(request: Request, src_path=Depends(factory.path_dependency)):
with factory.reader(src_path) as src:
renders = src.item.properties.get("renders", {})
renders = factory.get_renders(src)

prepared_renders = {
render_id: _prepare_render_item(render_id, render, request, src_path)
Expand Down Expand Up @@ -201,7 +201,7 @@ def render(
src_path=Depends(factory.path_dependency),
):
with factory.reader(src_path) as src:
renders = src.item.properties.get("renders", {})
renders = factory.get_renders(src)

if render_id not in renders:
raise HTTPException(status_code=404, detail="Render not found")
Expand Down
21 changes: 18 additions & 3 deletions src/titiler/extensions/titiler/extensions/wmts.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,31 @@ class wmtsExtension(FactoryExtension):

templates: Jinja2Templates = field(default=DEFAULT_TEMPLATES)

get_renders: Callable[[BaseReader], dict[str, dict[str, Any]]] = field(
default=lambda obj: {}
# TODO: Remove in 3.0
get_renders: Callable[[BaseReader], dict[str, dict[str, Any]]] | None = field(
default=None
)

# List of dependencies a `/tile` URL should validate
# Note: Those dependencies should only require Query() inputs
tile_dependencies: list[Callable] | None = field(default=None)

# TODO: Remove in 3.0
def __attrs_post_init__(self):
"""Warn about deprecation of `get_renders` attribute."""
if self.get_renders:
warnings.warn(
"The wmtsExtension's `get_renders` attribute is deprecated and will be ignored. Please set it at the factory level.",
DeprecationWarning,
stacklevel=2,
)

def register(self, factory: TilerFactory): # type: ignore [override] # noqa: C901
"""Register extension's endpoints."""

# TODO: Remove in 3.0
self.get_renders = factory.get_renders

tile_dependencies = (
self.tile_dependencies
if self.tile_dependencies is not None
Expand Down Expand Up @@ -105,7 +120,7 @@ def wmts( # noqa: C901
with rasterio.Env(**env):
with factory.reader(src_path, **reader_params.as_dict()) as src_dst:
dataset_bounds = src_dst.get_geographic_bounds(self.crs)
default_renders = self.get_renders(src_dst)
default_renders = factory.get_renders(src_dst)

renders: list[dict[str, Any]] = []

Expand Down
31 changes: 14 additions & 17 deletions src/titiler/mosaic/tests/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -813,24 +813,21 @@ def test_wmts_extension_mosaic():
"""Test wmtsExtension extension."""
mosaic = MosaicTilerFactory(
backend=MosaicJSONBackend,
extensions=[
wmtsExtension(
get_renders=lambda obj: {
"one_band_limit": {
"tilematrixsets": {
"WebMercatorQuad": [0, 1],
},
"spatial_extent": (-180, -90, 180, 90),
"bidx": [1, 2, 3],
"rescale": [[0, 10000], [0, 5000], [0, 1000]],
},
"one_band": {
"bidx": [1],
"rescale": ["0,10000"],
},
get_renders=lambda obj: {
"one_band_limit": {
"tilematrixsets": {
"WebMercatorQuad": [0, 1],
},
)
],
"spatial_extent": (-180, -90, 180, 90),
"bidx": [1, 2, 3],
"rescale": [[0, 10000], [0, 5000], [0, 1000]],
},
"one_band": {
"bidx": [1],
"rescale": ["0,10000"],
},
},
extensions=[wmtsExtension()],
)
app = FastAPI()
app.include_router(mosaic.router)
Expand Down
20 changes: 17 additions & 3 deletions src/titiler/mosaic/titiler/mosaic/extensions/wmts.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,31 @@ class wmtsExtension(FactoryExtension):

templates: Jinja2Templates = field(default=DEFAULT_TEMPLATES)

get_renders: Callable[[BaseBackend], dict[str, dict[str, Any]]] = field(
default=lambda obj: {}
# TODO: Remove in 3.0
get_renders: Callable[[BaseBackend], dict[str, dict[str, Any]]] | None = field(
default=None
)

# List of dependencies a `/tile` URL should validate
# Note: Those dependencies should only require Query() inputs
tile_dependencies: list[Callable] | None = field(default=None)

# TODO: Remove in 3.0
def __attrs_post_init__(self):
"""Warn about deprecation of `get_renders` attribute."""
if self.get_renders:
warnings.warn(
"The wmtsExtension's `get_renders` attribute is deprecated and will be ignored. Please set it at the factory level.",
DeprecationWarning,
stacklevel=2,
)

def register(self, factory: MosaicTilerFactory): # type: ignore [override] # noqa: C901
"""Register endpoint to the tiler factory."""

# TODO: Remove in 3.0
self.get_renders = factory.get_renders

tile_dependencies = (
self.tile_dependencies
if self.tile_dependencies is not None
Expand Down Expand Up @@ -116,7 +130,7 @@ def wmts( # noqa: C901
**backend_params.as_dict(),
) as src_dst:
dataset_bounds = src_dst.get_geographic_bounds(self.crs)
default_renders = self.get_renders(src_dst)
default_renders = factory.get_renders(src_dst)

renders: list[dict[str, Any]] = []

Expand Down
28 changes: 15 additions & 13 deletions src/titiler/mosaic/titiler/mosaic/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,10 @@ class MosaicTilerFactory(BaseFactory):

render_func: Callable[..., tuple[bytes, str]] = render_image

get_renders: Callable[[BaseBackend], dict[str, dict[str, Any]]] = field(
default=lambda obj: {}
)

optional_headers: list[OptionalHeader] = field(factory=list)

# Add/Remove some endpoints
Expand Down Expand Up @@ -769,23 +773,21 @@ def tilejson(
reader_options=reader_params.as_dict(),
**backend_params.as_dict(),
) as src_dst:
bounds = src_dst.get_geographic_bounds(tms.rasterio_geographic_crs)
minzoom = minzoom if minzoom is not None else src_dst.minzoom
maxzoom = maxzoom if maxzoom is not None else src_dst.maxzoom
center = (
(bounds[0] + bounds[2]) / 2,
(bounds[1] + bounds[3]) / 2,
minzoom,
)
return {
"bounds": bounds,
"center": center,
"minzoom": minzoom,
"maxzoom": maxzoom,
body = {
"bounds": src_dst.get_geographic_bounds(
tms.rasterio_geographic_crs
),
"minzoom": minzoom if minzoom is not None else src_dst.minzoom,
"maxzoom": maxzoom if maxzoom is not None else src_dst.maxzoom,
"tiles": [tiles_url],
"attribution": os.environ.get("TITILER_DEFAULT_ATTRIBUTION"),
}

# Custom TiTiler tilejson fields
body["raster_layers"] = self.get_renders(src_dst)

return body

def map_viewer(self): # noqa: C901
"""Register /map.html endpoint."""

Expand Down
4 changes: 2 additions & 2 deletions src/titiler/xarray/titiler/xarray/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def info_endpoint(
Query(description="Show info about the time dimension"),
] = None,
env=Depends(self.environment_dependency),
) -> Info:
):
"""Return dataset's basic info."""
with rasterio.Env(**env):
logger.info(f"opening data with reader: {self.reader}")
Expand All @@ -116,7 +116,7 @@ def info_endpoint(
info["count"] = len(times)
info["times"] = times

return Info(**info)
return info

@self.router.get(
"/info.geojson",
Expand Down