Skip to content

Commit 018d199

Browse files
Zaid Salemclaude
authored andcommitted
fix(route): draw real road geometry for KB routes (Valhalla through wbkb waypoints)
wbkb chose the correct corridor but returned schematic straight-line geometry (~9 node points), so routes looked wrong on the map. Now kb_service routes Valhalla THROUGH the wbkb checkpoint waypoints (entrance enforced) to get dense road-following geometry, while keeping wbkb's checkpoint list + verdict + safety. plan() is async; handler awaits and injects valhalla_client.route. Schematic geometry remains as fallback if Valhalla fails. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent abad999 commit 018d199

2 files changed

Lines changed: 44 additions & 8 deletions

File tree

services/westbank-alerts/app/routers/route.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ async def v2_route(req: RouteRequest):
109109
# the Valhalla geometry router below. Kill-switch: WBKB_ROUTE_ENABLED=0.
110110
plan = None
111111
if os.environ.get("WBKB_ROUTE_ENABLED", "1") != "0":
112-
plan = kb_service.plan((req.from_.lat, req.from_.lon), (req.to.lat, req.to.lon), envelopes)
112+
plan = await kb_service.plan((req.from_.lat, req.from_.lon), (req.to.lat, req.to.lon),
113+
envelopes, route_fn=valhalla_client.route)
113114

114115
if plan is None:
115116
plan = await build_route_plan(

services/westbank-alerts/app/routing/kb_service.py

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,8 @@ def _envelope(node: dict, status: str) -> dict:
9393
}
9494

9595

96-
def _shape(res: dict, nodes: dict) -> dict:
97-
"""wbkb route result -> the /v2/route response contract (+ wbkb extras)."""
98-
coords, dist_km = [], 0.0
99-
prev = None
96+
def _schematic_coords(res: dict, nodes: dict) -> tuple[list, float]:
97+
coords, dist_km, prev = [], 0.0, None
10098
for nid in res["path_nodes"]:
10199
c = nodes[nid].get("coord")
102100
if not c:
@@ -105,6 +103,39 @@ def _shape(res: dict, nodes: dict) -> dict:
105103
if prev:
106104
dist_km += _km(prev[1], prev[0], c["lat"], c["lng"])
107105
prev = [c["lng"], c["lat"]]
106+
return coords, round(dist_km, 1)
107+
108+
109+
async def _real_geometry(res: dict, nodes: dict, route_fn) -> dict | None:
110+
"""Real road geometry from Valhalla, routed THROUGH the wbkb checkpoint
111+
waypoints so the drawn line follows roads and passes the correct entrance/
112+
gates. None on any failure → caller uses schematic coords."""
113+
pts = res.get("path_nodes") or []
114+
if len(pts) < 2:
115+
return None
116+
117+
def latlon(nid):
118+
c = nodes.get(nid, {}).get("coord")
119+
return (c["lat"], c["lng"]) if c else None
120+
121+
a, b = latlon(pts[0]), latlon(pts[-1])
122+
if not a or not b:
123+
return None
124+
via = [latlon(n) for n in pts[1:-1] if nodes.get(n, {}).get("type") == "checkpoint" and nodes[n].get("coord")]
125+
try:
126+
out = await route_fn([a, *via, b], alternates=0)
127+
except Exception: # noqa: BLE001
128+
log.exception("valhalla geometry for wbkb route failed")
129+
return None
130+
return out[0] if out else None
131+
132+
133+
def _shape(res: dict, nodes: dict, geom: dict | None = None) -> dict:
134+
"""wbkb route result -> the /v2/route response contract (+ wbkb extras)."""
135+
if geom and geom.get("coords"):
136+
coords, dist_km = geom["coords"], round(geom.get("distance_km", 0.0), 1)
137+
else:
138+
coords, dist_km = _schematic_coords(res, nodes)
108139

109140
onr, along = [], 0.0
110141
prevc = None
@@ -156,8 +187,11 @@ def _has_verified_edge(res: dict, edges: dict) -> bool:
156187
return False
157188

158189

159-
def plan(from_latlon: tuple[float, float], to_latlon: tuple[float, float], envelopes: list) -> dict | None:
160-
"""Try a KB route. Returns the /v2/route plan dict, or None to fall back."""
190+
async def plan(from_latlon: tuple[float, float], to_latlon: tuple[float, float],
191+
envelopes: list, route_fn=None) -> dict | None:
192+
"""Try a KB route. Returns the /v2/route plan dict, or None to fall back.
193+
``route_fn`` (valhalla_client.route) is used to draw real road geometry through
194+
the KB's checkpoint waypoints; without it the geometry is schematic."""
161195
if not _AVAILABLE:
162196
return None
163197
try:
@@ -179,7 +213,8 @@ def plan(from_latlon: tuple[float, float], to_latlon: tuple[float, float], envel
179213
if not _has_verified_edge(res, edges):
180214
return None
181215
assert_safe(res, edges) # never return a forbidden route
182-
return _shape(res, nodes)
216+
geom = await _real_geometry(res, nodes, route_fn) if route_fn else None
217+
return _shape(res, nodes, geom)
183218
except Exception: # noqa: BLE001 — any failure → Valhalla fallback
184219
log.exception("wbkb plan failed; falling back")
185220
return None

0 commit comments

Comments
 (0)