surgeink-api / surgeink /api /geocode.py
kredd25's picture
✨ feat(flood): add SurgeInk backend + flood risk visualization
fb2ab55
import httpx
from fastapi import APIRouter, HTTPException, Query
from surgeink.cache.redis_cache import cache_get_json, cache_set_json
from surgeink.config import settings
from surgeink.models.schemas import GeocodeResponse, GeocodeResult
router = APIRouter()
CACHE_TTL = 86400 # 24 hours
USER_AGENT = "SurgeInk/0.1 (flood-risk-viz)"
@router.get("/geocode", response_model=GeocodeResponse)
async def geocode(
q: str = Query(..., min_length=2, description="Search query"),
limit: int = Query(5, ge=1, le=10),
):
cache_key = f"geocode:{q.lower().strip()}:{limit}"
cached = await cache_get_json(cache_key)
if cached is not None:
return GeocodeResponse(**cached)
try:
async with httpx.AsyncClient(timeout=10.0) as client:
resp = await client.get(
f"{settings.nominatim_base_url}/search",
params={
"format": "jsonv2",
"addressdetails": 1,
"limit": limit,
"q": q,
},
headers={"User-Agent": USER_AGENT},
)
resp.raise_for_status()
except Exception as e:
raise HTTPException(502, detail=f"Nominatim API error: {e}")
raw_results = resp.json()
results = []
for item in raw_results:
# Nominatim bbox: [south, north, west, east] as strings
# Convert to [min_lng, min_lat, max_lng, max_lat]
raw_bbox = item.get("boundingbox", [])
if len(raw_bbox) == 4:
south, north, west, east = [float(v) for v in raw_bbox]
bbox = [west, south, east, north]
else:
bbox = []
results.append(GeocodeResult(
name=item.get("name", item.get("display_name", "").split(",")[0]),
display_name=item.get("display_name", ""),
lat=float(item.get("lat", 0)),
lng=float(item.get("lon", 0)),
bbox=bbox,
flood_data_available=True,
available_layers=[],
))
response_data = {"results": [r.model_dump() for r in results]}
await cache_set_json(cache_key, response_data, CACHE_TTL)
return GeocodeResponse(results=results)