The repo ships two export tools whose GeoJSON output loads directly into webmap.dev’s overlay pair:
| Overlay (webmap.dev) | Exporter | Shows |
|---|---|---|
| Squeeze zones | cue-zone-export (webmap.dev#227) |
Where the imported region would cue — the static zones |
| Cue events | cue-events-export (webmap.dev#231) |
What one ride actually did — fired cues, their grades, delivery, and rider markers |
Loaded together they close the §13 tuning loop visually: the zones say where the policy watches, the events say what happened there.
Privacy first (NFR-005). Every file below — the Overpass extract, the ride trace, the latency sidecar, and both GeoJSON exports — reveals where you ride. They all stay on your machine:
*.geojson,/region.json, and/rides/are gitignored, and webmap.dev bundles no data for the same reason — you hand it a local file per session, nothing more. Never commit any of these; tests use hand-built synthetic fixtures only.
Both exporters take the same input the app’s “Import region…” picker
does: an Overpass out geom; JSON response for your riding
area’s bounding box (south, west, north, east):
QUERY='[out:json][timeout:60];
way["highway"~"^(primary|secondary|tertiary|residential|unclassified|trunk|primary_link|secondary_link|tertiary_link|living_street)$"](SOUTH,WEST,NORTH,EAST);
out geom;'
curl -sS --fail --max-time 90 -X POST --data-urlencode "data=$QUERY" \
https://overpass-api.de/api/interpreter > region.jsonThe highway-class filter matches rideableHighwayClasses
in the importer (and tools/osm_tag_audit.sh). The query
must end in out geom; — a tags-only fetch has no geometry
to export. If Overpass returns a remark field, the data is
truncated and both tools refuse it; retry or shrink the bbox. Note the
bbox itself is disclosed to the public Overpass endpoint — use a box
comfortably larger than your actual loop.
Export the scored zones through the same importer/scorer the app runs:
swift run cue-zone-export region.json -o squeeze-zones.geojsonThe summary line reports ways → segments → zones. Then, on webmap.dev:
squeeze-zones.geojson.A malformed or wrong-shape file is rejected at load with an error and the overlay stays empty — nothing partial is drawn or persisted.
Each zone renders as its member segment polylines; features sharing
an event_id are one logical zone. Popups show severity,
confidence, and the decoded reasons_bitmask (narrow lane /
no shoulder or bike lane / high-speed context).
After a ride, export the trace from the app (and the latency sidecar, if you want delivery facts), then join them with the region extract:
swift run cue-events-export ride-trace.json region.json \
--latency ride-latency.json -o cue-events.geojsonride-trace.json — the schema-v1 policy trace
(replay/replay_trace.schema.json).--latency — optional sidecar from the dispatcher’s
latency log; without it the delivered /
latency_ms fields are simply omitted.approx: true — “somewhere on this segment”, not a fix.segment_id has no match in the extract
(wrong region, or OSM edits changed the ids since the ride) is
skipped with a stderr warning and counted in the
summary; pass --strict to fail instead of
skipping. The summary also reports graded and latency-join counts, so a
silent mismatch can’t masquerade as a clean export.Load it the same way: layers control → Cue events → file picker.
Legend (webmap.dev#231):
| Marker | Meaning |
|---|---|
| Green point | outcome: useful |
| Red point | outcome: false_alarm |
| Orange point | outcome: too_late |
| Purple point | outcome: unrecognized |
| Gray point | ungraded (no review yet) |
| Dashed ring | delivered: false — the cue never reached the wrist |
| Triangle | rider marker (“unsafe here”, FR-006) |
Cue-event points sit on the zone that fired them — popups on both
overlays share event_id and segment_id, so a
point and its zone cross-reference directly. Patterns worth acting on in
the §13 loop:
lead_time_s against min_notice_s, and check
latency_ms — a slow watch delivery eats lead time the
kernel thinks it gave.unrecognized): check delivered/latency_ms
alongside it — a delivery/perceptibility problem, not a policy one.One stance, end to end: map data and ride data stay on the operator’s
machines. The repo’s .gitignore guards
*.geojson, /region.json, and
/rides/; webmap.dev holds nothing server-side and bundles
no data. If you share a screenshot, remember the base map itself shows
where you ride.