Skip to content

Commit

Permalink
update raster and vector services
Browse files Browse the repository at this point in the history
  • Loading branch information
vincentsarago committed Nov 3, 2023
1 parent f300973 commit f5cd23d
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 70 deletions.
72 changes: 53 additions & 19 deletions .github/workflows/tests/test_raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,29 +16,61 @@ def test_raster_api():
def test_mosaic_api():
"""test mosaic."""
query = {"collections": ["noaa-emergency-response"], "filter-lang": "cql-json"}
resp = httpx.post(f"{raster_endpoint}/mosaic/register", json=query)
resp = httpx.post(f"{raster_endpoint}/searches/register", json=query)
assert resp.headers["content-type"] == "application/json"
assert resp.status_code == 200
assert resp.json()["searchid"]
assert resp.json()["id"]
assert resp.json()["links"]

searchid = resp.json()["searchid"]
searchid = resp.json()["id"]

resp = httpx.get(f"{raster_endpoint}/mosaic/{searchid}/-85.6358,36.1624/assets")
resp = httpx.get(f"{raster_endpoint}/searches/{searchid}/-85.6358,36.1624/assets")
assert resp.status_code == 200
assert len(resp.json()) == 1
assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"]
assert resp.json()[0]["id"] == "20200307aC0853900w361030"

resp = httpx.get(f"{raster_endpoint}/mosaic/{searchid}/tiles/15/8589/12849/assets")
resp = httpx.get(
f"{raster_endpoint}/searches/{searchid}/tiles/15/8589/12849/assets"
)
assert resp.status_code == 200
assert len(resp.json()) == 1
assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"]
assert resp.json()[0]["id"] == "20200307aC0853900w361030"

z, x, y = 15, 8589, 12849
resp = httpx.get(
f"{raster_endpoint}/mosaic/{searchid}/tiles/{z}/{x}/{y}",
f"{raster_endpoint}/searches/{searchid}/tiles/{z}/{x}/{y}",
params={"assets": "cog"},
headers={"Accept-Encoding": "br, gzip"},
timeout=10.0,
)
assert resp.status_code == 200
assert resp.headers["content-type"] == "image/jpeg"
assert "content-encoding" not in resp.headers


def test_mosaic_collection_api():
"""test mosaic collection."""
resp = httpx.get(
f"{raster_endpoint}/collections/noaa-emergency-response/-85.6358,36.1624/assets"
)
assert resp.status_code == 200
assert len(resp.json()) == 1
assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"]
assert resp.json()[0]["id"] == "20200307aC0853900w361030"

resp = httpx.get(
f"{raster_endpoint}/collections/noaa-emergency-response/tiles/15/8589/12849/assets"
)
assert resp.status_code == 200
assert len(resp.json()) == 1
assert list(resp.json()[0]) == ["id", "bbox", "assets", "collection"]
assert resp.json()[0]["id"] == "20200307aC0853900w361030"

z, x, y = 15, 8589, 12849
resp = httpx.get(
f"{raster_endpoint}/collections/noaa-emergency-response/tiles/{z}/{x}/{y}",
params={"assets": "cog"},
headers={"Accept-Encoding": "br, gzip"},
timeout=10.0,
Expand Down Expand Up @@ -102,11 +134,11 @@ def test_mosaic_search():
},
]
for search in searches:
resp = httpx.post(f"{raster_endpoint}/mosaic/register", json=search)
resp = httpx.post(f"{raster_endpoint}/searches/register", json=search)
assert resp.status_code == 200
assert resp.json()["searchid"]
assert resp.json()["id"]

resp = httpx.get(f"{raster_endpoint}/mosaic/list")
resp = httpx.get(f"{raster_endpoint}/searches/list")
assert resp.headers["content-type"] == "application/json"
assert resp.status_code == 200
assert (
Expand All @@ -122,9 +154,11 @@ def test_mosaic_search():
assert len(links) == 2
assert links[0]["rel"] == "self"
assert links[1]["rel"] == "next"
assert links[1]["href"] == f"{raster_endpoint}/mosaic/list?limit=10&offset=10"
assert links[1]["href"] == f"{raster_endpoint}/searches/list?limit=10&offset=10"

resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"limit": 1, "offset": 1})
resp = httpx.get(
f"{raster_endpoint}/searches/list", params={"limit": 1, "offset": 1}
)
assert resp.status_code == 200
assert resp.json()["context"]["matched"] > 10
assert resp.json()["context"]["limit"] == 1
Expand All @@ -133,33 +167,33 @@ def test_mosaic_search():
links = resp.json()["links"]
assert len(links) == 3
assert links[0]["rel"] == "self"
assert links[0]["href"] == f"{raster_endpoint}/mosaic/list?limit=1&offset=1"
assert links[0]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=1"
assert links[1]["rel"] == "next"
assert links[1]["href"] == f"{raster_endpoint}/mosaic/list?limit=1&offset=2"
assert links[1]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=2"
assert links[2]["rel"] == "prev"
assert links[2]["href"] == f"{raster_endpoint}/mosaic/list?limit=1&offset=0"
assert links[2]["href"] == f"{raster_endpoint}/searches/list?limit=1&offset=0"

