Skip to content

Commit 5b9c4aa

Browse files
committed
update titiler/titiler-pgstac
1 parent cc8cf42 commit 5b9c4aa

File tree

6 files changed

+309
-77
lines changed

6 files changed

+309
-77
lines changed

pctiler/pctiler/endpoints/item.py

Lines changed: 164 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,19 @@
33
from typing import Annotated, Optional
44
from urllib.parse import quote_plus, urljoin
55

6+
import jinja2
67
import fastapi
78
import pystac
8-
from fastapi import Body, Depends, Query, Request, Response
9+
from pydantic import Field
10+
from fastapi import Body, Depends, Query, Request, Response, Path
911
from fastapi.templating import Jinja2Templates
1012
from geojson_pydantic.features import Feature
1113
from html_sanitizer.sanitizer import Sanitizer
1214
from starlette.responses import HTMLResponse
1315
from titiler.core.dependencies import CoordCRSParams, DstCRSParams
1416
from titiler.core.factory import MultiBaseTilerFactory, img_endpoint_params
1517
from titiler.core.resources.enums import ImageType
18+
from titiler.core.models.mapbox import TileJSON
1619
from titiler.pgstac.dependencies import get_stac_item
1720

1821
from pccommon.config import get_render_config
@@ -22,12 +25,6 @@
2225
from pctiler.endpoints.dependencies import get_endpoint_function
2326
from pctiler.reader import ItemSTACReader, ReaderParams
2427

25-
try:
26-
from importlib.resources import files as resources_files # type: ignore
27-
except ImportError:
28-
# Try backported to PY<39 `importlib_resources`.
29-
from importlib_resources import files as resources_files # type: ignore
30-
3128
logger = logging.getLogger(__name__)
3229

3330

@@ -79,11 +76,10 @@ async def _fetch() -> dict:
7976
return pystac.Item.from_dict(_item)
8077

8178

