feat(backend,web): tuned bike custom model on GraphHopper 12#72
Merged
Conversation
Replace the legacy single-vehicle bike profile with a custom-model bike profile tuned for our use case (cycle infra preference, calm-street boost, strong avoidance of trunk/primary, surface penalties). The image now ships GraphHopper 12, which removed `vehicle:`/`weighting:` from profiles and requires custom models to enforce per-direction access themselves; the new config matches that schema and explicitly blocks wrong-way oneway traversal via `bike_access` / `backward_bike_access`. The custom model JSON is mounted into the GH container from `backend/custom_models/`. CH preparation is disabled (`profiles_ch: []`) because custom weighting with dynamic per-request priority rules (rated areas, distance_influence) cannot be baked. Web: lower the default route-directness from 70 to 60 to match the custom model's distance_influence so first-load behavior is consistent.
This was referenced Apr 26, 2026
4 tasks
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.
Summary
vehicle: bike, weighting: customprofile with a single Add Fathom analytics via configurable build-time env var #12-style custom-modelbikeprofile mounted frombackend/custom_models/beebeebike.json.vehicle:/weighting:, nestturn_costs: { vehicle_types: [bicycle], u_turn_costs: 20 }, register custom models viacustom_models.directory: /custom_models.distance_influenceso first-load behavior is consistent (the slider can still override per request).What the custom model does
Layered on top of GH's built-in
bike_prioritybaseline, it applies these multipliers:Priority
!bike_access && (!backward_bike_access || roundabout) → 0, with the asymmetriconeway:bicycle=nocase allowed at0.2.bike_road_access == PRIVATE → 0.2;road_accessFORESTRY/AGRICULTURAL → 0.6,MILITARY → 0.1.road_environmentFERRY → 0.3,FORD → 0.2.mtb_rating > 2 → 0(no T2+ MTB segments).cycleway == TRACK → 1.3,cycleway == LANE → 1.1.get_off_bike → 0.6; surfaceCOBBLESTONE → 0.5, loose surfacesSAND/GROUND/DIRT → 0.7.Speed (km/h)
limit_to: bike_average_speed.limit_to: 6.>6° × 0.7,>12° × 0.5,<−6° × 1.2.Other
distance_influence: 60(slightly more willing to detour than the GH default of 70).u_turn_costs: 20(vs. GH default 10) to discourage tight reroutes.Notably not in this model: no road_class tuning (TRUNK/PRIMARY/SECONDARY/etc. all use
bike_priorityas-is), nobike_networkboost, no smoothness rules. Those can be added later if needed.Notes for reviewers
profiles_ch: []: CH preparation cannot bake custom weighting with dynamic per-request priority rules (rated-area injection, optionaldistance_influenceoverride). The per-request"ch.disable": trueinrouting.rsis now redundant but harmless.compose.ymlgains one bind mount:./backend/custom_models:/custom_models:ro.rating_weight+ optionaldistance_influence, noprofilefield.turn_costs/u_turn_costsrequires wiping the GH cache (data/osm/berlin/graphhopper/) and reimporting; otherwise GH refuses to start withProfile 'bike' does not match.Test plan
cargo test --lib(backend, 69 tests)cargo clippy --all-targets -- -D warningsnpm test -- --run(web, 71 tests)/routefor thebikeprofile