Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Endpoints to get a list of assets which overlap a point, box, or quadkey #351

Merged
merged 10 commits into from
Aug 6, 2021
31 changes: 30 additions & 1 deletion src/titiler/mosaic/tests/test_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def test_MosaicTilerFactory():
optional_headers=[OptionalHeader.server_timing, OptionalHeader.x_assets],
router_prefix="mosaic",
)
assert len(mosaic.router.routes) == 19
assert len(mosaic.router.routes) == 22
assert mosaic.tms_dependency == WebMercatorTMSParams

app = FastAPI()
Expand Down Expand Up @@ -142,3 +142,32 @@ def test_MosaicTilerFactory():
"/mosaic/validate", json=MosaicJSON.from_urls(assets).dict(),
)
assert response.status_code == 200

response = client.get("/mosaic/0302302/assets", params={"url": mosaic_file},)
assert response.status_code == 200
assert all(
filepath.split("/")[-1] in ["cog1.tif"] for filepath in response.json()
)

response = client.get("/mosaic/-71,46/assets", params={"url": mosaic_file},)
vincentsarago marked this conversation as resolved.
Show resolved Hide resolved
assert response.status_code == 200
assert all(
filepath.split("/")[-1] in ["cog1.tif", "cog2.tif"]
for filepath in response.json()
)

response = client.get(
"/mosaic/-75.9375,43.06888777416962,-73.125,45.089035564831015/assets",
params={"url": mosaic_file},
)
assert response.status_code == 200
assert all(
filepath.split("/")[-1] in ["cog1.tif", "cog2.tif"]
for filepath in response.json()
)

response = client.get(
"/mosaic/10,10,11,11/assets", params={"url": mosaic_file},
)
assert response.status_code == 200
assert response.json() == []
64 changes: 64 additions & 0 deletions src/titiler/mosaic/titiler/mosaic/factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from typing import Callable, Dict, Optional, Type
from urllib.parse import urlencode, urlparse

import mercantile
import rasterio
from cogeo_mosaic.backends import BaseBackend, MosaicBackend
from cogeo_mosaic.models import Info as mosaicInfo
Expand Down Expand Up @@ -64,6 +65,7 @@ def register_routes(self):
self.wmts()
self.point()
self.validate()
self.assets()

############################################################################
# /read
Expand Down Expand Up @@ -484,3 +486,65 @@ def validate(self):
def validate(body: MosaicJSON):
"""Validate a MosaicJSON"""
return True

def assets(self):
"""Register /assets endpoint."""

@self.router.get(
r"/{minx},{miny},{maxx},{maxy}/assets",
responses={200: {"description": "Return list of COGs in bounding box"}},
)
def bbox(
src_path=Depends(self.path_dependency),
minx: float = Query(None, description="Left side of bounding box"),
miny: float = Query(None, description="Bottom of bounding box"),
maxx: float = Query(None, description="Right side of bounding box"),
maxy: float = Query(None, description="Top of bounding box"),
):
"""Return a list of assets which overlap a bounding box"""
with self.reader(src_path, **self.backend_options) as mosaic:
tl_tile = mercantile.tile(minx, maxy, mosaic.minzoom)
br_tile = mercantile.tile(maxx, miny, mosaic.minzoom)
tiles = [
(x, y, mosaic.minzoom)
for x in range(tl_tile.x, br_tile.x + 1)
for y in range(tl_tile.y, br_tile.y + 1)
]
assets = list(
{
asset
for asset_list in [mosaic.assets_for_tile(*t) for t in tiles]
for asset in asset_list
}
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️


return assets

@self.router.get(
r"/{lng},{lat}/assets",
responses={200: {"description": "Return list of COGs"}},
)
def lonlat(
src_path=Depends(self.path_dependency),
lng: float = Query(None, description="Longitude"),
lat: float = Query(None, description="Latitude"),
):
"""Return a list of assets which overlap a point"""
with self.reader(src_path, **self.backend_options) as mosaic:
assets = mosaic.assets_for_point(lng, lat)

return assets

@self.router.get(
r"/{quadkey}/assets",
responses={200: {"description": "Return list of COGs"}},
)
def quadkey(
src_path=Depends(self.path_dependency),
quadkey: str = Query(None, description="Quadkey to return COGS for."),
):
"""Return a list of assets which overlap a given quadkey"""
with self.reader(src_path, **self.backend_options) as mosaic:
assets = mosaic.assets_for_tile(*mercantile.quadkey_to_tile(quadkey))

return assets