# Filter on mosaic metadata
resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"owner": "vincent"})
resp = httpx.get(f"{raster_endpoint}/searches/list", params={"owner": "vincent"})
assert resp.status_code == 200
assert resp.json()["context"]["matched"] == 7
assert resp.json()["context"]["limit"] == 10
assert resp.json()["context"]["returned"] == 7

# sortBy
resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "lastused"})
resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "lastused"})
assert resp.status_code == 200

resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "usecount"})
resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "usecount"})
assert resp.status_code == 200

resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "-owner"})
resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "-owner"})
assert resp.status_code == 200
assert (
"owner" not in resp.json()["searches"][0]["search"]["metadata"]
) # some mosaic don't have owners

resp = httpx.get(f"{raster_endpoint}/mosaic/list", params={"sortby": "owner"})
resp = httpx.get(f"{raster_endpoint}/searches/list", params={"sortby": "owner"})
assert resp.status_code == 200
assert "owner" in resp.json()["searches"][0]["search"]["metadata"]

Expand Down
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ services:
# At the time of writing, rasterio and psycopg wheels are not available for arm64 arch
# so we force the image to be built with linux/amd64
platform: linux/amd64
image: ghcr.io/stac-utils/titiler-pgstac:0.8.0
image: ghcr.io/stac-utils/titiler-pgstac:1.0.0a3
ports:
- "${MY_DOCKER_IP:-127.0.0.1}:8082:8082"
environment:
Expand Down Expand Up @@ -89,7 +89,7 @@ services:
- ./dockerfiles/scripts:/tmp/scripts

tipg:
image: ghcr.io/developmentseed/tipg:0.4.4
image: ghcr.io/developmentseed/tipg:0.5.0
ports:
- "${MY_DOCKER_IP:-127.0.0.1}:8083:8083"
environment:
Expand Down
152 changes: 106 additions & 46 deletions runtime/eoapi/raster/eoapi/raster/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,13 @@
from titiler.core.middleware import CacheControlMiddleware
from titiler.mosaic.errors import MOSAIC_STATUS_CODES
from titiler.pgstac.db import close_db_connection, connect_to_db
from titiler.pgstac.dependencies import ItemPathParams
from titiler.pgstac.factory import MosaicTilerFactory
from titiler.pgstac.dependencies import CollectionIdParams, ItemIdParams, SearchIdParams
from titiler.pgstac.extensions import searchInfoExtension
from titiler.pgstac.factory import (
MosaicTilerFactory,
add_search_list_route,
add_search_register_route,
)
from titiler.pgstac.reader import PgSTACReader

try:
Expand Down Expand Up @@ -86,56 +91,100 @@ async def lifespan(app: FastAPI):
)

