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
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Note: Minor version `0.X.0` update might break the API, It's recommended to pin
## [unreleased]

* switch to official python docker image from `bitnami`
* add `FactoryExtension` to `EndpointsFactory`
* rename `viewer_endpoint` function to `map_viewer` **breaking change**
* rename `/viewer` endpoint to `/map.html` **breaking change**

## [1.2.1] - 2025-08-26

Expand Down
10 changes: 5 additions & 5 deletions docs/src/user_guide/endpoints.md
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ Return a TileJSON document. **Not in OGC Tile API specification**

Path:

- `/collections/{collectionId}/{tileMatrixSetId}/tilejson.json`
- `/collections/{collectionId}/tiles/{tileMatrixSetId}/tilejson.json`

PathParams:

Expand All @@ -677,7 +677,7 @@ QueryParams:

Example:
```json
curl http://127.0.0.1:8081/collections/public.landsat_wrs/WebMercatorQuad/tilejson.json | jq
curl http://127.0.0.1:8081/collections/public.landsat_wrs/tiles/WebMercatorQuad/tilejson.json | jq
{
"tilejson": "3.0.0",
"name": "public.landsat_wrs",
Expand Down Expand Up @@ -722,7 +722,7 @@ Return a mapbox/maplibre StyleJSON document. **Not in OGC Tile API specification

Path:

- `/collections/{collectionId}/{tileMatrixSetId}/style.json`
- `/collections/{collectionId}/tiles/{tileMatrixSetId}/style.json`

PathParams:

Expand All @@ -741,7 +741,7 @@ QueryParams:
\* **Not in OGC API Tiles Specification**

```json
// http://127.0.0.1:8081/collections/public.landsat_wrs/WebMercatorQuad/style.json
// http://127.0.0.1:8081/collections/public.landsat_wrs/tiles/WebMercatorQuad/style.json
{
"version": 8,
"name": "TiPg",
Expand Down Expand Up @@ -831,7 +831,7 @@ Simple Map viewer. **Not in OGC Tile API specification**

Path:

- `/collections/{collectionId}/{tileMatrixSetId}/viewer`
- `/collections/{collectionId}/tiles/{tileMatrixSetId}/map.html`

PathParams:

Expand Down
16 changes: 8 additions & 8 deletions docs/src/user_guide/factories.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ app.include_router(endpoints.router)

- **supported_tms** (morecantile.TileMatrixSets): morecantile TileMatrixSets instance (holds a set of TileMatrixSet documents)

- **with_viewer** (bool, optional): add `/viewer` endpoint to visualize the Vector tile. Defaults to `True`
- **with_map_viewer** (bool, optional): add `/map.html` endpoint to visualize the Vector tile. Defaults to `True`

- **with_common** (bool, optional): Create Full OGC Features API set of endpoints with OGC Common endpoints (landing `/` and conformance `/conformance`). Defaults to `True`

Expand All @@ -134,9 +134,9 @@ app.include_router(endpoints.router)
| `GET` | `/collections/{collectionId}/tiles` | JSON | list of available vector tilesets
| `GET` | `/collections/{collectionId}/tiles/{tileMatrixSetId}` | JSON | vector tileset metadata
| `GET` | `/collections/{collectionId}/tiles/{tileMatrixSetId}/{z}/{x}/{y}` | Mapbox Vector Tile (Protobuf) | create a web map vector tile from collection's items
| `GET` | `/collections/{collectionId}/{tileMatrixSetId}/tilejson.json` | JSON | Mapbox TileJSON document
| `GET` | `/collections/{collectionId}/{tileMatrixSetId}/style.json` | JSON | Mapbox/Maplibre StyleJSON document
| `GET` | `/collections/{collectionId}/{tileMatrixSetId}/viewer` | HTML | simple map viewer **[OPTIONAL]**
| `GET` | `/collections/{collectionId}/tiles/{tileMatrixSetId}/tilejson.json` | JSON | Mapbox TileJSON document
| `GET` | `/collections/{collectionId}/tiles/{tileMatrixSetId}/style.json` | JSON | Mapbox/Maplibre StyleJSON document
| `GET` | `/collections/{collectionId}/tiles/{tileMatrixSetId}/map.html` | HTML | simple map viewer **[OPTIONAL]**
| `GET` | `/tileMatrixSets` | JSON | list of available TileMatrixSets
| `GET` | `/tileMatrixSets/{tileMatrixSetId}` | JSON | TileMatrixSet document
| `GET` | `/conformance` | HTML / JSON | conformance class landing Page
Expand All @@ -160,7 +160,7 @@ app.include_router(endpoints.router)

- **supported_tms** (morecantile.TileMatrixSets): morecantile TileMatrixSets instance (holds a set of TileMatrixSet documents)

- **with_tiles_viewer** (bool, optional): add `/viewer` endpoint to visualize the Vector tile. Defaults to `True`
- **with_tiles_viewer** (bool, optional): add `/map.html` endpoint to visualize the Vector tile. Defaults to `True`

- **with_common** (bool, optional): Create Full OGC Features API set of endpoints with OGC Common endpoints (landing `/` and conformance `/conformance`). Defaults to `True`

Expand All @@ -184,9 +184,9 @@ app.include_router(endpoints.router)
| `GET` | `/collections/{collectionId}/tiles` | JSON | list of available vector tilesets
| `GET` | `/collections/{collectionId}/tiles/{tileMatrixSetId}` | JSON | vector tileset metadata
| `GET` | `/collections/{collectionId}/tiles/{tileMatrixSetId}/{z}/{x}/{y}` | Mapbox Vector Tile (Protobuf) | create a web map vector tile from collection's items
| `GET` | `/collections/{collectionId}/{tileMatrixSetId}/tilejson.json` | JSON | Mapbox TileJSON document
| `GET` | `/collections/{collectionId}/{tileMatrixSetId}/style.json` | JSON | Mapbox/Maplibre StyleJSON document
| `GET` | `/collections/{collectionId}/{tileMatrixSetId}/viewer` | HTML | simple map viewer **[OPTIONAL]**
| `GET` | `/collections/{collectionId}/tiles/{tileMatrixSetId}/tilejson.json` | JSON | Mapbox TileJSON document
| `GET` | `/collections/{collectionId}/tiles/{tileMatrixSetId}/style.json` | JSON | Mapbox/Maplibre StyleJSON document
| `GET` | `/collections/{collectionId}/tiles/{tileMatrixSetId}/map.html` | HTML | simple map viewer **[OPTIONAL]**
| `GET` | `/tileMatrixSets` | JSON | list of available TileMatrixSets
| `GET` | `/tileMatrixSets/{tileMatrixSetId}` | JSON | TileMatrixSet document
| `GET` | `/conformance` | HTML / JSON | conformance class landing Page
Expand Down
1 change: 1 addition & 0 deletions tipg/extensions/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Tipg extensions"""
66 changes: 66 additions & 0 deletions tipg/extensions/viewer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""viewer Extension."""

from dataclasses import dataclass
from typing import Annotated, Optional
from urllib.parse import urlencode

from tipg.collections import Collection
from tipg.factory import EndpointsFactory, FactoryExtension

from fastapi import Depends, Query

from starlette.requests import Request
from starlette.responses import HTMLResponse


@dataclass
class viewerExtension(FactoryExtension):
"""Add /validate endpoint to a COG TilerFactory."""

def register(self, factory: EndpointsFactory):
"""Register endpoint to the tiler factory."""

@factory.router.get(
"/collections/{collectionId}/viewer.html",
response_class=HTMLResponse,
operation_id=".collection.vector.viewer",
tags=["Map Viewer"],
)
def viewer(
request: Request,
collection: Annotated[Collection, Depends(factory.collection_dependency)],
minzoom: Annotated[
Optional[int],
Query(description="Overwrite default minzoom."),
] = None,
maxzoom: Annotated[
Optional[int],
Query(description="Overwrite default maxzoom."),
] = None,
geom_column: Annotated[
Optional[str],
Query(
description="Select geometry column.",
alias="geom-column",
),
] = None,
):
"""Return Simple HTML Viewer for a collection."""
stylejson_url = factory.url_for(
request,
"collection_stylejson",
collectionId=collection.id,
tileMatrixSetId="WebMercatorQuad",
)
if request.query_params._list:
stylejson_url += f"?{urlencode(request.query_params._list)}"

return factory._create_html_response(
request,
{
"title": collection.id,
"stylejson_endpoint": stylejson_url,
},
template_name="viewer",
title=f"{collection.id} viewer",
)
27 changes: 22 additions & 5 deletions tipg/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,16 @@ def create_html_response(
)


@dataclass
class FactoryExtension(metaclass=abc.ABCMeta):
"""Factory Extension."""

@abc.abstractmethod
def register(self, factory: "EndpointsFactory"):
"""Register extension to the factory."""
...


# ref: https://github.com/python/mypy/issues/5374
@dataclass # type: ignore
class EndpointsFactory(metaclass=abc.ABCMeta):
Expand All @@ -191,6 +201,8 @@ class EndpointsFactory(metaclass=abc.ABCMeta):
# e.g if you mount the route with `/foo` prefix, set router_prefix to foo
router_prefix: str = ""

extensions: List[FactoryExtension] = field(default_factory=list)

templates: Jinja2Templates = DEFAULT_TEMPLATES

# Full application with Landing and Conformance
Expand All @@ -203,8 +215,13 @@ def __post_init__(self):
if self.with_common:
self._landing_route()
self._conformance_route()

self.register_routes()

# Register Extensions
for ext in self.extensions:
ext.register(self)

def url_for(self, request: Request, name: str, **path_params: Any) -> str:
"""Return full url (with prefix) for a specific handler."""
url_path = self.router.url_path_for(name, **path_params)
Expand Down Expand Up @@ -463,7 +480,7 @@ def _additional_collection_tiles_links(
title="Collection Map viewer (Template URL)",
href=str(
request.app.url_path_for(
"viewer_endpoint",
"map_viewer",
collectionId=collection.id,
tileMatrixSetId="{tileMatrixSetId}",
).make_absolute_url(base_url=base_url)
Expand Down Expand Up @@ -1238,7 +1255,7 @@ def links(self, request: Request) -> List[model.Link]:
title="Collection Map viewer (Template URL)",
href=self.url_for(
request,
"viewer_endpoint",
"map_viewer",
collectionId="{collectionId}",
tileMatrixSetId="{tileMatrixSetId}",
),
Expand Down Expand Up @@ -1572,7 +1589,7 @@ async def collection_tileset(
{
"href": self.url_for(
request,
"viewer_endpoint",
"map_viewer",
tileMatrixSetId=tileMatrixSetId,
collectionId=collection.id,
),
Expand Down Expand Up @@ -1906,12 +1923,12 @@ async def collection_stylejson(
if self.with_viewer:

@self.router.get(
"/collections/{collectionId}/tiles/{tileMatrixSetId}/viewer",
"/collections/{collectionId}/tiles/{tileMatrixSetId}/map.html",
response_class=HTMLResponse,
operation_id=".collection.vector.map",
tags=["Map Viewer"],
)
def viewer_endpoint(
def map_viewer(
request: Request,
collection: Annotated[Collection, Depends(self.collection_dependency)],
tileMatrixSetId: Annotated[
Expand Down
2 changes: 2 additions & 0 deletions tipg/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from tipg.collections import register_collection_catalog
from tipg.database import close_db_connection, connect_to_db
from tipg.errors import DEFAULT_STATUS_CODES, add_exception_handlers
from tipg.extensions.viewer import viewerExtension
from tipg.factory import Endpoints
from tipg.middleware import CacheControlMiddleware, CatalogUpdateMiddleware
from tipg.openapi import _update_openapi
Expand Down Expand Up @@ -73,6 +74,7 @@ async def lifespan(app: FastAPI):
title=settings.name,
templates=templates,
with_tiles_viewer=settings.add_tiles_viewer,
extensions=[viewerExtension()],
)
app.include_router(ogc_api.router)

Expand Down
Loading
Loading