add support for junction=circular, allowing named circular junctions to be treated as rotaries

This commit is contained in:
Moritz Kobitzsch 2016-11-24 13:29:17 +01:00
parent 4636aaabfe
commit 12d58ace10
14 changed files with 433 additions and 428 deletions

View File

@ -547,7 +547,7 @@ step.
| `use lane` | going straight on a specific lane | | `use lane` | going straight on a specific lane |
| `continue` | Turn in direction of `modifier` to stay on the same road | | `continue` | Turn in direction of `modifier` to stay on the same road |
| `roundabout` | traverse roundabout, has additional field `exit` with NR if the roundabout is left. `the modifier specifies the direction of entering the roundabout` | | `roundabout` | traverse roundabout, has additional field `exit` with NR if the roundabout is left. `the modifier specifies the direction of entering the roundabout` |
| `rotary` | a larger version of a roundabout, can offer `rotary_name/rotary_pronunciation` in addition to the `exit` parameter. | | `rotary` | a traffic circle. While very similar to a larger version of a roundabout, it does not necessarily follow roundabout rules for right of way. It can offer `rotary_name/rotary_pronunciation` in addition to the `exit` parameter. |
| `roundabout turn`| Describes a turn at a small roundabout that should be treated as normal turn. The `modifier` indicates the turn direciton. Example instruction: `At the roundabout turn left`. | | `roundabout turn`| Describes a turn at a small roundabout that should be treated as normal turn. The `modifier` indicates the turn direciton. Example instruction: `At the roundabout turn left`. |
| `notification` | not an actual turn but a change in the driving conditions. For example the travel mode. If the road takes a turn itself, the `modifier` describes the direction | | `notification` | not an actual turn but a change in the driving conditions. For example the travel mode. If the road takes a turn itself, the `modifier` describes the direction |

View File

