Remove lanes from roundabouts, closes #2626
After half a day of looking at the tagging and the data came to the following conclusion: We can't keep the user to the innermost / outermost lanes depending on the exit the route takes: we found situations where both heuristics were wrong. Even on popular roundabouts the tagging is often wrong or in the best case not present at all. There are at least two different ways to interpret roundabout indications: 1/ where e.g. a right arrow on the lane indicates turn restrictions for the roundabout and the need to take this lane to exit the roundabout to the right (possibly skipping multiple exits) and 2/ where a right arrow just means this is a lane in a immediate right turn. Example: Australia marks lanes with arrows that seem to indicate "angles you can exit the roundabout from", for example, these two ways: - http://www.openstreetmap.org/way/320941710 - http://www.openstreetmap.org/way/42918021 Whereas Germany marks lanes with "directions you can travel in these lanes immediately after entering the roundabout": - http://www.openstreetmap.org/way/52578338 These two different interpretations of how to draw the arrows on the roads mean we have conflicting solutions to "which lanes can you use to take exit B from entry A" based on locality. Continuing to tag ways based on lane markings is no problem, but unfortunately, we can't reliably resolve good advice for navigation system users (like "use the inside lane to take the second exit at the roundabout"), there are too many situations that would generate bad instructions (instructions that tell users to go into a lane they shouldn't use).
This commit is contained in:
parent
1fc63e1e72
commit
748fd3efa9
@ -314,7 +314,7 @@ Feature: Turn Lane Guidance
|
||||
| a,f | ab,df,df | depart,roundabout-exit-1,use lane slight right,arrive | ,,slight left:false slight left:false slight right:true, |
|
||||
|
||||
@anticipate
|
||||
Scenario: Anticipate with lanes in roundabout where we stay on the roundabout for multiple exits
|
||||
Scenario: No Lanes for Roundabouts, see #2626
|
||||
Given the node map
|
||||
| | | a | | |
|
||||
| | | b | | |
|
||||
@ -341,10 +341,10 @@ Feature: Turn Lane Guidance
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,h | ab,gh,gh | depart,roundabout-exit-5,arrive | ,slight right:false slight right:true, |
|
||||
| a,h | ab,gh,gh | depart,roundabout-exit-5,arrive | ,, |
|
||||
|
||||
@anticipate
|
||||
Scenario: Departing or arriving inside a roundabout does not yet anticipate lanes
|
||||
Scenario: No Lanes for Roundabouts, see #2626
|
||||
Given the node map
|
||||
| | | a | | |
|
||||
| x | b | | d | y |
|
||||
@ -361,12 +361,12 @@ Feature: Turn Lane Guidance
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| x,y | xb,dy,dy | depart,roundabout-exit-1,arrive | ,slight right:false slight right:true, |
|
||||
| x,c | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true, |
|
||||
| x,a | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true, |
|
||||
| x,y | xb,dy,dy | depart,roundabout-exit-1,arrive | ,, |
|
||||
| x,c | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,, |
|
||||
| x,a | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,, |
|
||||
|
||||
@anticipate
|
||||
Scenario: Departing or arriving inside a roundabout does not yet anticipate lanes (BIG version)
|
||||
Scenario: No Lanes for Roundabouts, see #2626
|
||||
Given the node map
|
||||
| | | a | | |
|
||||
| x | b | | d | y |
|
||||
@ -417,12 +417,12 @@ Feature: Turn Lane Guidance
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| x,y | xb,dy,dy | depart,roundabout-exit-1,arrive | ,slight right:false slight right:true, |
|
||||
| x,c | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true, |
|
||||
| x,a | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true, |
|
||||
| x,y | xb,dy,dy | depart,roundabout-exit-1,arrive | ,, |
|
||||
| x,c | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,, |
|
||||
| x,a | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,, |
|
||||
|
||||
@anticipate
|
||||
Scenario: Anticipate Lanes for turns before and / or after roundabout
|
||||
Scenario: No Lanes for Roundabouts, see #2626
|
||||
Given the node map
|
||||
| a | b | | | x |
|
||||
| | c | | | |
|
||||
@ -447,7 +447,7 @@ Feature: Turn Lane Guidance
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,h | abx,bc,fg,gh,gh | depart,turn right,cdefc-exit-2,turn right,arrive | ,straight:false right:false right:false right:false right:true,right:false right:false right:false right:true,straight:false right:true, |
|
||||
| a,h | abx,bc,fg,gh,gh | depart,turn right,cdefc-exit-2,turn right,arrive | ,straight:false right:false right:false right:false right:true,,straight:false right:true, |
|
||||
|
||||
@anticipate @bug @todo
|
||||
Scenario: Tripple Right keeping Left
|
||||
|
@ -150,6 +150,7 @@ class RouteAPI : public BaseAPI
|
||||
leg_geometry,
|
||||
phantoms.source_phantom,
|
||||
phantoms.target_phantom);
|
||||
leg.steps = guidance::removeLanesFromRoundabouts(std::move(leg.steps));
|
||||
leg.steps = guidance::anticipateLaneChange(std::move(leg.steps));
|
||||
leg.steps = guidance::collapseUseLane(std::move(leg.steps));
|
||||
leg_geometry = guidance::resyncGeometry(std::move(leg_geometry), leg.steps);
|
||||
|
@ -20,6 +20,9 @@ namespace guidance
|
||||
std::vector<RouteStep> anticipateLaneChange(std::vector<RouteStep> steps,
|
||||
const double min_duration_needed_for_lane_change = 15);
|
||||
|
||||
// Remove all lane information from roundabouts. See #2626.
|
||||
std::vector<RouteStep> removeLanesFromRoundabouts(std::vector<RouteStep> steps);
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "util/guidance/toolkit.hpp"
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "engine/guidance/toolkit.hpp"
|
||||
|
||||
#include <iterator>
|
||||
|
||||
@ -104,6 +105,29 @@ std::vector<RouteStep> anticipateLaneChange(std::vector<RouteStep> steps,
|
||||
return steps;
|
||||
}
|
||||
|
||||
std::vector<RouteStep> removeLanesFromRoundabouts(std::vector<RouteStep> steps)
|
||||
{
|
||||
using namespace util::guidance;
|
||||
|
||||
const auto removeLanes = [](RouteStep &step) {
|
||||
for (auto &intersection : step.intersections)
|
||||
{
|
||||
intersection.lane_description = {};
|
||||
intersection.lanes = {};
|
||||
}
|
||||
};
|
||||
|
||||
for (auto &step : steps)
|
||||
{
|
||||
const auto inst = step.maneuver.instruction;
|
||||
|
||||
if (entersRoundabout(inst) || staysOnRoundabout(inst) || leavesRoundabout(inst))
|
||||
removeLanes(step);
|
||||
}
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace engine
|
||||
} // namespace osrm
|
||||
|
@ -499,48 +499,6 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Works on steps including silent and invalid instructions in order to do lane anticipation for
|
||||
// roundabouts which later on get collapsed into a single multi-hop instruction.
|
||||
std::vector<RouteStep> anticipateLaneChangeForRoundabouts(std::vector<RouteStep> steps)
|
||||
{
|
||||
using namespace util::guidance;
|
||||
|
||||
using StepIter = decltype(steps)::iterator;
|
||||
using StepIterRange = std::pair<StepIter, StepIter>;
|
||||
|
||||
const auto anticipate_lanes_in_roundabout = [&](StepIterRange roundabout) {
|
||||
// We do lane anticipation on the roundabout's enter and leave step only.
|
||||
// TODO: This means, lanes _inside_ the roundabout are ignored at the moment.
|
||||
|
||||
auto enter = *roundabout.first;
|
||||
const auto leave = *roundabout.second;
|
||||
|
||||
// Although the enter instruction may be a left/right turn, for right-sided driving the
|
||||
// roundabout is counter-clockwise and therefore we need to always set it to a left turn.
|
||||
// FIXME: assumes right-side driving (counter-clockwise roundabout flow)
|
||||
const auto enter_direction = enter.maneuver.instruction.direction_modifier;
|
||||
|
||||
if (util::guidance::isRightTurn(enter.maneuver.instruction))
|
||||
enter.maneuver.instruction.direction_modifier =
|
||||
mirrorDirectionModifier(enter_direction);
|
||||
|
||||
// a roundabout is a continuous maneuver. We don't switch lanes within a roundabout, as long
|
||||
// as it can be avoided.
|
||||
auto enterAndLeave =
|
||||
anticipateLaneChange({enter, leave}, std::numeric_limits<double>::max());
|
||||
|
||||
// Undo flipping direction on a right turn in a right-sided counter-clockwise roundabout.
|
||||
// FIXME: assumes right-side driving (counter-clockwise roundabout flow)
|
||||
enterAndLeave[0].maneuver.instruction.direction_modifier = enter_direction;
|
||||
|
||||
std::swap(*roundabout.first, enterAndLeave[0]);
|
||||
std::swap(*roundabout.second, enterAndLeave[1]);
|
||||
};
|
||||
|
||||
forEachRoundabout(begin(steps), end(steps), anticipate_lanes_in_roundabout);
|
||||
return steps;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
// Post processing can invalidate some instructions. For example StayOnRoundabout
|
||||
@ -589,11 +547,6 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
|
||||
if (steps.size() == 2)
|
||||
return steps;
|
||||
|
||||
// Before we invalidate and remove silent instructions, we handle roundabouts (before they're
|
||||
// getting collapsed into a single multi-hop instruction) by back-propagating exit lane
|
||||
// constraints already to a roundabout's enter instruction.
|
||||
steps = anticipateLaneChangeForRoundabouts(std::move(steps));
|
||||
|
||||
// Count Street Exits forward
|
||||
bool on_roundabout = false;
|
||||
bool has_entered_roundabout = false;
|
||||
@ -1182,7 +1135,7 @@ std::vector<RouteStep> collapseUseLane(std::vector<RouteStep> steps)
|
||||
[containsTag](const util::guidance::LaneTupel lanes,
|
||||
extractor::guidance::TurnLaneDescription lane_description) {
|
||||
// the lane description is given left to right, lanes are counted from the right.
|
||||
// Therefore we access the lane description yousing the reverse iterator
|
||||
// Therefore we access the lane description using the reverse iterator
|
||||
if (lanes.first_lane_from_the_right > 0 &&
|
||||
containsTag(*(lane_description.rbegin() + (lanes.first_lane_from_the_right - 1)),
|
||||
(extractor::guidance::TurnLaneType::straight |
|
||||
|
Loading…
Reference in New Issue
Block a user