###############################################################################
# MOSAIC Endpoints
mosaic = MosaicTilerFactory(
router_prefix="/mosaic",
# add /statistics [POST]
# `Secret` endpoint for mosaic builder. Do not need to be public (in the OpenAPI docs)
@app.get("/collections", include_in_schema=False)
async def list_collection(request: Request):
"""list collections."""
with request.app.state.dbpool.connection() as conn:
with conn.cursor(row_factory=dict_row) as cursor:
cursor.execute("SELECT * FROM pgstac.all_collections();")
r = cursor.fetchone()
return r.get("all_collections", [])


###############################################################################
# STAC Search Endpoints
searches = MosaicTilerFactory(
path_dependency=SearchIdParams,
router_prefix="/searches/{search_id}",
add_statistics=True,
# add /map viewer
add_viewer=True,
# add /mosaic/list endpoint
add_mosaic_list=True,
# add `/bbox` and `/feature [POST]` endpoint
add_part=False,
add_part=True,
extensions=[
searchInfoExtension(),
],
)
app.include_router(
searches.router, tags=["STAC Search"], prefix="/searches/{search_id}"
)

add_search_register_route(
app,
prefix="/searches",
tile_dependencies=[
searches.layer_dependency,
searches.dataset_dependency,
searches.pixel_selection_dependency,
searches.tile_dependency,
searches.process_dependency,
searches.rescale_dependency,
searches.colormap_dependency,
searches.render_dependency,
searches.pgstac_dependency,
searches.reader_dependency,
searches.backend_dependency,
],
tags=["STAC Search"],
)
add_search_list_route(app, prefix="/searches", tags=["STAC Search"])


@mosaic.router.get("/builder", response_class=HTMLResponse)
async def mosaic_builder(request: Request):
@app.get("/searches/builder", response_class=HTMLResponse, tags=["STAC Search"])
async def virtual_mosaic_builder(request: Request):
"""Mosaic Builder Viewer."""
base_url = str(request.base_url)
return templates.TemplateResponse(
name="mosaic-builder.html",
context={
"request": request,
"register_endpoint": mosaic.url_for(request, "register_search"),
"collections_endpoint": str(request.url_for("list_collection")),
"register_endpoint": str(
app.url_path_for("register_search").make_absolute_url(base_url=base_url)
),
"collections_endpoint": str(
app.url_path_for("list_collection").make_absolute_url(base_url=base_url)
),
},
media_type="text/html",
)


# `Secret` endpoint for mosaic builder. Do not need to be public (in the OpenAPI docs)
@app.get("/collections", include_in_schema=False)
async def list_collection(request: Request):
"""list collections."""
with request.app.state.dbpool.connection() as conn:
with conn.cursor(row_factory=dict_row) as cursor:
cursor.execute("SELECT * FROM pgstac.all_collections();")
r = cursor.fetchone()
return r.get("all_collections", [])

###############################################################################
# STAC COLLECTION Endpoints
collection = MosaicTilerFactory(
path_dependency=CollectionIdParams,
router_prefix="/collections/{collection_id}",
add_statistics=True,
add_viewer=True,
add_part=True,
)
app.include_router(
collection.router, tags=["STAC Collection"], prefix="/collections/{collection_id}"
)

app.include_router(mosaic.router, tags=["Mosaic"], prefix="/mosaic")

###############################################################################
# STAC Item Endpoints
stac = MultiBaseTilerFactory(
reader=PgSTACReader,
path_dependency=ItemPathParams,
path_dependency=ItemIdParams,
router_prefix="/collections/{collection_id}/items/{item_id}",
# add /map viewer
add_viewer=True,
)
app.include_router(
stac.router,
tags=["STAC Item"],
prefix="/collections/{collection_id}/items/{item_id}",
)


@stac.router.get("/viewer", response_class=HTMLResponse)
Expand All @@ -155,9 +204,12 @@ def viewer(request: Request, item: pystac.Item = Depends(stac.path_dependency)):


app.include_router(
stac.router, tags=["Item"], prefix="/collections/{collection_id}/items/{item_id}"
stac.router,
tags=["STAC Item"],
prefix="/collections/{collection_id}/items/{item_id}",
)


###############################################################################
# Tiling Schemes Endpoints
tms = TMSFactory()
Expand Down Expand Up @@ -216,44 +268,52 @@ def landing(request: Request):
"rel": "service-doc",
},
{
"title": "STAC Item Asset's Info (template URL)",
"href": stac.url_for(request, "info"),
"title": "PgSTAC Virtual Mosaic list (JSON)",
"href": str(app.url_path_for("list_searches")),
"type": "application/json",
"rel": "data",
},
{
"title": "STAC Item Viewer (template URL)",
"href": stac.url_for(request, "viewer"),
"title": "PgSTAC Virtual Mosaic builder",
"href": str(app.url_path_for("virtual_mosaic_builder")),
"type": "text/html",
"rel": "data",
},
{
"title": "STAC Mosaic List (JSON)",
"href": mosaic.url_for(request, "list_mosaic"),
"type": "application/json",
"title": "PgSTAC Virtual Mosaic viewer (template URL)",
"href": str(app.url_path_for("map_viewer", search_id="{search_id}")),
"type": "text/html",
"rel": "data",
},
{
"title": "STAC Mosaic Builder",
"href": mosaic.url_for(request, "mosaic_builder"),
"title": "PgSTAC Collection viewer (template URL)",
"href": str(
app.url_path_for("map_viewer", collection_id="{collection_id}")
),
"type": "text/html",
"rel": "data",
},
{
"title": "STAC Mosaic Metadata (template URL)",
"href": mosaic.url_for(request, "info_search", searchid="{searchid}"),
"type": "application/json",
"title": "PgSTAC Item viewer (template URL)",
"href": str(
app.url_path_for(
"map_viewer",
collection_id="{collection_id}",
item_id="{item_id}",
)
),
"type": "text/html",
"rel": "data",
},
{
"title": "STAC Mosaic viewer (template URL)",
"href": mosaic.url_for(request, "map_viewer", searchid="{searchid}"),
"title": "TiTiler-PgSTAC Documentation (external link)",
"href": "https://stac-utils.github.io/titiler-pgstac/",
"type": "text/html",
"rel": "data",
"rel": "doc",
},
{
"title": "TiTiler-pgSTAC Documentation (external link)",
"href": "https://stac-utils.github.io/titiler-pgstac/",
"title": "TiTiler-PgSTAC source code (external link)",
"href": "https://github.com/stac-utils/titiler-pgstac",
"type": "text/html",
"rel": "doc",
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,7 @@
var info_link = data.links.filter((link) => link.rel == 'metadata');
var tilejson_link = data.links.filter((link) => link.rel == 'tilejson');
var map_link = data.links.filter((link) => link.rel == 'map');
var mosaic_info_str = `<p class="mt6">MosaicID: <strong>${data.searchid}</strong></p><p>Links:</p>`
var mosaic_info_str = `<p class="mt6">MosaicID: <strong>${data.id}</strong></p><p>Links:</p>`
if (info_link) {
mosaic_info_str += `<p class="mt6">- <a href="${info_link[0].href}" target="_blank">Mosaic Info Link</a></p>`
}
Expand Down
Loading

0 comments on commit f5cd23d

Please sign in to comment.