Improves Lane Handling for Multi-Hop Roundabout Instruction
This changeset implements Lane Anticipation on roundabouts, delimited by enter / leave step pairs. It does not handle lane anticipation within a roundabout. Lane anticipation happens on the granularity of a valid roundbaout: We discard partial roundabout (enter without exit or exit without enter) or data issues (no roundabout, exit before enter). Related: - https://github.com/Project-OSRM/osrm-backend/issues/2626 for lanes within a roundabout - https://github.com/Project-OSRM/osrm-backend/issues/2625 for handling going straight in lane anticipation
This commit is contained in:
parent
04667f1ed8
commit
e76e39a398
@ -259,6 +259,140 @@ Feature: Turn Lane Guidance
|
|||||||
| waypoints | route | turns | lanes |
|
| waypoints | route | turns | lanes |
|
||||||
| a,f | abx,bcy,cdz,dew,ef,ef | depart,turn right,turn left,turn right,turn left,arrive | ,straight:false right:false right:true right:false,left:false left:true straight:false,straight:false right:true right:false,left:true straight:false, |
|
| a,f | abx,bcy,cdz,dew,ef,ef | depart,turn right,turn left,turn right,turn left,arrive | ,straight:false right:false right:true right:false,left:false left:true straight:false,straight:false right:true right:false,left:true straight:false, |
|
||||||
|
|
||||||
|
@anticipate
|
||||||
|
Scenario: Anticipate with lanes in roundabout: roundabouts as the unit of anticipation
|
||||||
|
Given the node map
|
||||||
|
| | | e | | |
|
||||||
|
| a | b | | d | f |
|
||||||
|
| | | c | | |
|
||||||
|
| | | | | |
|
||||||
|
| | | g | | |
|
||||||
|
| k | h | | j | l |
|
||||||
|
| | | i | | |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | turn:lanes:forward | highway | junction | # |
|
||||||
|
| ab | slight_right\|slight_right&slight_right | primary | | |
|
||||||
|
| bc | slight_left\|slight_right&slight_right | primary | roundabout | top |
|
||||||
|
| cd | | primary | roundabout | top |
|
||||||
|
| de | | primary | roundabout | top |
|
||||||
|
| eb | | primary | roundabout | top |
|
||||||
|
| df | | primary | | |
|
||||||
|
| cg | slight_right\|slight_right | primary | | |
|
||||||
|
| gh | slight_left\|slight_right | primary | roundabout | bot |
|
||||||
|
| hi | | primary | roundabout | bot |
|
||||||
|
| ij | slight_left\|slight_right | primary | roundabout | bot |
|
||||||
|
| jg | | primary | roundabout | bot |
|
||||||
|
| hk | | primary | | |
|
||||||
|
| jl | | primary | | |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| # | waypoints | route | turns | lanes |
|
||||||
|
| right-right | a,k | ab,cg,hk,hk | depart,roundabout-exit-1,roundabout-exit-1,arrive | ,slight right:false slight right:false slight right:true,slight right:false slight right:true, |
|
||||||
|
| right-left | a,l | ab,cg,jl,jl | depart,roundabout-exit-1,roundabout-exit-2,arrive | ,slight right:false slight right:false slight right:true,slight right:false slight right:true, |
|
||||||
|
| todo exits | a,f | ab,df,df | depart,roundabout-exit-2,arrive | ,slight right:false slight right:false slight right:true, |
|
||||||
|
| todo exits | a,e | ab,bc,eb | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true slight right:true, |
|
||||||
|
|
||||||
|
@anticipate @todo
|
||||||
|
Scenario: Roundabout with lanes only tagged on exit
|
||||||
|
Given the node map
|
||||||
|
| | | e | | |
|
||||||
|
| a | b | | d | f |
|
||||||
|
| | | c | | |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | turn:lanes:forward | highway | junction |
|
||||||
|
| ab | | primary | |
|
||||||
|
| bc | | primary | roundabout |
|
||||||
|
| cd | slight_left\|slight_left&slight_right | primary | roundabout |
|
||||||
|
| de | | primary | roundabout |
|
||||||
|
| eb | | primary | roundabout |
|
||||||
|
| df | | primary | |
|
||||||
|
|
||||||
|
When I route I should get
|
||||||
|
| waypoints | route | turns | lanes |
|
||||||
|
| 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
|
||||||
|
Given the node map
|
||||||
|
| | | a | | |
|
||||||
|
| | | b | | |
|
||||||
|
| | c | | g | h |
|
||||||
|
| | | | | |
|
||||||
|
| | d | | f | |
|
||||||
|
| | | e | | |
|
||||||
|
| x | | | | y |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | turn:lanes:forward | highway | junction |
|
||||||
|
| ab | slight_right\|slight_right | primary | |
|
||||||
|
| bc | | primary | roundabout |
|
||||||
|
| cd | | primary | roundabout |
|
||||||
|
| de | | primary | roundabout |
|
||||||
|
| ef | | primary | roundabout |
|
||||||
|
| fg | slight_right | primary | roundabout |
|
||||||
|
| gb | | primary | roundabout |
|
||||||
|
| gh | | primary | |
|
||||||
|
| cx | | primary | |
|
||||||
|
| dx | | primary | |
|
||||||
|
| ey | | primary | |
|
||||||
|
| fy | | primary | |
|
||||||
|
|
||||||
|
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, |
|
||||||
|
|
||||||
|
@anticipate
|
||||||
|
Scenario: Departing or arriving inside a roundabout does not yet anticipate lanes
|
||||||
|
Given the node map
|
||||||
|
| | | a | | |
|
||||||
|
| x | b | | d | y |
|
||||||
|
| | | c | | |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | turn:lanes:forward | highway | junction | name |
|
||||||
|
| xb | slight_right\|slight_right | primary | | xb |
|
||||||
|
| dy | | primary | | dy |
|
||||||
|
| ab | | primary | roundabout | roundabout |
|
||||||
|
| bc | | primary | roundabout | roundabout |
|
||||||
|
| cd | left\|slight_right | primary | roundabout | roundabout |
|
||||||
|
| da | | primary | roundabout | roundabout |
|
||||||
|
|
||||||
|
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, |
|
||||||
|
|
||||||
|
@anticipate
|
||||||
|
Scenario: Anticipate Lanes for turns before and / or after roundabout
|
||||||
|
Given the node map
|
||||||
|
| a | b | | | x |
|
||||||
|
| | c | | | |
|
||||||
|
| d | | f | g | z |
|
||||||
|
| | e | | h | |
|
||||||
|
| | | | | |
|
||||||
|
| | y | | | |
|
||||||
|
|
||||||
|
And the ways
|
||||||
|
| nodes | turn:lanes:forward | highway | junction | name |
|
||||||
|
| ab | through\|right&right&right&right | primary | | abx |
|
||||||
|
| bx | | primary | | abx |
|
||||||
|
| bc | right\|right&right&right | primary | | bc |
|
||||||
|
| cd | | primary | roundabout | cdefc |
|
||||||
|
| de | slight_left\|slight_left&slight_left&slight_right | primary | roundabout | cdefc |
|
||||||
|
| ef | left\|slight_right&slight_right | primary | roundabout | cdefc |
|
||||||
|
| fc | | primary | roundabout | cdefc |
|
||||||
|
| ey | | primary | | ey |
|
||||||
|
| fg | through\|right | primary | | fg |
|
||||||
|
| gz | | primary | | gz |
|
||||||
|
| gh | | primary | | gh |
|
||||||
|
|
||||||
|
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, |
|
||||||
|
|
||||||
@anticipate @bug @todo
|
@anticipate @bug @todo
|
||||||
Scenario: Tripple Right keeping Left
|
Scenario: Tripple Right keeping Left
|
||||||
Given the node map
|
Given the node map
|
||||||
|
@ -2,10 +2,13 @@
|
|||||||
#define OSRM_ENGINE_GUIDANCE_TOOLKIT_HPP_
|
#define OSRM_ENGINE_GUIDANCE_TOOLKIT_HPP_
|
||||||
|
|
||||||
#include "extractor/guidance/turn_instruction.hpp"
|
#include "extractor/guidance/turn_instruction.hpp"
|
||||||
#include "util/guidance/toolkit.hpp"
|
#include "engine/guidance/route_step.hpp"
|
||||||
#include "util/bearing.hpp"
|
#include "util/bearing.hpp"
|
||||||
|
#include "util/guidance/toolkit.hpp"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
namespace osrm
|
namespace osrm
|
||||||
{
|
{
|
||||||
@ -40,6 +43,38 @@ inline extractor::guidance::DirectionModifier::Enum angleToDirectionModifier(con
|
|||||||
return extractor::guidance::DirectionModifier::Left;
|
return extractor::guidance::DirectionModifier::Left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Runs fn on RouteStep sub-ranges determined to be roundabouts.
|
||||||
|
// The function fn is getting called with a roundabout range as in: [enter, .., leave].
|
||||||
|
//
|
||||||
|
// The following situations are taken care for (i.e. we discard them):
|
||||||
|
// - partial roundabout: enter without exit or exit without enter
|
||||||
|
// - data issues: no roundabout, exit before enter
|
||||||
|
template <typename Iter, typename Fn> inline Fn forEachRoundabout(Iter first, Iter last, Fn fn)
|
||||||
|
{
|
||||||
|
while (first != last)
|
||||||
|
{
|
||||||
|
const auto enter = std::find_if(first, last, [](const RouteStep &step) {
|
||||||
|
return entersRoundabout(step.maneuver.instruction);
|
||||||
|
});
|
||||||
|
|
||||||
|
// enter has to come before leave, otherwise: faulty data / partial roundabout, skip those
|
||||||
|
const auto leave = std::find_if(enter, last, [](const RouteStep &step) {
|
||||||
|
return leavesRoundabout(step.maneuver.instruction);
|
||||||
|
});
|
||||||
|
|
||||||
|
// No roundabouts, or partial one (like start / end inside a roundabout)
|
||||||
|
if (enter == last || leave == last)
|
||||||
|
break;
|
||||||
|
|
||||||
|
(void)fn(std::make_pair(enter, leave));
|
||||||
|
|
||||||
|
// Skip to first step after the currently handled enter / leave pair
|
||||||
|
first = std::next(leave);
|
||||||
|
}
|
||||||
|
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace guidance
|
} // namespace guidance
|
||||||
} // namespace engine
|
} // namespace engine
|
||||||
} // namespace osrm
|
} // namespace osrm
|
||||||
|
@ -21,10 +21,13 @@ inline void print(const engine::guidance::RouteStep &step)
|
|||||||
{
|
{
|
||||||
std::cout << static_cast<int>(step.maneuver.instruction.type) << " "
|
std::cout << static_cast<int>(step.maneuver.instruction.type) << " "
|
||||||
<< static_cast<int>(step.maneuver.instruction.direction_modifier) << " "
|
<< static_cast<int>(step.maneuver.instruction.direction_modifier) << " "
|
||||||
<< static_cast<int>(step.maneuver.waypoint_type) << " Duration: " << step.duration
|
<< static_cast<int>(step.maneuver.waypoint_type) << " "
|
||||||
<< " Distance: " << step.distance << " Geometry: " << step.geometry_begin << " "
|
<< " Lanes: (" << static_cast<int>(step.maneuver.lanes.lanes_in_turn) << ", "
|
||||||
<< step.geometry_end << " exit: " << step.maneuver.exit
|
<< static_cast<int>(step.maneuver.lanes.first_lane_from_the_right) << ")"
|
||||||
<< " Intersections: " << step.intersections.size() << " [";
|
<< " Duration: " << step.duration << " Distance: " << step.distance
|
||||||
|
<< " Geometry: " << step.geometry_begin << " " << step.geometry_end
|
||||||
|
<< " exit: " << step.maneuver.exit << " Intersections: " << step.intersections.size()
|
||||||
|
<< " [";
|
||||||
|
|
||||||
for (const auto &intersection : step.intersections)
|
for (const auto &intersection : step.intersections)
|
||||||
{
|
{
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
#include "extractor/guidance/turn_instruction.hpp"
|
|
||||||
#include "engine/guidance/post_processing.hpp"
|
#include "engine/guidance/post_processing.hpp"
|
||||||
|
#include "extractor/guidance/turn_instruction.hpp"
|
||||||
|
|
||||||
#include "engine/guidance/assemble_steps.hpp"
|
#include "engine/guidance/assemble_steps.hpp"
|
||||||
|
#include "engine/guidance/lane_processing.hpp"
|
||||||
#include "engine/guidance/toolkit.hpp"
|
#include "engine/guidance/toolkit.hpp"
|
||||||
|
|
||||||
#include "util/guidance/toolkit.hpp"
|
#include "util/guidance/toolkit.hpp"
|
||||||
@ -498,11 +499,49 @@ 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);
|
||||||
|
|
||||||
|
auto enterAndLeave = anticipateLaneChange({enter, leave});
|
||||||
|
|
||||||
|
// 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
|
} // namespace
|
||||||
|
|
||||||
// Post processing can invalidate some instructions. For example StayOnRoundabout
|
// Post processing can invalidate some instructions. For example StayOnRoundabout
|
||||||
// is turned into exit counts. These instructions are removed by the following function
|
// is turned into exit counts. These instructions are removed by the following function
|
||||||
|
|
||||||
std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
|
std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
|
||||||
{
|
{
|
||||||
// finally clean up the post-processed instructions.
|
// finally clean up the post-processed instructions.
|
||||||
@ -518,6 +557,9 @@ std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
|
|||||||
|
|
||||||
boost::remove_erase_if(steps, not_is_valid);
|
boost::remove_erase_if(steps, not_is_valid);
|
||||||
|
|
||||||
|
// the steps should still include depart and arrive at least
|
||||||
|
BOOST_ASSERT(steps.size() >= 2);
|
||||||
|
|
||||||
BOOST_ASSERT(steps.front().intersections.size() >= 1);
|
BOOST_ASSERT(steps.front().intersections.size() >= 1);
|
||||||
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
|
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
|
||||||
BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
|
BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
|
||||||
@ -544,6 +586,11 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
|
|||||||
if (steps.size() == 2)
|
if (steps.size() == 2)
|
||||||
return steps;
|
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
|
// Count Street Exits forward
|
||||||
bool on_roundabout = false;
|
bool on_roundabout = false;
|
||||||
bool has_entered_roundabout = false;
|
bool has_entered_roundabout = false;
|
||||||
@ -551,7 +598,7 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
|
|||||||
// count the exits forward. if enter/exit roundabout happen both, no further treatment is
|
// count the exits forward. if enter/exit roundabout happen both, no further treatment is
|
||||||
// required. We might end up with only one of them (e.g. starting within a roundabout)
|
// required. We might end up with only one of them (e.g. starting within a roundabout)
|
||||||
// or having a via-point in the roundabout.
|
// or having a via-point in the roundabout.
|
||||||
// In this case, exits are numbered from the start of the lag.
|
// In this case, exits are numbered from the start of the leg.
|
||||||
for (std::size_t step_index = 0; step_index < steps.size(); ++step_index)
|
for (std::size_t step_index = 0; step_index < steps.size(); ++step_index)
|
||||||
{
|
{
|
||||||
auto &step = steps[step_index];
|
auto &step = steps[step_index];
|
||||||
|
Loading…
Reference in New Issue
Block a user