Added Mapbox Road Avoidance - #71
Merged
Merged
Conversation
…lude_string to Navigation SDK
There was a problem hiding this comment.
Pull request overview
This PR improves Mapbox-based routing and navigation by preserving backend-provided Mapbox geometries, passing road-exclusion points into the native Mapbox Navigation SDK, and introducing a new backend path for dynamic road avoidance via Overpass-fetched/sampled centerline points.
Changes:
- Frontend: keep backend
full_geometryeven for “current location”, passexclude_stringthrough toNavigationScreen, and make preview camera framing declarative viabounds. - Backend: add Overpass endpoint fallbacks and centerline sampling for Mapbox road avoidance; add messaging when falling back to non-avoid routing.
- Repo hygiene: update
.gitignore(but currently with duplication) and add road points cache JSON files.
Reviewed changes
Copilot reviewed 10 out of 11 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
| frontend/src/components/route-preview.tsx | Stops discarding backend geometry for “current location” and forwards exclude_string into navigation params. |
| frontend/src/components/navigation-screen.tsx | Uses exclude_string directly when starting native Mapbox navigation. |
| frontend/src/components/mapbox-map-view.tsx | Replaces imperative fitBounds() with declarative Camera bounds for more reliable preview zooming. |
| backend/model.py | Tracks which routing attempt succeeded and appends a fallback explanation when avoidance can’t be honored. |
| backend/llm_gpt.py | Switches to always auto-detecting the running vLLM model name (but currently breaks the override contract). |
| backend/db.py | Adds JSON-backed cache load/store helpers for pre-sampled road centerline points. |
| backend/adapters/openrouteservice_adapter.py | Moves Overpass to HTTPS and switches to POST with a User-Agent header. |
| backend/adapters/mapbox_directions_adapter.py | Adds centerline fetching with Overpass fallbacks plus sampling logic to meet Mapbox exclusion limits (but has edge-case bugs). |
| backend/road_points_cache.json | Adds a road points cache artifact. |
| backend/road_points_cache_v2.json | Adds a road points cache artifact used by new backend cache code. |
| .gitignore | Adds ignore entries, but currently duplicates a large block and misses ignoring the v2 cache file. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+188
to
+190
| else: | ||
| indices = [int(i * (len(cached_points) - 1) / (points_per_road - 1)) for i in range(points_per_road)] | ||
| sampled = [cached_points[i] for i in indices] |
Comment on lines
+143
to
+149
| coords = [] | ||
| for line in geom.geoms: | ||
| # Distribute points proportionally among segments | ||
| segment_points = max(2, int(round((line.length / total_length) * num_points))) | ||
| dists = [i * (line.length / max(1, segment_points - 1)) for i in range(segment_points)] | ||
| coords.extend([[p.x, p.y] for p in [line.interpolate(d) for d in dists]]) | ||
| return coords |
Comment on lines
+115
to
+117
| if not lines: | ||
| print(f"[Mapbox Adapter] No geometry found for {road_name} on {endpoint}") | ||
| return [] |
Comment on lines
+82
to
+86
| bbox = "14.402,120.917,14.810,121.150" | ||
| query = f""" | ||
| [out:json][timeout:25]; | ||
| way["highway"]["name"~"{road_name}", i]({bbox}); | ||
| out geom; |
Comment on lines
+992
to
+993
| if successful_attempt_label == "retry_without_avoid": | ||
| response += f" However, I couldn't find an alternative route that completely avoids {', '.join(avoid_roads)}, so I generated the standard recommended route instead." |
Comment on lines
+64
to
+65
| # Always auto-detect model from the vLLM server to ensure we match what's actually running | ||
| effective_model = await _get_auto_model_name(client) |
Comment on lines
+521
to
+532
| async def load_road_points(road_name: str) -> list: | ||
| """ | ||
| Retrieves stored road centerline points for Mapbox avoidance. | ||
| """ | ||
| if os.path.exists(POINTS_CACHE_FILE): | ||
| try: | ||
| with open(POINTS_CACHE_FILE, "r") as f: | ||
| cache = json.load(f) | ||
| return cache.get(road_name.lower()) | ||
| except Exception: | ||
| pass | ||
| return None |
Comment on lines
+534
to
+550
| async def store_road_points(road_name: str, points: list): | ||
| """ | ||
| Stores road centerline points for Mapbox avoidance. | ||
| """ | ||
| cache = {} | ||
| if os.path.exists(POINTS_CACHE_FILE): | ||
| try: | ||
| with open(POINTS_CACHE_FILE, "r") as f: | ||
| cache = json.load(f) | ||
| except Exception: | ||
| pass | ||
| cache[road_name.lower()] = points | ||
| try: | ||
| with open(POINTS_CACHE_FILE, "w") as f: | ||
| json.dump(cache, f) | ||
| except Exception as e: | ||
| print(f"Failed to write road points cache: {e}") No newline at end of file |
| @@ -0,0 +1 @@ | |||
| {"lawton avenue": [[121.0470666, 14.5416227], [121.0469858751877, 14.539931092471093], [121.04702159605661, 14.538230058920899], [121.0467369309763, 14.53656561148466], [121.04595237323285, 14.53506151012516], [121.04437551252602, 14.534674774407257], [121.04267769593329, 14.53479206745881], [121.04097944595574, 14.534902380129632], [121.03928170605654, 14.534925784318004], [121.03766979560213, 14.53441540040989], [121.03631768291692, 14.533391704001293], [121.03505946337228, 14.532245781807166], [121.03387090352128, 14.531030485254364], [121.03290434189765, 14.529651454725505], [121.03180689308543, 14.528402137425589], [121.03030658239722, 14.52761027724015], [121.02873484516451, 14.526957775053404], [121.02738127975498, 14.52593617903127], [121.0260497, 14.5248827], [121.0261546, 14.5248073], [121.02747097398411, 14.52588798081789], [121.0288322103901, 14.526904165438031], [121.03040867026641, 14.527556089726739], [121.03191598256798, 14.528346892695728], [121.03300163639508, 14.529610133261386], [121.03397540162098, 14.5309930425771], [121.03517800418325, 14.532201172347872], [121.03643725484025, 14.533352139365073], [121.03778894743314, 14.534383914151048], [121.03942066475874, 14.534844795190692], [121.04112321496683, 14.53478575771468], [121.0428250500233, 14.534667573914719], [121.04452692072006, 14.534549374669398], [121.04607185945692, 14.535038103966226], [121.04685433044038, 14.53654944782412], [121.0471507405775, 14.53821617507452], [121.04710616173554, 14.539921350547834], [121.0471867, 14.5416152], [121.0549363, 14.5688283], [121.05516070910072, 14.56702871008611], [121.05455402795133, 14.565290751214098], [121.05421029715262, 14.56348829735885], [121.05404189768657, 14.56165750918172], [121.0534885, 14.5599103], [121.0535904, 14.559879], [121.05408746850532, 14.561630991013264], [121.05425429116612, 14.5634497402827], [121.0546063760168, 14.565235835284227], [121.0552136722109, 14.566960028849781], [121.0550514, 14.5687511], [121.0541, 14.5624044], [121.0540139, 14.5622849], [121.0541, 14.5624044], [121.0544188, 14.5647506], [121.0541685, 14.5622271], [121.0541, 14.5624044]]} No newline at end of file | |||
| @@ -0,0 +1 @@ | |||
| {"lawton avenue": [[121.0470666, 14.5416227], [121.0469858751877, 14.539931092471093], [121.04702159605661, 14.538230058920899], [121.0467369309763, 14.53656561148466], [121.04595237323285, 14.53506151012516], [121.04437551252602, 14.534674774407257], [121.04267769593329, 14.53479206745881], [121.04097944595574, 14.534902380129632], [121.03928170605654, 14.534925784318004], [121.03766979560213, 14.53441540040989], [121.03631768291692, 14.533391704001293], [121.03505946337228, 14.532245781807166], [121.03387090352128, 14.531030485254364], [121.03290434189765, 14.529651454725505], [121.03180689308543, 14.528402137425589], [121.03030658239722, 14.52761027724015], [121.02873484516451, 14.526957775053404], [121.02738127975498, 14.52593617903127], [121.0260497, 14.5248827], [121.0261546, 14.5248073], [121.02747097398411, 14.52588798081789], [121.0288322103901, 14.526904165438031], [121.03040867026641, 14.527556089726739], [121.03191598256798, 14.528346892695728], [121.03300163639508, 14.529610133261386], [121.03397540162098, 14.5309930425771], [121.03517800418325, 14.532201172347872], [121.03643725484025, 14.533352139365073], [121.03778894743314, 14.534383914151048], [121.03942066475874, 14.534844795190692], [121.04112321496683, 14.53478575771468], [121.0428250500233, 14.534667573914719], [121.04452692072006, 14.534549374669398], [121.04607185945692, 14.535038103966226], [121.04685433044038, 14.53654944782412], [121.0471507405775, 14.53821617507452], [121.04710616173554, 14.539921350547834], [121.0471867, 14.5416152], [121.0549363, 14.5688283], [121.05516070910072, 14.56702871008611], [121.05455402795133, 14.565290751214098], [121.05421029715262, 14.56348829735885], [121.05404189768657, 14.56165750918172], [121.0534885, 14.5599103], [121.0535904, 14.559879], [121.05408746850532, 14.561630991013264], [121.05425429116612, 14.5634497402827], [121.0546063760168, 14.565235835284227], [121.0552136722109, 14.566960028849781], [121.0550514, 14.5687511], [121.0541, 14.5624044], [121.0540139, 14.5622849], [121.0541, 14.5624044], [121.0544188, 14.5647506], [121.0541685, 14.5622271], [121.0541, 14.5624044]]} No newline at end of file | |||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
This PR resolves critical issues preventing Mapbox road avoidance from functioning properly, fixes route preview map zooming, and introduces robust, dynamic road avoidance using OpenStreetMap (Overpass API).
Key Changes & Fixes
Frontend Navigation & Mapbox Fixes
route-preview.tsxthat silently threw away backend Mapbox geometries when the origin was set to "Current Location", causing a fallback to Google Routes.exclude_stringis properly unpacked from the backend payload and passed directly into the<MapboxNavigation>SDK to prevent re-routing through avoided roads while driving.mapbox-map-view.tsx) from the unreliable imperativefitBounds()useEffectmethod to a declarativeboundsprop. The camera now smoothly and consistently frames the entire route.Backend Dynamic Road Avoidance
_get_road_centerline_pointsinmapbox_directions_adapter.pyto seamlessly iterate through multiple fallback Overpass API endpoints (overpass-api.de,lz4.overpass-api.de,kumi.systems). This prevents the system from silently failing due to aggressive OSM rate-limiting.Testing Performed