110 lines
4.2 KiB
Python
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")
|