@ -0,0 +1,282 @@
@routing @guidance
Feature: Rotary
Background:
Given the profile "car"
Given a grid size of 30 meters
Scenario: Enter and Exit
Given the node map
"""
a
b
h g c d
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bgecb | circular |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,bgecb-exit-3,arrive |
| a,f | ab,ef,ef | depart,bgecb-exit-2,arrive |
| a,h | ab,gh,gh | depart,bgecb-exit-1,arrive |
| d,f | cd,ef,ef | depart,bgecb-exit-3,arrive |
| d,h | cd,gh,gh | depart,bgecb-exit-2,arrive |
| d,a | cd,ab,ab | depart,bgecb-exit-1,arrive |
| f,h | ef,gh,gh | depart,bgecb-exit-3,arrive |
| f,a | ef,ab,ab | depart,bgecb-exit-2,arrive |
| f,d | ef,cd,cd | depart,bgecb-exit-1,arrive |
| h,a | gh,ab,ab | depart,bgecb-exit-3,arrive |
| h,d | gh,cd,cd | depart,bgecb-exit-2,arrive |
| h,f | gh,ef,ef | depart,bgecb-exit-1,arrive |
Scenario: Only Enter
Given the node map
"""
a
b
d c g h
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | circular |
When I route I should get
| waypoints | route | turns |
| a,c | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| a,e | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| a,g | ab,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| d,e | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| d,g | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| d,b | cd,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| f,g | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| f,b | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| f,c | ef,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| h,b | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| h,c | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
| h,e | gh,bcegb,bcegb | depart,bcegb-exit-undefined,arrive |
Scenario: Only Exit
Given the node map
"""
a
b
d c g h
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | circular |
When I route I should get
| waypoints | route | turns |
| b,d | bcegb,cd,cd | depart,bcegb-exit-1,arrive |
| b,f | bcegb,ef,ef | depart,bcegb-exit-2,arrive |
| b,h | bcegb,gh,gh | depart,bcegb-exit-3,arrive |
| c,f | bcegb,ef,ef | depart,bcegb-exit-1,arrive |
| c,h | bcegb,gh,gh | depart,bcegb-exit-2,arrive |
| c,a | bcegb,ab,ab | depart,bcegb-exit-3,arrive |
| e,h | bcegb,gh,gh | depart,bcegb-exit-1,arrive |
| e,a | bcegb,ab,ab | depart,bcegb-exit-2,arrive |
| e,d | bcegb,cd,cd | depart,bcegb-exit-3,arrive |
| g,a | bcegb,ab,ab | depart,bcegb-exit-1,arrive |
| g,d | bcegb,cd,cd | depart,bcegb-exit-2,arrive |
| g,f | bcegb,ef,ef | depart,bcegb-exit-3,arrive |
#phantom node snapping can result in a full round-trip here, therefore we cannot test b->a and the other direct exits
Scenario: Drive Around
Given the node map
"""
a
b
d c g h
e
f
"""
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | circular |
When I route I should get
| waypoints | route | turns |
| b,c | bcegb,bcegb | depart,arrive |
| b,e | bcegb,bcegb | depart,arrive |
| b,g | bcegb,bcegb | depart,arrive |
| c,e | bcegb,bcegb | depart,arrive |
| c,g | bcegb,bcegb | depart,arrive |
| c,b | bcegb,bcegb | depart,arrive |
| e,g | bcegb,bcegb | depart,arrive |
| e,b | bcegb,bcegb | depart,arrive |
| e,c | bcegb,bcegb | depart,arrive |
| g,b | bcegb,bcegb | depart,arrive |
| g,c | bcegb,bcegb | depart,arrive |
| g,e | bcegb,bcegb | depart,arrive |
#needs to be adjusted when name-discovery works for entrys
Scenario: Mixed Entry and Exit
Given the node map
"""
c a
j b f
k e
l h d
g i
"""
And the ways
| nodes | junction | oneway |
| abc | | yes |
| def | | yes |
| ghi | | yes |
| jkl | | yes |
| bkheb | circular | yes |
When I route I should get
| waypoints | route | turns |
| a,c | abc,abc,abc | depart,rotary-exit-1,arrive |
| a,l | abc,jkl,jkl | depart,bkheb-exit-2,arrive |
| a,i | abc,ghi,ghi | depart,bkheb-exit-3,arrive |
| a,f | abc,def,def | depart,bkheb-exit-4,arrive |
| d,f | def,def,def | depart,rotary-exit-1,arrive |
| d,c | def,abc,abc | depart,bkheb-exit-2,arrive |
| d,l | def,jkl,jkl | depart,bkheb-exit-3,arrive |
| d,i | def,ghi,ghi | depart,bkheb-exit-4,arrive |
| g,i | ghi,ghi,ghi | depart,rotary-exit-1,arrive |
| g,f | ghi,def,def | depart,bkheb-exit-2,arrive |
| g,c | ghi,abc,abc | depart,bkheb-exit-3,arrive |
| g,l | ghi,jkl,jkl | depart,bkheb-exit-4,arrive |
| j,l | jkl,jkl,jkl | depart,rotary-exit-1,arrive |
| j,i | jkl,ghi,ghi | depart,bkheb-exit-2,arrive |
| j,f | jkl,def,def | depart,bkheb-exit-3,arrive |
| j,c | jkl,abc,abc | depart,bkheb-exit-4,arrive |
Scenario: Collinear in X,Y
Given the node map
"""
a
b
c d f
e
"""
And the ways
| nodes | junction |
| ab | |
| bcdb | circular |
| ce | |
| df | |
When I route I should get
| waypoints | route | turns |
| a,e | ab,ce,ce | depart,bcdb-exit-1,arrive |
| a,f | ab,df,df | depart,bcdb-exit-2,arrive |
Scenario: Collinear in X,Y
Given the node map
"""
a
d
b c f
e
"""
And the ways
| nodes | junction |
| ad | |
| bcdb | circular |
| be | |
| cf | |
When I route I should get
| waypoints | route | turns |
| a,e | ad,be,be | depart,bcdb-exit-1,arrive |
| a,f | ad,cf,cf | depart,bcdb-exit-2,arrive |
Scenario: Collinear in X,Y
Given the node map
"""
a
c
d b f
e
"""
And the ways
| nodes | junction |
| ac | |
| bcdb | circular |
| de | |
| bf | |
When I route I should get
| waypoints | route | turns |
| a,e | ac,de,de | depart,bcdb-exit-1,arrive |
| a,f | ac,bf,bf | depart,bcdb-exit-2,arrive |
Scenario: Collinear in X,Y
Given the node map
"""
f
d c e
b
a
"""
And the ways
| nodes | junction |
| ab | |
| bcdb | circular |
| ce | |
| df | |
When I route I should get
| waypoints | route | turns |
| a,e | ab,ce,ce | depart,bcdb-exit-1,arrive |
| a,f | ab,df,df | depart,bcdb-exit-2,arrive |
Scenario: Collinear in X,Y
Given the node map
"""
f
d c e
b
a
"""
And the ways
| nodes | junction |
| ab | |
| bcdb | circular |
| ce | |
| df | |
When I route I should get
| waypoints | route | turns |
| a,e | ab,ce,ce | depart,bcdb-exit-1,arrive |
| a,f | ab,df,df | depart,bcdb-exit-2,arrive |

View File

@ -45,6 +45,7 @@ struct ExtractionWay
backward_speed = -1; backward_speed = -1;
duration = -1; duration = -1;
roundabout = false; roundabout = false;
circular = false;
is_startpoint = true; is_startpoint = true;
is_access_restricted = false; is_access_restricted = false;
name.clear(); name.clear();
@ -89,6 +90,7 @@ struct ExtractionWay
std::string turn_lanes_forward; std::string turn_lanes_forward;
std::string turn_lanes_backward; std::string turn_lanes_backward;
bool roundabout; bool roundabout;
bool circular;
bool is_access_restricted; bool is_access_restricted;
bool is_startpoint; bool is_startpoint;
TravelMode forward_travel_mode : 4; TravelMode forward_travel_mode : 4;

View File

@ -43,11 +43,12 @@ struct InternalExtractorEdge
MIN_OSM_NODEID, MIN_OSM_NODEID,
0, 0,
0, 0,
false, false, // forward
false, false, // backward
false, false, // roundabout
false, false, // circular
true, false, // access restricted
true, // can be startpoint
TRAVEL_MODE_INACCESSIBLE, TRAVEL_MODE_INACCESSIBLE,
false, false,
guidance::TurnLaneType::empty, guidance::TurnLaneType::empty,
@ -62,6 +63,7 @@ struct InternalExtractorEdge
bool forward, bool forward,
bool backward, bool backward,
bool roundabout, bool roundabout,
bool circular,
bool access_restricted, bool access_restricted,
bool startpoint, bool startpoint,
TravelMode travel_mode, TravelMode travel_mode,
@ -75,6 +77,7 @@ struct InternalExtractorEdge
forward, forward,
backward, backward,
roundabout, roundabout,
circular,
access_restricted, access_restricted,
startpoint, startpoint,
travel_mode, travel_mode,
@ -99,11 +102,12 @@ struct InternalExtractorEdge
MIN_OSM_NODEID, MIN_OSM_NODEID,
0, 0,
WeightData(), WeightData(),
false, false, // forward
false, false, // backward
false, false, // roundabout
false, false, // circular
true, false, // access restricted
true, // can be startpoint
TRAVEL_MODE_INACCESSIBLE, TRAVEL_MODE_INACCESSIBLE,
false, false,
INVALID_LANE_DESCRIPTIONID, INVALID_LANE_DESCRIPTIONID,
@ -115,11 +119,12 @@ struct InternalExtractorEdge
MAX_OSM_NODEID, MAX_OSM_NODEID,
SPECIAL_NODEID, SPECIAL_NODEID,
WeightData(), WeightData(),
false, false, // forward
false, false, // backward
false, false, // roundabout
false, false, // circular
true, false, // access restricted
true, // can be startpoint
TRAVEL_MODE_INACCESSIBLE, TRAVEL_MODE_INACCESSIBLE,
false, false,
INVALID_LANE_DESCRIPTIONID, INVALID_LANE_DESCRIPTIONID,

View File

@ -22,6 +22,7 @@ struct NodeBasedEdge
bool forward, bool forward,
bool backward, bool backward,
bool roundabout, bool roundabout,
bool circular,
bool access_restricted, bool access_restricted,
bool startpoint, bool startpoint,
TravelMode travel_mode, TravelMode travel_mode,
@ -38,6 +39,7 @@ struct NodeBasedEdge
bool forward : 1; bool forward : 1;
bool backward : 1; bool backward : 1;
bool roundabout : 1; bool roundabout : 1;
bool circular : 1;
bool access_restricted : 1; bool access_restricted : 1;
bool startpoint : 1; bool startpoint : 1;
bool is_split : 1; bool is_split : 1;
@ -55,6 +57,7 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge
bool forward, bool forward,
bool backward, bool backward,
bool roundabout, bool roundabout,
bool circular,
bool access_restricted, bool access_restricted,
bool startpoint, bool startpoint,
TravelMode travel_mode, TravelMode travel_mode,
@ -70,7 +73,7 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge
inline NodeBasedEdge::NodeBasedEdge() inline NodeBasedEdge::NodeBasedEdge()
: source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), forward(false), : source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), forward(false),
backward(false), roundabout(false), access_restricted(false), startpoint(true), backward(false), roundabout(false), circular(false), access_restricted(false), startpoint(true),
is_split(false), travel_mode(false), lane_description_id(INVALID_LANE_DESCRIPTIONID) is_split(false), travel_mode(false), lane_description_id(INVALID_LANE_DESCRIPTIONID)
{ {
} }
@ -82,6 +85,7 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source,
bool forward, bool forward,
bool backward, bool backward,
bool roundabout, bool roundabout,
bool circular,
bool access_restricted, bool access_restricted,
bool startpoint, bool startpoint,
TravelMode travel_mode, TravelMode travel_mode,
@ -89,7 +93,7 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source,
const LaneDescriptionID lane_description_id, const LaneDescriptionID lane_description_id,
guidance::RoadClassification road_classification) guidance::RoadClassification road_classification)
: source(source), target(target), name_id(name_id), weight(weight), forward(forward), : source(source), target(target), name_id(name_id), weight(weight), forward(forward),
backward(backward), roundabout(roundabout), access_restricted(access_restricted), backward(backward), roundabout(roundabout), circular(circular), access_restricted(access_restricted),
startpoint(startpoint), is_split(is_split), travel_mode(travel_mode), startpoint(startpoint), is_split(is_split), travel_mode(travel_mode),
lane_description_id(lane_description_id), road_classification(std::move(road_classification)) lane_description_id(lane_description_id), road_classification(std::move(road_classification))
{ {
@ -119,6 +123,7 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source,
bool forward, bool forward,
bool backward, bool backward,
bool roundabout, bool roundabout,
bool circular,
bool access_restricted, bool access_restricted,
bool startpoint, bool startpoint,
TravelMode travel_mode, TravelMode travel_mode,
@ -132,6 +137,7 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source,
forward, forward,
backward, backward,
roundabout, roundabout,
circular,
access_restricted, access_restricted,
startpoint, startpoint,
travel_mode, travel_mode,

View File

@ -20,7 +20,7 @@ struct NodeBasedEdgeData
NodeBasedEdgeData() NodeBasedEdgeData()
: distance(INVALID_EDGE_WEIGHT), edge_id(SPECIAL_NODEID), : distance(INVALID_EDGE_WEIGHT), edge_id(SPECIAL_NODEID),
name_id(std::numeric_limits<unsigned>::max()), access_restricted(false), reversed(false), name_id(std::numeric_limits<unsigned>::max()), access_restricted(false), reversed(false),
roundabout(false), travel_mode(TRAVEL_MODE_INACCESSIBLE), roundabout(false), circular(false), travel_mode(TRAVEL_MODE_INACCESSIBLE),
lane_description_id(INVALID_LANE_DESCRIPTIONID) lane_description_id(INVALID_LANE_DESCRIPTIONID)
{ {
} }
@ -31,12 +31,14 @@ struct NodeBasedEdgeData
bool access_restricted, bool access_restricted,
bool reversed, bool reversed,
bool roundabout, bool roundabout,
bool circular,
bool startpoint, bool startpoint,
extractor::TravelMode travel_mode, extractor::TravelMode travel_mode,
const LaneDescriptionID lane_description_id) const LaneDescriptionID lane_description_id)
: distance(distance), edge_id(edge_id), name_id(name_id), : distance(distance), edge_id(edge_id), name_id(name_id),
access_restricted(access_restricted), reversed(reversed), roundabout(roundabout), access_restricted(access_restricted), reversed(reversed), roundabout(roundabout),
startpoint(startpoint), travel_mode(travel_mode), lane_description_id(lane_description_id) circular(circular), startpoint(startpoint), travel_mode(travel_mode),
lane_description_id(lane_description_id)
{ {
} }
@ -46,6 +48,7 @@ struct NodeBasedEdgeData
bool access_restricted : 1; bool access_restricted : 1;
bool reversed : 1; bool reversed : 1;
bool roundabout : 1; bool roundabout : 1;
bool circular : 1;
bool startpoint : 1; bool startpoint : 1;
extractor::TravelMode travel_mode : 4; extractor::TravelMode travel_mode : 4;
LaneDescriptionID lane_description_id; LaneDescriptionID lane_description_id;
@ -54,7 +57,8 @@ struct NodeBasedEdgeData
bool IsCompatibleTo(const NodeBasedEdgeData &other) const bool IsCompatibleTo(const NodeBasedEdgeData &other) const
{ {
return (reversed == other.reversed) && (roundabout == other.roundabout) && return (reversed == other.reversed) && (roundabout == other.roundabout) &&
(startpoint == other.startpoint) && (access_restricted == other.access_restricted) && (circular == other.circular) && (startpoint == other.startpoint) &&
(access_restricted == other.access_restricted) &&
(travel_mode == other.travel_mode) && (travel_mode == other.travel_mode) &&
(road_classification == other.road_classification); (road_classification == other.road_classification);
} }
@ -82,6 +86,7 @@ NodeBasedDynamicGraphFromEdges(NodeID number_of_nodes,
BOOST_ASSERT(output_edge.data.distance > 0); BOOST_ASSERT(output_edge.data.distance > 0);
output_edge.data.roundabout = input_edge.roundabout; output_edge.data.roundabout = input_edge.roundabout;
output_edge.data.circular = input_edge.circular;
output_edge.data.name_id = input_edge.name_id; output_edge.data.name_id = input_edge.name_id;
output_edge.data.access_restricted = input_edge.access_restricted; output_edge.data.access_restricted = input_edge.access_restricted;
output_edge.data.travel_mode = input_edge.travel_mode; output_edge.data.travel_mode = input_edge.travel_mode;

View File

@ -554,6 +554,9 @@ function handle_roundabouts(way,result)
if way:get_value_by_key("junction") == "roundabout" then if way:get_value_by_key("junction") == "roundabout" then
result.roundabout = true result.roundabout = true
end end
if way:get_value_by_key("junction") == "circular" then
result.circular = true
end
end end
-- Set access restriction flag if access is allowed under certain restrictions only -- Set access restriction flag if access is allowed under certain restrictions only
@ -638,6 +641,7 @@ function handle_oneway(way,result)
oneway == "1" or oneway == "1" or
oneway == "true" or oneway == "true" or
way:get_value_by_key("junction") == "roundabout" or way:get_value_by_key("junction") == "roundabout" or
way:get_value_by_key("junction") == "circular" or
(way:get_value_by_key("highway") == "motorway" and oneway ~= "no") then (way:get_value_by_key("highway") == "motorway" and oneway ~= "no") then
result.backward_mode = mode.inaccessible result.backward_mode = mode.inaccessible

View File

@ -327,6 +327,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
true, true,
false, false,
parsed_way.roundabout, parsed_way.roundabout,
parsed_way.circular,
parsed_way.is_access_restricted, parsed_way.is_access_restricted,
parsed_way.is_startpoint, parsed_way.is_startpoint,
parsed_way.backward_travel_mode, parsed_way.backward_travel_mode,
@ -359,6 +360,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
true, true,
!forward_only, !forward_only,
parsed_way.roundabout, parsed_way.roundabout,
parsed_way.circular,
parsed_way.is_access_restricted, parsed_way.is_access_restricted,
parsed_way.is_startpoint, parsed_way.is_startpoint,
parsed_way.forward_travel_mode, parsed_way.forward_travel_mode,
@ -381,6 +383,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
false, false,
true, true,
parsed_way.roundabout, parsed_way.roundabout,
parsed_way.circular,
parsed_way.is_access_restricted, parsed_way.is_access_restricted,
parsed_way.is_startpoint, parsed_way.is_startpoint,
parsed_way.backward_travel_mode, parsed_way.backward_travel_mode,

View File

@ -88,7 +88,7 @@ CoordinateExtractor::GetCoordinateAlongRoad(const NodeID intersection_node,
const auto &turn_edge_data = node_based_graph.GetEdgeData(turn_edge); const auto &turn_edge_data = node_based_graph.GetEdgeData(turn_edge);
// roundabouts, check early to avoid other costly checks // roundabouts, check early to avoid other costly checks
if (turn_edge_data.roundabout) if (turn_edge_data.roundabout || turn_edge_data.circular )
return TrimCoordinatesToLength(std::move(coordinates), return TrimCoordinatesToLength(std::move(coordinates),
distance_to_skip_over_due_to_coordinate_inaccuracies) distance_to_skip_over_due_to_coordinate_inaccuracies)
.back(); .back();

View File

@ -56,7 +56,8 @@ bool IntersectionNormalizer::CanMerge(const NodeID node_at_intersection,
return false; return false;
// may not be on a roundabout // may not be on a roundabout
if (first_data.roundabout || second_data.roundabout) if (first_data.roundabout || second_data.roundabout || first_data.circular ||
second_data.circular)
return false; return false;
// exactly one of them has to be reversed // exactly one of them has to be reversed
@ -237,7 +238,8 @@ Intersection IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersect
const bool is_connected_to_roundabout = [this, &intersection]() { const bool is_connected_to_roundabout = [this, &intersection]() {
for (const auto &road : intersection) for (const auto &road : intersection)
{ {
if (node_based_graph.GetEdgeData(road.eid).roundabout) if (node_based_graph.GetEdgeData(road.eid).roundabout ||
node_based_graph.GetEdgeData(road.eid).circular)
return true; return true;
} }
return false; return false;

View File

@ -67,7 +67,7 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const
{ {
const auto &in_edge_data = node_based_graph.GetEdgeData(via_eid); const auto &in_edge_data = node_based_graph.GetEdgeData(via_eid);
bool on_roundabout = in_edge_data.roundabout; bool on_roundabout = in_edge_data.roundabout || in_edge_data.circular;
bool can_enter_roundabout = false; bool can_enter_roundabout = false;
bool can_exit_roundabout_separately = false; bool can_exit_roundabout_separately = false;
@ -82,7 +82,7 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
if (edge_data.reversed || !road.entry_allowed) if (edge_data.reversed || !road.entry_allowed)
continue; continue;
if (edge_data.roundabout) if (edge_data.roundabout || edge_data.circular)
{ {
can_enter_roundabout = true; can_enter_roundabout = true;
} }
@ -106,7 +106,7 @@ void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
Intersection &intersection) const Intersection &intersection) const
{ {
const auto &in_edge_data = node_based_graph.GetEdgeData(via_eid); const auto &in_edge_data = node_based_graph.GetEdgeData(via_eid);
if (in_edge_data.roundabout) if (in_edge_data.roundabout || in_edge_data.circular)
return; return;
bool past_roundabout_angle = false; bool past_roundabout_angle = false;
@ -121,7 +121,7 @@ void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
if (edge_data.reversed) if (edge_data.reversed)
{ {
// remember whether we have seen the roundabout in-part // remember whether we have seen the roundabout in-part
if (edge_data.roundabout) if (edge_data.roundabout || edge_data.circular)
past_roundabout_angle = true; past_roundabout_angle = true;
continue; continue;
@ -131,8 +131,8 @@ void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
// This workaround handles cases in which an exit precedes and entry. The resulting // This workaround handles cases in which an exit precedes and entry. The resulting
// u-turn against the roundabout direction is invalidated. // u-turn against the roundabout direction is invalidated.
// The sorting of the angles represents a problem for left-sided driving, though. // The sorting of the angles represents a problem for left-sided driving, though.
if (!edge_data.roundabout && node_based_graph.GetTarget(road.eid) != from_nid && if (!edge_data.roundabout && !edge_data.circular &&
past_roundabout_angle) node_based_graph.GetTarget(road.eid) != from_nid && past_roundabout_angle)
{ {
road.entry_allowed = false; road.entry_allowed = false;
} }
@ -176,7 +176,7 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node)) for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
{ {
const auto edge_data = node_based_graph.GetEdgeData(edge); const auto edge_data = node_based_graph.GetEdgeData(edge);
if (edge_data.roundabout) if (edge_data.roundabout || edge_data.circular)
continue; continue;
// there is a single non-roundabout edge // there is a single non-roundabout edge
@ -224,12 +224,14 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
std::unordered_set<unsigned> connected_names; std::unordered_set<unsigned> connected_names;
const auto getNextOnRoundabout = [this, &roundabout_name_ids, &connected_names]( const auto getNextOnRoundabout = [this, &roundabout_name_ids, &connected_names](
const NodeID node) { const NodeID node, const bool roundabout, const bool circular) {
BOOST_ASSERT(roundabout != circular);
EdgeID continue_edge = SPECIAL_EDGEID; EdgeID continue_edge = SPECIAL_EDGEID;
for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node)) for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
{ {
const auto &edge_data = node_based_graph.GetEdgeData(edge); const auto &edge_data = node_based_graph.GetEdgeData(edge);
if (!edge_data.reversed && edge_data.roundabout) if (!edge_data.reversed && (edge_data.circular == circular) &&
(edge_data.roundabout == roundabout))
{ {
if (SPECIAL_EDGEID != continue_edge) if (SPECIAL_EDGEID != continue_edge)
{ {
@ -251,7 +253,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
continue_edge = edge; continue_edge = edge;
} }
else if (!edge_data.roundabout) else if (!edge_data.roundabout && !edge_data.circular)
{ {
// remember all connected road names // remember all connected road names
connected_names.insert(edge_data.name_id); connected_names.insert(edge_data.name_id);
@ -268,7 +270,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
for (const auto edge : node_based_graph.GetAdjacentEdgeRange(at_node)) for (const auto edge : node_based_graph.GetAdjacentEdgeRange(at_node))
{ {
const auto &edge_data = node_based_graph.GetEdgeData(edge); const auto &edge_data = node_based_graph.GetEdgeData(edge);
if (edge_data.roundabout) if (edge_data.roundabout || edge_data.circular)
count++; count++;
} }
return count; return count;
@ -294,6 +296,19 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
double roundabout_length = 0.; double roundabout_length = 0.;
NodeID last_node = nid; NodeID last_node = nid;
// cannot be find_if, as long as adjacent edge range does not work.
bool roundabout = false, circular = false;
for (const auto eid : node_based_graph.GetAdjacentEdgeRange(nid))
{
const auto data = node_based_graph.GetEdgeData(eid);
roundabout |= data.roundabout;
circular |= data.circular;
}
if (roundabout == circular)
return RoundaboutType::None;
while (0 == roundabout_nodes.count(last_node)) while (0 == roundabout_nodes.count(last_node))
{ {
// only count exits/entry locations // only count exits/entry locations
@ -304,7 +319,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
if (countRoundaboutFlags(last_node) != 2) if (countRoundaboutFlags(last_node) != 2)
return RoundaboutType::None; return RoundaboutType::None;
const auto eid = getNextOnRoundabout(last_node); const auto eid = getNextOnRoundabout(last_node, roundabout, circular);
if (eid == SPECIAL_EDGEID) if (eid == SPECIAL_EDGEID)
{ {
@ -324,7 +339,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
return RoundaboutType::None; return RoundaboutType::None;
// More a traffic loop than anything else, currently treated as roundabout turn // More a traffic loop than anything else, currently treated as roundabout turn
if (roundabout_nodes.size() == 1) if (roundabout_nodes.size() == 1 && roundabout)
{ {
return RoundaboutType::RoundaboutIntersection; return RoundaboutType::RoundaboutIntersection;
} }
@ -336,13 +351,18 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
// This function can theoretically fail if the roundabout name is partly // This function can theoretically fail if the roundabout name is partly
// used with a reference and without. This will be fixed automatically // used with a reference and without. This will be fixed automatically
// when we handle references separately or if the useage is more consistent // when we handle references separately or if the useage is more consistent
const auto is_rotary = 1 == roundabout_name_ids.size() && // const auto is_rotary = (1 == roundabout_name_ids.size()) &&
0 == connected_names.count(*roundabout_name_ids.begin()) && // (circular || //
radius > MAX_ROUNDABOUT_RADIUS; ((0 == connected_names.count(*roundabout_name_ids.begin())) && //
(radius > MAX_ROUNDABOUT_RADIUS)));
if (is_rotary) if (is_rotary)
return RoundaboutType::Rotary; return RoundaboutType::Rotary;
// circular intersections need to be rotaries
if (circular)
return RoundaboutType::None;
// Looks like an intersection: four ways and turn angles are easy to distinguish // Looks like an intersection: four ways and turn angles are easy to distinguish
const auto is_roundabout_intersection = qualifiesAsRoundaboutIntersection(roundabout_nodes) && const auto is_roundabout_intersection = qualifiesAsRoundaboutIntersection(roundabout_nodes) &&
radius < MAX_ROUNDABOUT_INTERSECTION_RADIUS; radius < MAX_ROUNDABOUT_INTERSECTION_RADIUS;
@ -376,7 +396,7 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou
auto &road = intersection[idx]; auto &road = intersection[idx];
auto &turn = road; auto &turn = road;
const auto &out_data = node_based_graph.GetEdgeData(road.eid); const auto &out_data = node_based_graph.GetEdgeData(road.eid);
if (out_data.roundabout) if (out_data.roundabout || out_data.circular)
{ {
// TODO can forks happen in roundabouts? E.g. required lane changes // TODO can forks happen in roundabouts? E.g. required lane changes
if (1 == node_based_graph.GetDirectedOutDegree(node_at_center_of_intersection)) if (1 == node_based_graph.GetDirectedOutDegree(node_at_center_of_intersection))
@ -401,6 +421,7 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou
const auto &data_of_leaving_edge = node_based_graph.GetEdgeData(eid); const auto &data_of_leaving_edge = node_based_graph.GetEdgeData(eid);
if (!data_of_leaving_edge.reversed && if (!data_of_leaving_edge.reversed &&
!data_of_leaving_edge.roundabout && !data_of_leaving_edge.roundabout &&
!data_of_leaving_edge.circular &&
!data_of_leaving_edge.road_classification.IsLowPriorityRoadClass()) !data_of_leaving_edge.road_classification.IsLowPriorityRoadClass())
return true; return true;
} }
@ -433,7 +454,7 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou
continue; continue;
auto &turn = road; auto &turn = road;
const auto &out_data = node_based_graph.GetEdgeData(turn.eid); const auto &out_data = node_based_graph.GetEdgeData(turn.eid);
if (out_data.roundabout) if (out_data.roundabout || out_data.circular)
{ {
if (can_exit_roundabout_separately) if (can_exit_roundabout_separately)
turn.instruction = TurnInstruction::ENTER_ROUNDABOUT_AT_EXIT( turn.instruction = TurnInstruction::ENTER_ROUNDABOUT_AT_EXIT(

View File

@ -220,6 +220,7 @@ void LuaScriptingEnvironment::InitContext(LuaScriptingContext &context)
&ExtractionWay::GetTurnLanesBackward, &ExtractionWay::GetTurnLanesBackward,
&ExtractionWay::SetTurnLanesBackward) &ExtractionWay::SetTurnLanesBackward)
.def_readwrite("roundabout", &ExtractionWay::roundabout) .def_readwrite("roundabout", &ExtractionWay::roundabout)
.def_readwrite("circular", &ExtractionWay::circular)
.def_readwrite("is_access_restricted", &ExtractionWay::is_access_restricted) .def_readwrite("is_access_restricted", &ExtractionWay::is_access_restricted)
.def_readwrite("is_startpoint", &ExtractionWay::is_startpoint) .def_readwrite("is_startpoint", &ExtractionWay::is_startpoint)
.def_readwrite("duration", &ExtractionWay::duration) .def_readwrite("duration", &ExtractionWay::duration)

View File

@ -350,6 +350,11 @@
"object_types": [ "way" ], "object_types": [ "way" ],
"value": "roundabout" "value": "roundabout"
}, },
{
"key": "junction",
"object_types": [ "way" ],
"value": "circular"
},
{ {
"key": "type", "key": "type",
"value": "restriction", "value": "restriction",

View File

@ -16,6 +16,30 @@ using namespace osrm::extractor;
using InputEdge = util::NodeBasedDynamicGraph::InputEdge; using InputEdge = util::NodeBasedDynamicGraph::InputEdge;
using Graph = util::NodeBasedDynamicGraph; using Graph = util::NodeBasedDynamicGraph;
namespace
{
// creates a default edge of unit weight
inline InputEdge MakeUnitEdge(const NodeID from, const NodeID to)
{
// src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, circular
// travel_mode
return {from,
to,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID};
}
} // namespace
BOOST_AUTO_TEST_CASE(long_road_test) BOOST_AUTO_TEST_CASE(long_road_test)
{ {
// //
@ -28,96 +52,14 @@ BOOST_AUTO_TEST_CASE(long_road_test)
RestrictionMap map; RestrictionMap map;
CompressedEdgeContainer container; CompressedEdgeContainer container;
std::vector<InputEdge> edges = { std::vector<InputEdge> edges = {MakeUnitEdge(0, 1),
// src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode MakeUnitEdge(1, 0),
{0, MakeUnitEdge(1, 2),
1, MakeUnitEdge(2, 1),
1, MakeUnitEdge(2, 3),
SPECIAL_EDGEID, MakeUnitEdge(3, 2),
0, MakeUnitEdge(3, 4),
false, MakeUnitEdge(4, 3)};
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{1,
0,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{1,
2,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{2,
1,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{2,
3,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{3,
2,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{3,
4,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{4,
3,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID}};
BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[2].data)); BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[2].data));
BOOST_ASSERT(edges[2].data.IsCompatibleTo(edges[4].data)); BOOST_ASSERT(edges[2].data.IsCompatibleTo(edges[4].data));
@ -147,141 +89,18 @@ BOOST_AUTO_TEST_CASE(loop_test)
RestrictionMap map; RestrictionMap map;
CompressedEdgeContainer container; CompressedEdgeContainer container;
std::vector<InputEdge> edges = { std::vector<InputEdge> edges = {MakeUnitEdge(0, 1),
// src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode MakeUnitEdge(0, 5),
{0, MakeUnitEdge(1, 0),
1, MakeUnitEdge(1, 2),
1, MakeUnitEdge(2, 1),
SPECIAL_EDGEID, MakeUnitEdge(2, 3),
0, MakeUnitEdge(3, 2),
false, MakeUnitEdge(3, 4),
false, MakeUnitEdge(4, 3),
false, MakeUnitEdge(4, 5),
true, MakeUnitEdge(5, 0),
TRAVEL_MODE_INACCESSIBLE, MakeUnitEdge(5, 4)};
INVALID_LANE_DESCRIPTIONID},
{0,
5,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{1,
0,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{1,
2,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{2,
1,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{2,
3,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{3,
2,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{3,
4,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{4,
3,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{4,
5,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{5,
0,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{5,
4,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
};
BOOST_ASSERT(edges.size() == 12); BOOST_ASSERT(edges.size() == 12);
BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data)); BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data));
@ -322,75 +141,12 @@ BOOST_AUTO_TEST_CASE(t_intersection)
RestrictionMap map; RestrictionMap map;
CompressedEdgeContainer container; CompressedEdgeContainer container;
std::vector<InputEdge> edges = { std::vector<InputEdge> edges = {MakeUnitEdge(0, 1),
// src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode MakeUnitEdge(1, 0),
{0, MakeUnitEdge(1, 2),
1, MakeUnitEdge(1, 3),
1, MakeUnitEdge(2, 1),
SPECIAL_EDGEID, MakeUnitEdge(3, 1)};
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{1,
0,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{1,
2,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{1,
3,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{2,
1,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{3,
1,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
};
BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data)); BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data));
BOOST_ASSERT(edges[1].data.IsCompatibleTo(edges[2].data)); BOOST_ASSERT(edges[1].data.IsCompatibleTo(edges[2].data));
@ -419,52 +175,8 @@ BOOST_AUTO_TEST_CASE(street_name_changes)
CompressedEdgeContainer container; CompressedEdgeContainer container;
std::vector<InputEdge> edges = { std::vector<InputEdge> edges = {
// src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode MakeUnitEdge(0, 1), MakeUnitEdge(1, 0), MakeUnitEdge(1, 2), MakeUnitEdge(2, 1)};
{0, edges[2].data.name_id = edges[3].data.name_id = 1;
1,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{1,
0,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{1,
2,
1,
SPECIAL_EDGEID,
1,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{2,
1,
1,
SPECIAL_EDGEID,
1,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
};
BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data)); BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data));
BOOST_ASSERT(edges[2].data.IsCompatibleTo(edges[3].data)); BOOST_ASSERT(edges[2].data.IsCompatibleTo(edges[3].data));
@ -489,52 +201,9 @@ BOOST_AUTO_TEST_CASE(direction_changes)
CompressedEdgeContainer container; CompressedEdgeContainer container;
std::vector<InputEdge> edges = { std::vector<InputEdge> edges = {
// src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode MakeUnitEdge(0, 1), MakeUnitEdge(1, 0), MakeUnitEdge(1, 2), MakeUnitEdge(2, 1)};
{0, // make first edge point forward
1, edges[1].data.reversed = true;
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{1,
0,
1,
SPECIAL_EDGEID,
0,
false,
true,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{1,
2,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
{2,
1,
1,
SPECIAL_EDGEID,
0,
false,
false,
false,
true,
TRAVEL_MODE_INACCESSIBLE,
INVALID_LANE_DESCRIPTIONID},
};
Graph graph(5, edges); Graph graph(5, edges);
compressor.Compress(barrier_nodes, traffic_lights, map, graph, container); compressor.Compress(barrier_nodes, traffic_lights, map, graph, container);