82-
# TODO: mypy fails in python 3.9, we need to find a proper way to do this
83-
templates = Jinja2Templates(
84-
directory=str(resources_files(__package__) / "templates") # type: ignore
79+
jinja2_env = jinja2.Environment(
80+
loader=jinja2.ChoiceLoader([jinja2.PackageLoader(__package__, "templates")])
8581
)
86-
82+
templates = Jinja2Templates(env=jinja2_env)
8783

8884
pc_tile_factory = MultiBaseTilerFactory(
8985
reader=ItemSTACReader,
@@ -135,17 +131,20 @@ def map(
135131
},
136132
)
137133

138-
134+
# crop/feature endpoint compat with titiler<0.15 (`/crop` was renamed `/feature`)
139135
@pc_tile_factory.router.post(
140136
r"/crop",
137+
operation_id=f"{self.operation_prefix}postDataForGeoJSONCrop",
141138
**img_endpoint_params,
142139
)
143140
@pc_tile_factory.router.post(
144141
r"/crop.{format}",
142+
operation_id=f"{self.operation_prefix}postDataForGeoJSONWithFormatCrop",
145143
**img_endpoint_params,
146144
)
147145
@pc_tile_factory.router.post(
148146
r"/crop/{width}x{height}.{format}",
147+
operation_id=f"{self.operation_prefix}postDataForGeoJSONWithSizesAndFormatCrop",
149148
**img_endpoint_params,
150149
)
151150
def geojson_crop( # type: ignore
@@ -155,7 +154,9 @@ def geojson_crop( # type: ignore
155154
],
156155
format: Annotated[
157156
ImageType,
158-
"Default will be automatically defined if the output image needs a mask (png) or not (jpeg).", # noqa: E501,F722
157+
Field(
158+
description="Default will be automatically defined if the output image needs a mask (png) or not (jpeg)."
159+
),
159160
] = None, # type: ignore[assignment]
160161
src_path=Depends(pc_tile_factory.path_dependency),
161162
coord_crs=Depends(CoordCRSParams),
@@ -164,14 +165,6 @@ def geojson_crop( # type: ignore
164165
dataset_params=Depends(pc_tile_factory.dataset_dependency),
165166
image_params=Depends(pc_tile_factory.img_part_dependency),
166167
post_process=Depends(pc_tile_factory.process_dependency),
167-
rescale=Depends(pc_tile_factory.rescale_dependency),
168-
color_formula: Annotated[
169-
Optional[str],
170-
Query(
171-
title="Color Formula", # noqa: F722
172-
description="rio-color formula (info: https://github.com/mapbox/rio-color)", # noqa: E501,F722
173-
),
174-
] = None,
175168
colormap=Depends(pc_tile_factory.colormap_dependency),
176169
render_params=Depends(pc_tile_factory.render_dependency),
177170
reader_params=Depends(pc_tile_factory.reader_dependency),
@@ -191,11 +184,159 @@ def geojson_crop( # type: ignore
191184
dataset_params=dataset_params,
192185
image_params=image_params,
193186
post_process=post_process,
194-
rescale=rescale,
195-
color_formula=color_formula,
196187
colormap=colormap,
197188
render_params=render_params,
198189
reader_params=reader_params,
199190
env=env,
200191
)
201192
return result
193+
194+
195+
# /tiles endpoint compat with titiler<0.15, Optional `tileMatrixSetId`
196+
@pc_tile_factory.router.get(
197+
"/tiles/{z}/{x}/{y}",
198+
operation_id=f"{pc_tile_factory.operation_prefix}getWebMercatorQuadTile",
199+
**img_endpoint_params,
200+
)
201+
@pc_tile_factory.router.get(
202+
"/tiles/{z}/{x}/{y}.{format}",
203+
operation_id=f"{pc_tile_factory.operation_prefix}getWebMercatorQuadTileWithFormat",
204+
**img_endpoint_params,
205+
)
206+
@pc_tile_factory.router.get(
207+
"/tiles/{z}/{x}/{y}@{scale}x",
208+
operation_id=f"{pc_tile_factory.operation_prefix}getWebMercatorQuadTileWithScale",
209+
**img_endpoint_params,
210+
)
211+
@pc_tile_factory.router.get(
212+
"/tiles/{z}/{x}/{y}@{scale}x.{format}",
213+
operation_id=f"{pc_tile_factory.operation_prefix}getWebMercatorQuadTileWithFormatAndScale",
214+
**img_endpoint_params,
215+
)
216+
def tile_compat(
217+
request: fastapi.Request,
218+
z: Annotated[
219+
int,
220+
Path(
221+
description="Identifier (Z) selecting one of the scales defined in the TileMatrixSet and representing the scaleDenominator the tile.",
222+
),
223+
],
224+
x: Annotated[
225+
int,
226+
Path(
227+
description="Column (X) index of the tile on the selected TileMatrix. It cannot exceed the MatrixHeight-1 for the selected TileMatrix.",
228+
),
229+
],
230+
y: Annotated[
231+
int,
232+
Path(
233+
description="Row (Y) index of the tile on the selected TileMatrix. It cannot exceed the MatrixWidth-1 for the selected TileMatrix.",
234+
),
235+
],
236+
scale: Annotated[
237+
int,
238+
Field(
239+
gt=0, le=4, description="Tile size scale. 1=256x256, 2=512x512..."
240+
),
241+
] = 1,
242+
format: Annotated[
243+
ImageType,
244+
Field(
245+
description="Default will be automatically defined if the output image needs a mask (png) or not (jpeg)."
246+
),
247+
] = None,
248+
src_path=Depends(pc_tile_factory.path_dependency),
249+
reader_params=Depends(pc_tile_factory.reader_dependency),
250+
tile_params=Depends(pc_tile_factory.tile_dependency),
251+
layer_params=Depends(pc_tile_factory.layer_dependency),
252+
dataset_params=Depends(pc_tile_factory.dataset_dependency),
253+
post_process=Depends(pc_tile_factory.process_dependency),
254+
colormap=Depends(pc_tile_factory.colormap_dependency),
255+
render_params=Depends(pc_tile_factory.render_dependency),
256+
env=Depends(pc_tile_factory.environment_dependency),
257+
) -> Response:
258+
"""tiles endpoints compat."""
259+
endpoint = get_endpoint_function(
260+
pc_tile_factory.router, path="/tiles/{tileMatrixSetId}/{z}/{x}/{y}", method=request.method
261+
)
262+
result = endpoint(
263+
z=z,
264+
x=x,
265+
y=y,
266+
tileMatrixSetId="WebMercatorQuad",
267+
scale=scale,
268+
format=format,
269+
src_path=src_path,
270+
reader_params=reader_params,
271+
tile_params=tile_params,
272+
layer_params=layer_params,
273+
dataset_params=dataset_params,
274+
post_process=post_process,
275+
colormap=colormap,
276+
render_params=render_params,
277+
env=env,
278+
)
279+
return result
280+
281+
282+
# /tilejson.json endpoint compat with titiler<0.15, Optional `tileMatrixSetId`
283+
@pc_tile_factory.router.get(
284+
"/tilejson.json",
285+
response_model=TileJSON,
286+
responses={200: {"description": "Return a tilejson"}},
287+
response_model_exclude_none=True,
288+
operation_id=f"{pc_tile_factory.operation_prefix}getWebMercatorQuadTileJSON",
289+
)
290+
def tilejson_compat(
291+
request: fastapi.Request,
292+
tile_format: Annotated[
293+
Optional[ImageType],
294+
Query(
295+
description="Default will be automatically defined if the output image needs a mask (png) or not (jpeg).",
296+
),
297+
] = None,
298+
tile_scale: Annotated[
299+
int,
300+
Query(
301+
gt=0, lt=4, description="Tile size scale. 1=256x256, 2=512x512..."
302+
),
303+
] = 1,
304+
minzoom: Annotated[
305+
Optional[int],
306+
Query(description="Overwrite default minzoom."),
307+
] = None,
308+
maxzoom: Annotated[
309+
Optional[int],
310+
Query(description="Overwrite default maxzoom."),
311+
] = None,
312+
src_path=Depends(pc_tile_factory.path_dependency),
313+
reader_params=Depends(pc_tile_factory.reader_dependency),
314+
tile_params=Depends(pc_tile_factory.tile_dependency),
315+
layer_params=Depends(pc_tile_factory.layer_dependency),
316+
dataset_params=Depends(pc_tile_factory.dataset_dependency),
317+
post_process=Depends(pc_tile_factory.process_dependency),
318+
colormap=Depends(pc_tile_factory.colormap_dependency),
319+
render_params=Depends(pc_tile_factory.render_dependency),
320+
env=Depends(pc_tile_factory.environment_dependency),
321+
) -> Response:
322+
"""tilejson endpoint compat."""
323+
endpoint = get_endpoint_function(
324+
pc_tile_factory.router, path="/{tileMatrixSetId}/tilejson.json", method=request.method
325+
)
326+
result = endpoint(
327+
tileMatrixSetId="WebMercatorQuad",
328+
tile_format=tile_format,
329+
tile_scale=tile_scale,
330+
minzoom=minzoom,
331+
maxzoom=maxzoom,
332+
src_path=src_path,
333+
reader_params=reader_params,
334+
tile_params=tile_params,
335+
layer_params=layer_params,
336+
dataset_params=dataset_params,
337+
post_process=post_process,
338+
colormap=colormap,
339+
render_params=render_params,
340+
env=env,
341+
)
342+
return result

pctiler/pctiler/endpoints/pg_mosaic.py

Lines changed: 38 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
from dataclasses import dataclass, field
22
from typing import Annotated, List, Literal, Optional
33

4-
from fastapi import APIRouter, Depends, FastAPI, Query, Request, Response
4+
from fastapi import APIRouter, Depends, FastAPI, Query, Request, Response, Path
55
from fastapi.responses import ORJSONResponse
66
from psycopg_pool import ConnectionPool
77
from pydantic import Field
88
from titiler.core import dependencies
99
from titiler.core.dependencies import ColorFormulaParams
1010
from titiler.core.factory import img_endpoint_params
1111
from titiler.core.resources.enums import ImageType
12-
from titiler.pgstac.dependencies import SearchIdParams, TmsTileParams
12+
from titiler.pgstac.dependencies import SearchIdParams
1313
from titiler.pgstac.extensions import searchInfoExtension
1414
from titiler.pgstac.factory import MosaicTilerFactory
1515

@@ -40,7 +40,7 @@ def __init__(self, request: Request):
4040

4141

4242
pgstac_mosaic_factory = MosaicTilerFactory(
43-
reader=PGSTACBackend,
43+
backend=PGSTACBackend,
4444
path_dependency=SearchIdParams,
4545
colormap_dependency=PCColorMapParams,
4646
layer_dependency=AssetsBidxExprParams,
@@ -86,7 +86,7 @@ def mosaic_info(
8686

8787
legacy_mosaic_router = APIRouter()
8888

89-
89+
# Compat with titiler-pgstac<0.3.0, (`/tiles/{search_id}/...` was renamed `/{search_id}/tiles/...`)
9090
@legacy_mosaic_router.get("/tiles/{search_id}/{z}/{x}/{y}", **img_endpoint_params)
9191
@legacy_mosaic_router.get(
9292
"/tiles/{search_id}/{z}/{x}/{y}.{format}", **img_endpoint_params
@@ -115,57 +115,74 @@ def mosaic_info(
115115
def tile_routes( # type: ignore
116116
request: Request,
117117
search_id=Depends(pgstac_mosaic_factory.path_dependency),
118-
tile=Depends(TmsTileParams),
118+
z: Annotated[
119+
int,
120+
Path(
121+
description="Identifier (Z) selecting one of the scales defined in the TileMatrixSet and representing the scaleDenominator the tile.",
122+
),
123+
],
124+
x: Annotated[
125+
int,
126+
Path(
127+
description="Column (X) index of the tile on the selected TileMatrix. It cannot exceed the MatrixHeight-1 for the selected TileMatrix.",
128+
),
129+
],
130+
y: Annotated[
131+
int,
132+
Path(
133+
description="Row (Y) index of the tile on the selected TileMatrix. It cannot exceed the MatrixWidth-1 for the selected TileMatrix.",
134+
),
135+
],
119136
tileMatrixSetId: Annotated[ # type: ignore
120137
Literal[tuple(pgstac_mosaic_factory.supported_tms.list())],
121-
f"Identifier selecting one of the TileMatrixSetId supported (default: '{pgstac_mosaic_factory.default_tms}')", # noqa: E501,F722
122-
] = pgstac_mosaic_factory.default_tms,
138+
f"Identifier selecting one of the TileMatrixSetId supported (default: 'WebMercatorQuad')", # noqa: E501,F722
139+
] = "WebMercatorQuad",
123140
scale: Annotated[ # type: ignore
124141
Optional[Annotated[int, Field(gt=0, le=4)]],
125142
"Tile size scale. 1=256x256, 2=512x512...", # noqa: E501,F722
126143
] = None,
127144
format: Annotated[
128145
Optional[ImageType],
129-
"Default will be automatically defined if the output image needs a mask (png) or not (jpeg).", # noqa: E501,F722
146+
Field(
147+
description="Default will be automatically defined if the output image needs a mask (png) or not (jpeg)."
148+
),
130149
] = None,
150+
backend_params=Depends(pgstac_mosaic_factory.backend_dependency),
151+
reader_params=Depends(pgstac_mosaic_factory.reader_dependency),
152+
assets_accessor_params=Depends(pgstac_mosaic_factory.assets_accessor_dependency),
131153
layer_params=Depends(pgstac_mosaic_factory.layer_dependency),
132154
dataset_params=Depends(pgstac_mosaic_factory.dataset_dependency),
133155
pixel_selection=Depends(pgstac_mosaic_factory.pixel_selection_dependency),
134156
tile_params=Depends(pgstac_mosaic_factory.tile_dependency),
135157
post_process=Depends(pgstac_mosaic_factory.process_dependency),
136-
rescale=Depends(pgstac_mosaic_factory.rescale_dependency),
137-
color_formula=Depends(ColorFormulaParams),
138158
colormap=Depends(pgstac_mosaic_factory.colormap_dependency),
139159
render_params=Depends(pgstac_mosaic_factory.render_dependency),
140-
pgstac_params=Depends(pgstac_mosaic_factory.pgstac_dependency),
141-
backend_params=Depends(pgstac_mosaic_factory.backend_dependency),
142-
reader_params=Depends(pgstac_mosaic_factory.reader_dependency),
143160
env=Depends(pgstac_mosaic_factory.environment_dependency),
144161
) -> Response:
145162
"""Create map tile."""
146163
endpoint = get_endpoint_function(
147164
pgstac_mosaic_factory.router,
148-
path="/tiles/{z}/{x}/{y}",
165+
path="/tiles/{tileMatrixSetId}/{z}/{x}/{y}",
149166
method=request.method,
150167
)
151168
result = endpoint(
152169
search_id=search_id,
153-
tile=tile,
170+
z=z,
171+
x=x,
172+
y=y,
154173
tileMatrixSetId=tileMatrixSetId,
155174
scale=scale,
156175
format=format,
157-
tile_params=tile_params,
176+
backend_params=backend_params,
177+
reader_params=reader_params,
178+
assets_accessor_params=assets_accessor_params,
158179
layer_params=layer_params,
159180
dataset_params=dataset_params,
160181
pixel_selection=pixel_selection,
182+
tile_params=tile_params,
161183
post_process=post_process,
162-
rescale=rescale,
163-
color_formula=color_formula,
164184
colormap=colormap,
165185
render_params=render_params,
166-
pgstac_params=pgstac_params,
167-
backend_params=backend_params,
168-
reader_params=reader_params,
169186
env=env,
170187
)
171188
return result

pctiler/pctiler/main.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,8 @@ async def lifespan(app: FastAPI) -> AsyncGenerator:
9292
pg_mosaic.pgstac_mosaic_factory.dataset_dependency,
9393
pg_mosaic.pgstac_mosaic_factory.pixel_selection_dependency,
9494
pg_mosaic.pgstac_mosaic_factory.process_dependency,
95-
pg_mosaic.pgstac_mosaic_factory.rescale_dependency,
96-
pg_mosaic.pgstac_mosaic_factory.colormap_dependency,
9795
pg_mosaic.pgstac_mosaic_factory.render_dependency,
96+
pg_mosaic.pgstac_mosaic_factory.assets_accessor_dependency,
9897
pg_mosaic.pgstac_mosaic_factory.reader_dependency,
9998
pg_mosaic.pgstac_mosaic_factory.backend_dependency,
10099
pg_mosaic.pgstac_mosaic_factory.pgstac_dependency,

0 commit comments

Comments
 (0)