import asyncio from typing import Optional from fastapi import APIRouter, HTTPException, Query from app.internal import ny511 router = APIRouter(prefix="/traffic", tags=["traffic"]) # Per-layer cap per response. A statewide view is ~3k cameras; past this the # map is unreadable anyway, and the client is told the list was cut. MAX_ITEMS = 1500 def _feed_status(feed: "ny511._Feed") -> dict: return {"fetched_at": feed.fetched_at, "error": feed.error} @router.get("/511") async def get_511( south: float = Query(..., ge=-90, le=90), west: float = Query(..., ge=-180, le=180), north: float = Query(..., ge=-90, le=90), east: float = Query(..., ge=-180, le=180), layers: Optional[str] = Query("cameras,events", description="comma list: cameras, events"), ): if south > north or west > east: raise HTTPException(400, "bbox must satisfy south<=north and west<=east") wanted = {s.strip() for s in (layers or "").split(",") if s.strip()} feeds = {name: getattr(ny511, name) for name in ("cameras", "events") if name in wanted} await asyncio.gather(*(f.get() for f in feeds.values())) out: dict = {} for name, feed in feeds.items(): hits = ny511.in_bbox(feed.items, south, west, north, east) out[name] = hits[:MAX_ITEMS] out[f"{name}_status"] = {**_feed_status(feed), "total_in_bbox": len(hits), "truncated": len(hits) > MAX_ITEMS} return out