Skip to content

Commit 3cc42d5

Browse files
committed
update for titiler 1.1.0
1 parent 2ec0b34 commit 3cc42d5

6 files changed

Lines changed: 639 additions & 174 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@ on:
1919
# Run tests on pull requests.
2020
pull_request:
2121
env:
22-
LATEST_PY_VERSION: '3.13'
22+
LATEST_PY_VERSION: '3.14'
2323

2424

2525
jobs:
2626
tests:
2727
runs-on: ubuntu-latest
2828
strategy:
2929
matrix:
30-
python-version: ['3.11', '3.12', '3.13']
30+
python-version: ['3.11', '3.12', '3.13', '3.14']
3131

3232
steps:
3333
- uses: actions/checkout@v5

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ARG PYTHON_VERSION=3.13
1+
ARG PYTHON_VERSION=3.14
22

33
FROM python:${PYTHON_VERSION}
44
RUN apt update && apt upgrade -y \

pyproject.toml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
name = "titiler-stacapi"
33
description = "Connect titiler to STAC APIs."
44
readme = "README.md"
5-
requires-python = ">=3.11,<3.14"
5+
requires-python = ">=3.11"
66
license = {file = "LICENSE"}
77
authors = [
88
{name = "Vincent Sarago", email = "vincent@developmentseed.com"},
@@ -18,14 +18,15 @@ classifiers = [
1818
"Programming Language :: Python :: 3.11",
1919
"Programming Language :: Python :: 3.12",
2020
"Programming Language :: Python :: 3.13",
21+
"Programming Language :: Python :: 3.14",
2122
"Topic :: Scientific/Engineering :: GIS",
2223
]
2324
dynamic = ["version"]
2425
dependencies = [
2526
"orjson",
26-
"titiler.core>=1.0.2,<1.1",
27-
"titiler.mosaic>=1.0.2,<1.1",
28-
"titiler.extensions>=1.0.2,<1.1",
27+
"titiler.core>=1.1,<1.2",
28+
"titiler.mosaic>=1.1,<1.2",
29+
"titiler.extensions>=1.1,<1.2",
2930
"pystac-client",
3031
"pydantic>=2.4,<3.0",
3132
"pydantic-settings~=2.0",

titiler/stacapi/backend.py

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,14 @@
1010
from geojson_pydantic import Point, Polygon
1111
from geojson_pydantic.geometries import Geometry
1212
from morecantile import Tile, TileMatrixSet
13-
from pystac_client import ItemSearch
13+
from pystac_client import Client, ItemSearch
1414
from pystac_client.stac_api_io import StacApiIO
1515
from rasterio.crs import CRS
1616
from rasterio.warp import transform, transform_bounds
1717
from rio_tiler.constants import WEB_MERCATOR_TMS, WGS84_CRS
18-
from rio_tiler.mosaic.backend import BaseBackend
18+
from rio_tiler.mosaic.backend import BaseBackend, MosaicInfo
1919
from rio_tiler.types import BBox
20+
from rio_tiler.utils import CRS_to_uri
2021
from urllib3 import Retry
2122

2223
from titiler.stacapi.dependencies import APIParams, Search
@@ -161,3 +162,38 @@ def get_assets(
161162
f"{self.api_params['url']}/search", stac_io=stac_api_io, **params
162163
)
163164
return list(results.items_as_dicts())
165+
166+
@cached( # type: ignore
167+
ttl_cache,
168+
key=lambda self: hashkey(
169+
self.api_params["url"],
170+
json.dumps(self.input),
171+
json.dumps(self.api_params.get("headers", {})),
172+
),
173+
lock=Lock(),
174+
)
175+
def info(self) -> MosaicInfo: # type: ignore
176+
"""Mosaic info."""
177+
renders = {}
178+
bounds = self.bounds
179+
crs = self.crs
180+
181+
if collections := self.input.get("collections", []):
182+
if len(collections) == 1:
183+
stac_api_io = StacApiIO(
184+
max_retries=Retry(
185+
total=retry_config.retry,
186+
backoff_factor=retry_config.retry_factor,
187+
),
188+
headers=self.api_params.get("headers", {}),
189+
)
190+
client = Client.open(f"{self.api_params['url']}", stac_io=stac_api_io)
191+
collection = client.get_collection(collections[0])
192+
if collection.extent.spatial:
193+
bounds = tuple(collection.extent.spatial.bboxes[0])
194+
crs = WGS84_CRS
195+
renders = collection.extra_fields.get("renders", {})
196+
197+
return MosaicInfo(
198+
bounds=bounds, crs=CRS_to_uri(crs) or crs.to_wkt(), renders=renders
199+
)

titiler/stacapi/main.py

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""TiTiler+stacapi FastAPI application."""
22

3-
from typing import Any, List, Literal, Optional
3+
from typing import Annotated, Any, Literal
44

55
import httpx
66
import jinja2
@@ -12,7 +12,6 @@
1212
from starlette.middleware.cors import CORSMiddleware
1313
from starlette.requests import Request
1414
from starlette.templating import Jinja2Templates
15-
from typing_extensions import Annotated
1615

1716
from titiler.core import __version__ as titiler_version
1817
from titiler.core.dependencies import AssetsBidxExprParams
@@ -27,7 +26,9 @@
2726
from titiler.core.models.OGC import Conformance, Landing
2827
from titiler.core.resources.enums import OptionalHeader
2928
from titiler.core.utils import accept_media_type, create_html_response, update_openapi
29+
from titiler.extensions import wmtsExtension
3030
from titiler.mosaic.errors import MOSAIC_STATUS_CODES
31+
from titiler.mosaic.extensions.wmts import wmtsExtension as wmtsExtensionMosaic
3132
from titiler.mosaic.factory import MosaicTilerFactory
3233
from titiler.stacapi import __version__ as titiler_stacapi_version
3334
from titiler.stacapi.backend import STACAPIBackend
@@ -47,7 +48,7 @@
4748
stacapi_config = STACAPISettings()
4849

4950
# custom template directory
50-
templates_location: List[Any] = (
51+
templates_location: list[Any] = (
5152
[jinja2.FileSystemLoader(settings.template_directory)]
5253
if settings.template_directory
5354
else []
@@ -146,6 +147,11 @@
146147
layer_dependency=AssetsBidxExprParams,
147148
router_prefix="/collections/{collection_id}",
148149
add_viewer=True,
150+
extensions=[
151+
wmtsExtensionMosaic(
152+
get_renders=lambda obj: obj.info().renders or {} # type: ignore [attr-defined]
153+
),
154+
],
149155
templates=templates,
150156
)
151157
app.include_router(
@@ -164,6 +170,9 @@
164170
path_dependency=ItemIdParams,
165171
router_prefix="/collections/{collection_id}/items/{item_id}",
166172
add_viewer=True,
173+
extensions=[
174+
wmtsExtension(get_renders=lambda obj: obj.item.properties.get("renders", {})), # type: ignore [attr-defined]
175+
],
167176
templates=templates,
168177
)
169178
app.include_router(
@@ -214,7 +223,7 @@
214223
def landing(
215224
request: Request,
216225
f: Annotated[
217-
Optional[Literal["html", "json"]],
226+
Literal["html", "json"] | None,
218227
Query(
219228
description="Response MediaType. Defaults to endpoint's default or value defined in `accept` header."
220229
),
@@ -326,7 +335,7 @@ def landing(
326335
def conformance(
327336
request: Request,
328337
f: Annotated[
329-
Optional[Literal["html", "json"]],
338+
Literal["html", "json"] | None,
330339
Query(
331340
description="Response MediaType. Defaults to endpoint's default or value defined in `accept` header."
332341
),

0 commit comments

Comments
 (0)