feat: implement turning circle support for u-turns#7388
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
- Add has_turning_facility field to ExtractionTurn structure - Detect turning facilities (turning_circle, turning_loop, mini_roundabout) at intersections - Expose has_turning_facility to Lua profiles via scripting environment - Modify car.lua to eliminate u-turn penalty at turning facilities - Add comprehensive test scenarios for turning circle u-turn behavior Co-authored-by: DennisOSRM <1067895+DennisOSRM@users.noreply.github.com>
This comment was marked as outdated.
This comment was marked as outdated.
- Apply clang-format to graph_compressor.cpp - Add turning circle feature to CHANGELOG.md - Document has_turning_facility field in profiles.md Co-authored-by: DennisOSRM <1067895+DennisOSRM@users.noreply.github.com>
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
* Initial plan * Fix turning facility detection by adding explicit highway-to-obstacle-type mapping The Lua profile was attempting to use obstacle_type[highway] as a table lookup, but Sol2's new_enum() doesn't automatically create string-indexed access. Added an explicit mapping table (highway_to_obstacle_type) to correctly map highway tag values like "turning_circle", "turning_loop", and "mini_roundabout" to their corresponding obstacle type enums. This fixes the issue where turning facilities were not being recognized during route calculation, causing u-turns to use direct u-turn penalties instead of taking advantage of designated turning facilities. Co-authored-by: DennisOSRM <1067895+DennisOSRM@users.noreply.github.com> --------- Co-authored-by: anthropic-code-agent[bot] <242468646+Claude@users.noreply.github.com> Co-authored-by: DennisOSRM <1067895+DennisOSRM@users.noreply.github.com>
- Add bearings (90,10 270,10) to all directional u-turn scenarios so OSRM is forced to make a real turn rather than returning a trivial zero-distance route for same start/end waypoints - Add no_u_turn turn restrictions to scenarios 1-3, modeling the real-world case where turning circles exist precisely because direct u-turns on the road are restricted - Fix expected routes: side-street turning facilities produce abc,bd,bd,abc,abc (not abc,bd,abc) since the outbound and inbound traversals of the spur road are reported separately - Fix expected turns: continue uturn + turn left (not turn right twice) - Fix one-way scenario: with bd being one-way the turning circle at d is inaccessible for u-turns, so OSRM correctly falls back to a regular u-turn on the main road (abc,abc,abc, continue uturn) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Instead of zero penalty, u-turns at turning circles, turning loops, and mini roundabouts now incur a small 5-second delay. This reflects the real-world time cost of navigating the facility while still being much cheaper than a regular u-turn penalty (20 seconds). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix Scenario 1 topology: extend main road to a---b---c---e so the dead-end is 2 hops from the junction (matching scenarios 2/3), preventing the cheaper dead-end u-turn at c from shadowing the turning_circle at d - Add 'Prefer turning_circle over plain dead end' scenario: topology with a---b + bc (plain dead end) + bd (turning_circle), no_u_turn at b. Directly validates that the 5s penalty beats the 20s penalty (29s via turning_circle vs 44s via dead end) - Remove dead code in graph_compressor.cpp: turning facilities are always Obstacle::Type::Incompressible, so the get_obstacle_penalty lambda is never reached for them; replace the any(via, Turning) query with a documented 'false' - Make turning_circle_penalty a configurable profile property (default 5s) instead of a hardcoded literal; placed alongside turn_penalty and turn_bias in car.lua - Correct CHANGELOG entry from 'zero penalty' to 'configurable reduced penalty (default 5s)' Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ne-ways, and bearings Add 4 new complex scenarios to the turning circle feature tests: - Intermediate turning circle on through road used for u-turn - One-way main road forces u-turn at intermediate turning circle - Multiple intermediate turning facilities with bearings - U-turn at turning circle beats competing dead-end paths (2 variants) These test that turning circles at intermediate nodes (not just leaves) are correctly preferred for u-turns over plain dead-ends due to the lower penalty (5s vs 20s). Also verifies that intersection_analysis blocks u-turns at regular degree-3 intersections, making turning circles essential for legal u-turns on non-trivial road networks. All 13 scenarios pass with both CH and MLD algorithms.
There was a problem hiding this comment.
Pull request overview
Adds a has_turning_facility signal from the C++ extractor through to Lua profiles, and uses it in car.lua to apply a reduced u-turn penalty when the u-turn happens at a highway=turning_circle, turning_loop, or mini_roundabout node. Adds a Cucumber feature exercising the routing behavior across several topologies, plus docs/CHANGELOG updates.
Changes:
- Plumb a new
has_turning_facilityboolean intoExtractionTurn, populate it fromObstacleMapinedge_based_graph_factory.cpp, hardcodefalsefor fake compression turns, and expose it to Lua API v3/v4. - Add
turning_circle_penalty = 5tocar.luaand use it instead ofu_turn_penaltywhenturn.has_turning_facilityis set; refactorobstacles.luato use an explicithighway_to_obstacle_typetable. - Add
features/car/turning_circle_uturn.feature(route/turn assertions only, no time/weight), plus docs and aobstacles.hppany(to, type)helper.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| include/extractor/extraction_turn.hpp | Adds has_turning_facility member and threads it through both constructors. |
| include/extractor/obstacles.hpp | Adds two-argument any(to, type) helper on ObstacleMap. |
| src/extractor/edge_based_graph_factory.cpp | Detects turning facility at intersection and passes it into ExtractionTurn. |
| src/extractor/graph_compressor.cpp | Reformats fake_turn ctor and passes has_turning_facility=false (turning nodes are incompressible). |
| src/extractor/scripting_environment_lua.cpp | Exposes has_turning_facility to Lua API v3 and v4. |
| profiles/car.lua | Adds turning_circle_penalty = 5 and uses it for u-turns at turning facilities. |
| profiles/lib/obstacles.lua | Replaces dynamic obstacle_type[highway] lookup with explicit allow-list table. |
| features/car/turning_circle_uturn.feature | New scenarios asserting routes/turns for turning-facility u-turns. |
| features/step_definitions/trip.js | Unrelated lint-style rename ri → _ri. |
| features/step_definitions/distance_matrix.js | Unrelated lint-style cleanups (_ri, dropped response destructure). |
| docs/profiles.md | Documents new has_turning_facility field on process_turn. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| -- Penalty in seconds for u-turns at designated turning facilities | ||
| -- (highway=turning_circle, turning_loop, mini_roundabout). | ||
| -- Lower than u_turn_penalty because these nodes are specifically designed for turning around. | ||
| turning_circle_penalty = 5, |
| if turn.has_turning_facility then | ||
| -- No regular u-turn penalty at designated turning facilities (turning_circle, turning_loop, mini_roundabout) | ||
| turn.duration = turn.duration + profile.turning_circle_penalty |
|
|
||
| await this.reprocessAndLoadData(); | ||
| const testRow = function (row) { | ||
| const testRow = function (row, _ri) { |
| When I route I should get | ||
| | waypoints | bearings | route | turns | | ||
| | a,a | 90,10 270,10 | abce,bd,bd,abce,abce | depart,turn right,continue uturn,turn left,arrive | |
Welcome to Codecov 🎉Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests. Thanks for integrating Codecov - We've got you covered ☂️ |
- Fix misleading comments: penalty is 5s not 0s, clarify 'reduced' vs 'no' - Keep necessary lint fixes in distance_matrix.js (unused _ri param, response destructure) - Add time and weight assertions (+-10s margin) to turning circle u-turn scenarios to verify the 5s penalty is actually applied at turning facilities
Implements support for using
highway=turning_circle,highway=turning_loop, andhighway=mini_roundaboutnodes for u-turn maneuvers, eliminating unnecessary u-turn penalties at designated turning facilities.Implementation
C++ Core Changes
has_turning_facilityfield toExtractionTurnstructureedge_based_graph_factory.cppto detect turning facilities at intersections usingObstacleMapgraph_compressor.cppto handle turning facilities during node compressionscripting_environment_lua.cppto expose the field to Lua profiles (API v3 and v4)Routing Logic
profiles/car.luato checkturn.has_turning_facilitybefore applying u-turn penaltyTesting
features/car/turning_circle_uturn.featureCode Formatting
src/extractor/graph_compressor.cppto fix constructor alignment violationsDocumentation
has_turning_facilityfield toprocess_turnAPI documentation indocs/profiles.mdCHANGELOG.mdwith feature entry under Routing sectionThe
has_turning_facilityboolean is now available in Lua profiles:Design Decisions
Files Modified
include/extractor/extraction_turn.hpp- Add field (~9 lines)src/extractor/edge_based_graph_factory.cpp- Detect facility (~3 lines)src/extractor/graph_compressor.cpp- Handle compression & formatting (~18 lines)src/extractor/scripting_environment_lua.cpp- Expose to Lua (~4 lines)profiles/car.lua- Adjust penalty (~5 lines)features/car/turning_circle_uturn.feature- Tests (new, ~140 lines)CHANGELOG.md- Document feature (~1 line)docs/profiles.md- Document API (~1 line)Total: ~180 lines across 8 files
Original prompt
Todo