gnss/gnss/api/routes/maps.py

110 lines
4.2 KiB
Python

import json
from pathlib import Path
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel, Field
from ..dependencies import get_raster_mbtiles_service, get_tile_service, get_vector_mbtiles_service
from ...services.raster_mbtiles import RasterMBTilesError, RasterMBTilesService
from ...services.tiles import TileCutError, TileService
from ...services.vector_mbtiles import VectorMBTilesError, VectorMBTilesService
from ...services.tilesets import TilesetError, TilesetService
from ... import config
router = APIRouter(tags=["maps"])
class MapIngestRequest(BaseModel):
path: str
tileset: str | None = None
min_zoom: int = Field(default=10, ge=0)
max_zoom: int = Field(default=16, ge=0)
@router.post("/maps/ingest")
async def ingest_map(
payload: MapIngestRequest,
tile_service: TileService = Depends(get_tile_service),
raster_mbtiles: RasterMBTilesService = Depends(get_raster_mbtiles_service),
vector_mbtiles: VectorMBTilesService = Depends(get_vector_mbtiles_service),
) -> dict:
p = Path(payload.path).expanduser().resolve()
if not p.exists():
raise HTTPException(status_code=400, detail=f"path not found: {p}")
tileset_svc = TilesetService(tilesets_dir=config.TILESSETS_DIR, default_tileset=config.DEFAULT_TILESET)
tileset_svc.ensure()
suggested_tileset = (payload.tileset or p.stem or "").strip()
tileset_name: str | None = None
if suggested_tileset:
try:
tileset_name = tileset_svc.create(suggested_tileset)
tileset_svc.set_active(tileset_name)
tile_service.set_tiles_dir(tileset_svc.resolve(tileset_name))
except TilesetError as exc:
raise HTTPException(status_code=400, detail=str(exc))
if p.suffix.lower() == ".mbtiles":
try:
info = vector_mbtiles.activate(p)
if tileset_name:
manifest = {"kind": "vector-mbtiles", "source": str(info.path), "format": info.metadata.get("format")}
(tileset_svc.resolve(tileset_name) / "gnss_tileset.json").write_text(
json.dumps(manifest, ensure_ascii=False, indent=2),
encoding="utf-8",
)
return {
"status": "ok",
"kind": "vector",
"driver": "mbtiles",
"path": str(info.path),
"format": info.metadata.get("format"),
"tileset": tileset_name,
}
except VectorMBTilesError:
pass
try:
info = raster_mbtiles.activate(p)
except RasterMBTilesError as exc:
raise HTTPException(status_code=400, detail=str(exc))
if tileset_name:
manifest = {"kind": "raster-mbtiles", "source": str(info.path), "format": info.metadata.get("format")}
(tileset_svc.resolve(tileset_name) / "gnss_tileset.json").write_text(
json.dumps(manifest, ensure_ascii=False, indent=2),
encoding="utf-8",
)
return {
"status": "ok",
"kind": "raster",
"driver": "mbtiles",
"path": str(info.path),
"format": info.metadata.get("format"),
"tileset": tileset_name,
}
if p.suffix.lower() in {".tif", ".tiff", ".vrt"}:
if payload.max_zoom < payload.min_zoom:
raise HTTPException(status_code=400, detail="max_zoom must be >= min_zoom")
if tileset_name:
tile_service.set_tiles_dir(tileset_svc.resolve(tileset_name))
try:
tile_service.slice_to_tiles(
source=p,
destination=tile_service.tiles_dir,
min_zoom=payload.min_zoom,
max_zoom=payload.max_zoom,
scheme="xyz",
)
except TileCutError as exc:
raise HTTPException(status_code=400, detail=str(exc))
return {
"status": "ok",
"kind": "raster",
"driver": "gdal2tiles",
"path": str(p),
"tiles_dir": str(tile_service.tiles_dir),
"tileset": tileset_name,
}
raise HTTPException(status_code=400, detail="unsupported input; expected .mbtiles or GeoTIFF/VRT")