Skip to content

Added Mapbox Road Avoidance - #71

Merged
LourenzBani merged 8 commits into
developfrom
feature/mapbox-road-avoidance
Jul 15, 2026
Merged

Added Mapbox Road Avoidance#71
LourenzBani merged 8 commits into
developfrom
feature/mapbox-road-avoidance

Conversation

@LourenzBani

Copy link
Copy Markdown

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

  • Fixed Route Discarding Bug: Removed flawed logic in route-preview.tsx that silently threw away backend Mapbox geometries when the origin was set to "Current Location", causing a fallback to Google Routes.
  • Fixed Active Navigation Exclusions: Ensure exclude_string is properly unpacked from the backend payload and passed directly into the <MapboxNavigation> SDK to prevent re-routing through avoided roads while driving.
  • Fixed Map Zooming: Migrated the Mapbox map view (mapbox-map-view.tsx) from the unreliable imperative fitBounds() useEffect method to a declarative bounds prop. The camera now smoothly and consistently frames the entire route.

Backend Dynamic Road Avoidance

  • Resilient Overpass API Fetching: Refactored _get_road_centerline_points in mapbox_directions_adapter.py to 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.
  • Dynamic Road Caching: The backend can now dynamically intercept any road avoidance request, fetch its geometry, sample it to Mapbox's 50-point limit, and seamlessly store it in the JSON cache, removing the need for hardcoded paths.

Testing Performed

  • Verified frontend accurately renders Mapbox routes from "Current Location" without falling back to Google.
  • Confirmed Mapbox native SDK correctly honors the exclusion points while driving.
  • Tested dynamic avoidance of uncached roads to ensure seamless Overpass API fetching, fallback handling, and local caching.

Copilot AI review requested due to automatic review settings July 15, 2026 07:40
@LourenzBani
LourenzBani merged commit 1a41b24 into develop Jul 15, 2026
1 check passed

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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_geometry even for “current location”, pass exclude_string through to NavigationScreen, and make preview camera framing declarative via bounds.
  • 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 thread backend/model.py
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 thread backend/llm_gpt.py
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 thread backend/db.py
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 thread backend/db.py
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants