From ede6852657bb6276512223f75b349750ddf7b878 Mon Sep 17 00:00:00 2001 From: malice Date: Mon, 25 May 2026 15:33:47 -0600 Subject: [PATCH] =?UTF-8?q?Rename=20mode=20constants:=20mtb=E2=86=922w,=20?= =?UTF-8?q?atv=E2=86=924w=20(#20)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rename mode constants: mtb->2w, atv->4w Rename the OFFROUTE travel-mode identifiers mtb->2w and atv->4w across backend and frontend. MVUM vehicle-access classes/columns (atv, motorcycle) are a separate vocabulary and are left untouched. UI labels (MTB/ATV) and the cost.py __main__ demo local variables (cannot be digit-initial identifiers) are unchanged. Backend: MODE_PROFILES, MODE_TO_COSTING, MODE_TO_VALID_HIGHWAYS, AUTO_MODE_PRIORITY, _MODES_* sets, CATEGORY_ELIGIBLE_MODES, route()/compute_cost*/_pathfind_wilderness Literals, VALID_MODES, and tests. offroute_route.py adds a backward-compat shim mapping legacy mode=mtb->2w and mode=atv->4w before validation so bookmarked URLs still work. Frontend: store.js routeMode doc, DirectionsPanel TRAVEL_MODES ids + SELECTED_MODE_LABEL keys, Panel TRAVEL_MODES ids, ManeuverList network_mode->verb map keys, api.js jsdoc. Co-Authored-By: Claude Opus 4.7 (1M context) * Fix user-visible labels: MTB->2W, ATV->4W + grep cleanup Update the user-facing travel-mode labels to match the renamed ids: - DirectionsPanel TRAVEL_MODES (2w -> "2W", 4w -> "4W") + SELECTED_MODE_LABEL values. - Panel TRAVEL_MODES labels. - Stale test comment: mode=mtb -> mode=2w. Grep pass over frontend/src + backend/services/navi_offroute found no remaining mode-identifier string literals to rename. Residual hits are all out-of-scope: MVUM vehicle-access vocabulary (mvum.py, /api/mvum output, single-quoted test fixtures), the cost.py __main__ demo locals, the offroute_route.py back-compat shim, and comments referencing the historical names. Co-Authored-By: Claude Opus 4.7 (1M context) --------- Co-authored-by: Matt Co-authored-by: Claude Opus 4.7 (1M context) --- backend/services/navi_offroute/cost.py | 20 ++++----- .../services/navi_offroute/offroute_route.py | 11 +++-- backend/services/navi_offroute/router.py | 44 +++++++++---------- .../navi_offroute/tests/test_offroute.py | 38 ++++++++-------- frontend/src/api.js | 2 +- frontend/src/components/DirectionsPanel.jsx | 6 +-- frontend/src/components/ManeuverList.jsx | 6 +-- frontend/src/components/Panel.jsx | 4 +- frontend/src/store.js | 2 +- 9 files changed, 69 insertions(+), 64 deletions(-) diff --git a/backend/services/navi_offroute/cost.py b/backend/services/navi_offroute/cost.py index c8dd343..e5049db 100755 --- a/backend/services/navi_offroute/cost.py +++ b/backend/services/navi_offroute/cost.py @@ -1,7 +1,7 @@ """ Multi-mode travel cost functions for OFFROUTE. -Supports four travel modes: foot, mtb, atv, vehicle. +Supports four travel modes: foot, 2w, 4w, vehicle. Each mode has its own speed function, max slope, trail access rules, and terrain friction overrides. @@ -120,8 +120,8 @@ MODE_PROFILES: Dict[str, ModeProfile] = { wilderness_impassable=False, ), - "mtb": ModeProfile( - name="mtb", + "2w": ModeProfile( + name="2w", description="Mountain bike / dirt bike (Herzog wheeled model)", speed_function="herzog", base_speed_kmh=12.0, @@ -143,8 +143,8 @@ MODE_PROFILES: Dict[str, ModeProfile] = { wilderness_impassable=True, ), - "atv": ModeProfile( - name="atv", + "4w": ModeProfile( + name="4w", description="ATV / side-by-side (Herzog wheeled model, higher base speed)", speed_function="herzog", base_speed_kmh=25.0, @@ -225,7 +225,7 @@ def compute_cost_multiplier_grid( friction: Optional[np.ndarray] = None, friction_raw: Optional[np.ndarray] = None, wilderness: Optional[np.ndarray] = None, - mode: Literal["foot", "mtb", "atv", "vehicle"] = "foot", + mode: Literal["foot", "2w", "4w", "vehicle"] = "foot", ) -> np.ndarray: """Per-cell SLOPE-FREE context cost multiplier for the anisotropic A* pathfinder. @@ -300,7 +300,7 @@ def compute_cost_grid( wilderness: Optional[np.ndarray] = None, mvum: Optional[np.ndarray] = None, boundary_mode: Literal["strict", "pragmatic", "emergency"] = "pragmatic", - mode: Literal["foot", "mtb", "atv", "vehicle"] = "foot" + mode: Literal["foot", "2w", "4w", "vehicle"] = "foot" ) -> np.ndarray: """ Compute isotropic travel cost grid from elevation data. @@ -327,7 +327,7 @@ def compute_cost_grid( MVUM closures respond to boundary_mode (strict/pragmatic/emergency). Foot mode should pass None (MVUM is motor-vehicle specific). boundary_mode: How to handle barriers ("strict", "pragmatic", "emergency") - mode: Travel mode ("foot", "mtb", "atv", "vehicle") + mode: Travel mode ("foot", "2w", "4w", "vehicle") Returns: 2D array of travel cost in seconds per cell. @@ -550,7 +550,7 @@ if __name__ == "__main__": trails = np.zeros((10, 10), dtype=np.uint8) trails[5, :] = 5 # Road across middle - for mode_name in ["foot", "mtb", "atv", "vehicle"]: + for mode_name in ["foot", "2w", "4w", "vehicle"]: cost = compute_cost_grid( elev, cell_size_m=30.0, friction=friction, @@ -567,7 +567,7 @@ if __name__ == "__main__": wilderness = np.zeros((10, 10), dtype=np.uint8) wilderness[3:7, 3:7] = 255 - for mode_name in ["foot", "mtb", "atv", "vehicle"]: + for mode_name in ["foot", "2w", "4w", "vehicle"]: cost = compute_cost_grid( elev, cell_size_m=30.0, wilderness=wilderness, diff --git a/backend/services/navi_offroute/offroute_route.py b/backend/services/navi_offroute/offroute_route.py index be15aa9..0a27c59 100644 --- a/backend/services/navi_offroute/offroute_route.py +++ b/backend/services/navi_offroute/offroute_route.py @@ -18,7 +18,7 @@ logger = logging.getLogger('navi_offroute.route') bp = Blueprint('offroute', __name__) -VALID_MODES = ("auto", "foot", "mtb", "atv", "vehicle") +VALID_MODES = ("auto", "foot", "2w", "4w", "vehicle") VALID_BOUNDARY_MODES = ("strict", "pragmatic", "emergency") @@ -29,7 +29,7 @@ def api_offroute(): Request body: {start:[lat,lon], end:[lat,lon], - mode: auto|foot|mtb|atv|vehicle (default foot), + mode: auto|foot|2w|4w|vehicle (default foot), boundary_mode: strict|pragmatic|emergency (default pragmatic)} Response: {status:"ok", route:, summary:{...}} @@ -53,8 +53,13 @@ def api_offroute(): end_lat, end_lon = float(end[0]), float(end[1]) mode = data.get("mode", "foot") + # Backward-compat: map legacy mode names from bookmarked URLs. + if mode == "mtb": + mode = "2w" + elif mode == "atv": + mode = "4w" if mode not in VALID_MODES: - return jsonify({"status": "error", "message": "mode must be auto, foot, mtb, atv, or vehicle"}), 400 + return jsonify({"status": "error", "message": "mode must be auto, foot, 2w, 4w, or vehicle"}), 400 boundary_mode = data.get("boundary_mode", "pragmatic") if boundary_mode not in VALID_BOUNDARY_MODES: diff --git a/backend/services/navi_offroute/router.py b/backend/services/navi_offroute/router.py index 9de3a15..82d7fe6 100755 --- a/backend/services/navi_offroute/router.py +++ b/backend/services/navi_offroute/router.py @@ -11,7 +11,7 @@ Off-network detection: Valhalla /locate snap distance > 500m = off-network. IMPORTANT: The wilderness segment ALWAYS uses foot mode for pathfinding. The user's selected mode affects: - 1. Which entry points are valid (foot=any, mtb=tracks+roads, vehicle=roads only) + 1. Which entry points are valid (foot=any, 2w=tracks+roads, vehicle=roads only) 2. The Valhalla costing profile for the network segment """ import gc @@ -83,20 +83,20 @@ PATH_USE_VALUES = frozenset({ MODE_TO_COSTING = { "auto": "auto", "foot": "pedestrian", - "mtb": "bicycle", - "atv": "auto", + "2w": "bicycle", + "4w": "auto", "vehicle": "auto", } # Auto mode probes these concrete modes in capability order (most -> least # demanding terrain) and uses the first that yields a usable route. -AUTO_MODE_PRIORITY = ["vehicle", "atv", "mtb", "foot"] +AUTO_MODE_PRIORITY = ["vehicle", "4w", "2w", "foot"] # Per-endpoint travel-mode eligibility from an OSM-style "key:value" category hint. # Looked up exact first, then "key:*" wildcard (see _eligible_modes_from_category). -_MODES_ALL = frozenset({"vehicle", "atv", "mtb", "foot"}) -_MODES_TRACK = frozenset({"atv", "mtb", "foot"}) -_MODES_PATH = frozenset({"mtb", "foot"}) +_MODES_ALL = frozenset({"vehicle", "4w", "2w", "foot"}) +_MODES_TRACK = frozenset({"4w", "2w", "foot"}) +_MODES_PATH = frozenset({"2w", "foot"}) _MODES_FOOT = frozenset({"foot"}) CATEGORY_ELIGIBLE_MODES = { @@ -115,9 +115,9 @@ CATEGORY_ELIGIBLE_MODES = { "place:village": _MODES_ALL, "place:hamlet": _MODES_ALL, "place:suburb": _MODES_ALL, "place:neighbourhood": _MODES_ALL, "railway:station": _MODES_ALL, - # Track-like -> atv/mtb/foot + # Track-like -> 4w/2w/foot "highway:track": _MODES_TRACK, "highway:trailhead": _MODES_TRACK, - # Path-like -> mtb/foot + # Path-like -> 2w/foot "highway:path": _MODES_PATH, "highway:bridleway": _MODES_PATH, # Foot-only "highway:footway": _MODES_FOOT, "highway:steps": _MODES_FOOT, @@ -130,15 +130,15 @@ CATEGORY_ELIGIBLE_MODES = { } # Mode to valid entry point highway classes -# foot = any trail/track/road, mtb = tracks and roads, vehicle = roads only +# foot = any trail/track/road, 2w = tracks and roads, vehicle = roads only MODE_TO_VALID_HIGHWAYS = { "auto": {"primary", "secondary", "tertiary", "unclassified", "residential", "service"}, "foot": {"primary", "secondary", "tertiary", "unclassified", "residential", "service", "track", "path", "footway", "bridleway"}, - "mtb": {"primary", "secondary", "tertiary", "unclassified", "residential", + "2w": {"primary", "secondary", "tertiary", "unclassified", "residential", "service", "track"}, - "atv": {"primary", "secondary", "tertiary", "unclassified", "residential", + "4w": {"primary", "secondary", "tertiary", "unclassified", "residential", "service", "track"}, "vehicle": {"primary", "secondary", "tertiary", "unclassified", "residential", "service"}, @@ -597,7 +597,7 @@ class OffrouteRouter: start_lon: float, end_lat: float, end_lon: float, - mode: Literal["auto", "foot", "mtb", "atv", "vehicle"] = "foot", + mode: Literal["auto", "foot", "2w", "4w", "vehicle"] = "foot", boundary_mode: Literal["strict", "pragmatic", "emergency"] = "pragmatic", start_category: Optional[str] = None, end_category: Optional[str] = None @@ -614,7 +614,7 @@ class OffrouteRouter: Args: start_lat, start_lon: Starting coordinates end_lat, end_lon: Destination coordinates - mode: Travel mode (foot, mtb, atv, vehicle) + mode: Travel mode (foot, 2w, 4w, vehicle) boundary_mode: How to handle private land (strict, pragmatic, emergency) Returns a GeoJSON FeatureCollection with route segments. @@ -704,8 +704,8 @@ class OffrouteRouter: Runs the three distinct costings (auto/pedestrian/bicycle) in parallel and applies the per-mode snap-distance + road-class rules. snap_cache dedupes /locate results within a single request.""" - # auto costing -> vehicle/atv reach, bicycle -> mtb reach, pedestrian -> foot - costing_modes = {"auto": "vehicle", "pedestrian": "foot", "bicycle": "mtb"} + # auto costing -> vehicle/4w reach, bicycle -> 2w reach, pedestrian -> foot + costing_modes = {"auto": "vehicle", "pedestrian": "foot", "bicycle": "2w"} need = [c for c in costing_modes if (lat, lon, c) not in snap_cache] if need: with ThreadPoolExecutor(max_workers=3) as ex: @@ -727,15 +727,15 @@ class OffrouteRouter: (d_auto <= AUTO_SNAP_RELAXED_M and cls_auto in PAVED_HIGHWAY_CLASSES and self._is_terrain_flat(lat, lon)): modes.add("vehicle") - # atv: near a paved road OR a track (auto costing) + # 4w: near a paved road OR a track (auto costing) if d_auto <= AUTO_SNAP_RELAXED_M and ( cls_auto in PAVED_HIGHWAY_CLASSES or use_auto in TRACK_USE_VALUES): - modes.add("atv") - # mtb: near a paved road OR a track/path (bicycle costing) + modes.add("4w") + # 2w: near a paved road OR a track/path (bicycle costing) if d_bike <= AUTO_SNAP_RELAXED_M and ( cls_bike in PAVED_HIGHWAY_CLASSES or use_bike in (TRACK_USE_VALUES | PATH_USE_VALUES)): - modes.add("mtb") + modes.add("2w") return frozenset(modes) def _route_auto( @@ -945,7 +945,7 @@ class OffrouteRouter: if not entry_points: if mode == "vehicle": msg = f"No roads found within {EXPANDED_SEARCH_RADIUS_KM}km. Try a different mode." - elif mode in ("mtb", "atv"): + elif mode in ("2w", "4w"): msg = f"No tracks or roads found within {EXPANDED_SEARCH_RADIUS_KM}km. Try foot mode." else: msg = f"No trail entry points found within {EXPANDED_SEARCH_RADIUS_KM}km of start." @@ -1025,7 +1025,7 @@ class OffrouteRouter: if not entry_points: if mode == "vehicle": msg = f"No roads found within {EXPANDED_SEARCH_RADIUS_KM}km of destination. Try a different mode." - elif mode in ("mtb", "atv"): + elif mode in ("2w", "4w"): msg = f"No tracks or roads found within {EXPANDED_SEARCH_RADIUS_KM}km of destination. Try foot mode." else: msg = f"No trail entry points found within {EXPANDED_SEARCH_RADIUS_KM}km of destination." diff --git a/backend/services/navi_offroute/tests/test_offroute.py b/backend/services/navi_offroute/tests/test_offroute.py index a25ed4d..fbad7b4 100644 --- a/backend/services/navi_offroute/tests/test_offroute.py +++ b/backend/services/navi_offroute/tests/test_offroute.py @@ -265,7 +265,7 @@ def test_admin_info_no_secrets_and_probes(client, monkeypatch): from services.navi_offroute.router import OffrouteRouter, AUTO_MODE_PRIORITY -ALL_MODES = frozenset({"vehicle", "atv", "mtb", "foot"}) +ALL_MODES = frozenset({"vehicle", "4w", "2w", "foot"}) def _stub_route(per_mode, calls): @@ -284,8 +284,8 @@ def _all_ok(): def test_eligible_modes_exact_match(): r = object.__new__(OffrouteRouter) assert r._eligible_modes_from_category("highway:residential") == ALL_MODES - assert r._eligible_modes_from_category("highway:track") == frozenset({"atv", "mtb", "foot"}) - assert r._eligible_modes_from_category("highway:path") == frozenset({"mtb", "foot"}) + assert r._eligible_modes_from_category("highway:track") == frozenset({"4w", "2w", "foot"}) + assert r._eligible_modes_from_category("highway:path") == frozenset({"2w", "foot"}) assert r._eligible_modes_from_category("highway:footway") == frozenset({"foot"}) @@ -329,8 +329,8 @@ def test_route_auto_falls_through_to_foot(monkeypatch): calls = [] per_mode = { "vehicle": {"status": "error", "message": "No roads found"}, - "atv": {"status": "error", "message": "No tracks found"}, - "mtb": {"status": "error", "message": "No tracks found"}, + "4w": {"status": "error", "message": "No tracks found"}, + "2w": {"status": "error", "message": "No tracks found"}, "foot": {"status": "ok"}, } monkeypatch.setattr(OffrouteRouter, "route", _stub_route(per_mode, calls)) @@ -338,7 +338,7 @@ def test_route_auto_falls_through_to_foot(monkeypatch): out = r._route_auto(42.0, -114.0, 42.5, -114.5, "pragmatic") assert out["status"] == "ok" assert out["selected_mode"] == "foot" - assert calls == ["vehicle", "atv", "mtb", "foot"] + assert calls == ["vehicle", "4w", "2w", "foot"] def test_route_auto_all_error_returns_error(monkeypatch): @@ -351,7 +351,7 @@ def test_route_auto_all_error_returns_error(monkeypatch): assert out["status"] == "error" assert "selected_mode" not in out assert out["selected_mode_set"] == sorted(ALL_MODES) - assert calls == ["vehicle", "atv", "mtb", "foot"] + assert calls == ["vehicle", "4w", "2w", "foot"] def test_route_auto_selected_mode_present_in_ok_response(monkeypatch): @@ -359,15 +359,15 @@ def test_route_auto_selected_mode_present_in_ok_response(monkeypatch): calls = [] per_mode = { "vehicle": {"status": "error", "message": "No roads found"}, - "atv": {"status": "ok", "route": {"type": "FeatureCollection", "features": []}}, - "mtb": {"status": "ok"}, + "4w": {"status": "ok", "route": {"type": "FeatureCollection", "features": []}}, + "2w": {"status": "ok"}, "foot": {"status": "ok"}, } monkeypatch.setattr(OffrouteRouter, "route", _stub_route(per_mode, calls)) r = object.__new__(OffrouteRouter) out = r._route_auto(42.0, -114.0, 42.5, -114.5, "pragmatic") assert out["status"] == "ok" - assert out["selected_mode"] == "atv" + assert out["selected_mode"] == "4w" # ── _route_auto with category type hints (real _eligible_modes_from_category) ── @@ -388,9 +388,9 @@ def test_route_auto_address_to_trailhead_picks_atv(monkeypatch): r = object.__new__(OffrouteRouter) out = r._route_auto(42.0, -114.0, 42.5, -114.5, "pragmatic", start_category="building:house", end_category="highway:trailhead") - assert out["selected_mode"] == "atv" + assert out["selected_mode"] == "4w" assert "vehicle" not in calls - assert out["selected_mode_set"] == sorted({"atv", "mtb", "foot"}) + assert out["selected_mode_set"] == sorted({"4w", "2w", "foot"}) def test_route_auto_address_to_peak_picks_foot(monkeypatch): @@ -436,7 +436,7 @@ def test_spatial_service_other_picks_vehicle(monkeypatch): r = object.__new__(OffrouteRouter) modes = r._spatial_eligible_modes(43.6, -116.2, {}) assert "vehicle" in modes - assert modes == frozenset({"vehicle", "atv", "mtb", "foot"}) + assert modes == frozenset({"vehicle", "4w", "2w", "foot"}) def test_spatial_use_track_picks_atv_mtb_foot(monkeypatch): @@ -445,7 +445,7 @@ def test_spatial_use_track_picks_atv_mtb_foot(monkeypatch): monkeypatch.setattr(OffrouteRouter, "_locate_on_network", _stub_locate_fixed(snap)) r = object.__new__(OffrouteRouter) modes = r._spatial_eligible_modes(43.6, -116.2, {}) - assert modes == frozenset({"atv", "mtb", "foot"}) + assert modes == frozenset({"4w", "2w", "foot"}) assert "vehicle" not in modes @@ -455,7 +455,7 @@ def test_spatial_use_footway_picks_mtb_foot(monkeypatch): monkeypatch.setattr(OffrouteRouter, "_locate_on_network", _stub_locate_fixed(snap)) r = object.__new__(OffrouteRouter) modes = r._spatial_eligible_modes(43.6, -116.2, {}) - assert modes == frozenset({"mtb", "foot"}) + assert modes == frozenset({"2w", "foot"}) def test_spatial_no_class_no_use_picks_foot(monkeypatch): @@ -635,7 +635,7 @@ def test_compute_cost_multiplier_grid_math(): fr = _np.full((4, 4), 30, dtype=_np.uint8) fr[0, 0] = 80 mult = compute_cost_multiplier_grid( - elev, 30.0, 30.0, friction=friction, friction_raw=fr, wilderness=None, mode="mtb") + elev, 30.0, 30.0, friction=friction, friction_raw=fr, wilderness=None, mode="2w") assert mult[1, 1] == 4.0 # 2.0 friction * 2.0 grass override assert _np.isinf(mult[0, 0]) # water impassable @@ -659,7 +659,7 @@ class _FakeGrid: def test_pathfind_wilderness_always_uses_foot_effort(monkeypatch): - # Even when called with mode="mtb", the wilderness cost is computed as foot: + # Even when called with mode="2w", the wilderness cost is computed as foot: # compute_cost_multiplier_grid receives mode="foot", and A* gets the foot speed # function (tobler=0), foot base speed (6.0), and foot trail friction. captured = {} @@ -688,10 +688,10 @@ def test_pathfind_wilderness_always_uses_foot_effort(monkeypatch): r.wilderness_reader = None # foot is not wilderness_impassable -> not loaded anyway ep = [{"lat": 44.001, "lon": -115.001, "highway_class": "track", "name": "t", "land_status": "open"}] - out = r._pathfind_wilderness(44.0, -115.0, 44.001, -115.001, ep, "pragmatic", "start", mode="mtb") + out = r._pathfind_wilderness(44.0, -115.0, 44.001, -115.001, ep, "pragmatic", "start", mode="2w") assert out["status"] == "ok" - assert captured["mult_mode"] == "foot" # cost grid built as foot despite mode=mtb + assert captured["mult_mode"] == "foot" # cost grid built as foot despite mode=2w assert captured["speed_function_id"] == 0 # tobler (foot) assert captured["base_speed"] == 6.0 # foot base speed assert captured["lookup"][5] == 0.1 # foot road diff --git a/frontend/src/api.js b/frontend/src/api.js index f2a0869..80b3291 100644 --- a/frontend/src/api.js +++ b/frontend/src/api.js @@ -331,7 +331,7 @@ const MVUM_URL = "/api/mvum" * Request an offroute route from the pathfinder API. * @param {object} start - { lat, lon } * @param {object} end - { lat, lon } - * @param {string} mode - auto | foot | mtb | atv | vehicle + * @param {string} mode - auto | foot | 2w | 4w | vehicle * @param {string} boundaryMode - strict | pragmatic | emergency * @param {string} [startCategory] - OSM "key:value" hint for the origin (Auto mode) * @param {string} [endCategory] - OSM "key:value" hint for the destination (Auto mode) diff --git a/frontend/src/components/DirectionsPanel.jsx b/frontend/src/components/DirectionsPanel.jsx index 6bd7339..b925422 100644 --- a/frontend/src/components/DirectionsPanel.jsx +++ b/frontend/src/components/DirectionsPanel.jsx @@ -10,13 +10,13 @@ import ManeuverList from "./ManeuverList" const TRAVEL_MODES = [ { id: "auto", label: "Auto", Icon: Zap }, { id: "foot", label: "Foot", Icon: Footprints }, - { id: "mtb", label: "MTB", Icon: Bike }, - { id: "atv", label: "ATV", Icon: Car }, + { id: "2w", label: "2W", Icon: Bike }, + { id: "4w", label: "4W", Icon: Car }, { id: "vehicle", label: "Drive", Icon: Car }, ] // Maps the backend's selected_mode to the chip label shown in the "Auto chose X" badge. -const SELECTED_MODE_LABEL = { vehicle: "Drive", atv: "ATV", mtb: "MTB", foot: "Foot" } +const SELECTED_MODE_LABEL = { vehicle: "Drive", "4w": "4W", "2w": "2W", foot: "Foot" } const BOUNDARY_MODES = [ { id: "strict", label: "Strict", Icon: Shield, title: "Avoid barriers" }, diff --git a/frontend/src/components/ManeuverList.jsx b/frontend/src/components/ManeuverList.jsx index 44d1ffc..f895467 100644 --- a/frontend/src/components/ManeuverList.jsx +++ b/frontend/src/components/ManeuverList.jsx @@ -106,7 +106,7 @@ function ManeuverIcon({ type }) { /** * Add transport mode prefix to network maneuver instruction. - * "Drive east on..." for auto, "Walk south on..." for foot, "Ride north on..." for mtb + * "Drive east on..." for auto, "Walk south on..." for foot, "Ride north on..." for 2w */ function formatNetworkInstruction(instruction, mode) { if (!instruction) return '' @@ -116,9 +116,9 @@ function formatNetworkInstruction(instruction, mode) { 'auto': 'Drive', 'foot': 'Walk', 'pedestrian': 'Walk', - 'mtb': 'Ride', + '2w': 'Ride', 'bicycle': 'Ride', - 'atv': 'Drive', + '4w': 'Drive', 'vehicle': 'Drive', } const verb = modeVerbs[mode] || 'Go' diff --git a/frontend/src/components/Panel.jsx b/frontend/src/components/Panel.jsx index 6f0a0ac..aa89144 100644 --- a/frontend/src/components/Panel.jsx +++ b/frontend/src/components/Panel.jsx @@ -14,8 +14,8 @@ import PlaceDetail from './PlaceDetail' const TRAVEL_MODES = [ { id: 'auto', label: 'Drive', Icon: Car }, { id: 'foot', label: 'Foot', Icon: Footprints }, - { id: 'mtb', label: 'MTB', Icon: Bike }, - { id: 'atv', label: 'ATV', Icon: Car }, + { id: '2w', label: '2W', Icon: Bike }, + { id: '4w', label: '4W', Icon: Car }, { id: 'vehicle', label: '4x4', Icon: Car }, ] diff --git a/frontend/src/store.js b/frontend/src/store.js index 90d8a06..febc3c4 100644 --- a/frontend/src/store.js +++ b/frontend/src/store.js @@ -31,7 +31,7 @@ export const useStore = create((set, get) => ({ routeStart: null, // { lat, lon, name } routeEnd: null, // { lat, lon, name } stops: [], // Intermediate waypoints only: [{ id, lat, lon, name }, ...] - routeMode: "auto", // auto | foot | mtb | atv | vehicle + routeMode: "auto", // auto | foot | 2w | 4w | vehicle boundaryMode: "strict", // strict | pragmatic | emergency routeResult: null, // Response from /api/offroute routeLoading: false,