diff --git a/docs/http.md b/docs/http.md index a389f43e0..09d07dc26 100644 --- a/docs/http.md +++ b/docs/http.md @@ -547,7 +547,7 @@ step. | `use lane` | going straight on a specific lane | | `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` | - | `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`. | | `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 | diff --git a/features/guidance/circular.feature b/features/guidance/circular.feature new file mode 100644 index 000000000..82a414d95 --- /dev/null +++ b/features/guidance/circular.feature @@ -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 | diff --git a/include/extractor/extraction_way.hpp b/include/extractor/extraction_way.hpp index e2dd3fca1..3fe1673be 100644 --- a/include/extractor/extraction_way.hpp +++ b/include/extractor/extraction_way.hpp @@ -45,6 +45,7 @@ struct ExtractionWay backward_speed = -1; duration = -1; roundabout = false; + circular = false; is_startpoint = true; is_access_restricted = false; name.clear(); @@ -89,6 +90,7 @@ struct ExtractionWay std::string turn_lanes_forward; std::string turn_lanes_backward; bool roundabout; + bool circular; bool is_access_restricted; bool is_startpoint; TravelMode forward_travel_mode : 4; diff --git a/include/extractor/internal_extractor_edge.hpp b/include/extractor/internal_extractor_edge.hpp index 890f8521c..01e708e06 100644 --- a/include/extractor/internal_extractor_edge.hpp +++ b/include/extractor/internal_extractor_edge.hpp @@ -43,11 +43,12 @@ struct InternalExtractorEdge MIN_OSM_NODEID, 0, 0, - false, - false, - false, - false, - true, + false, // forward + false, // backward + false, // roundabout + false, // circular + false, // access restricted + true, // can be startpoint TRAVEL_MODE_INACCESSIBLE, false, guidance::TurnLaneType::empty, @@ -62,6 +63,7 @@ struct InternalExtractorEdge bool forward, bool backward, bool roundabout, + bool circular, bool access_restricted, bool startpoint, TravelMode travel_mode, @@ -75,6 +77,7 @@ struct InternalExtractorEdge forward, backward, roundabout, + circular, access_restricted, startpoint, travel_mode, @@ -99,11 +102,12 @@ struct InternalExtractorEdge MIN_OSM_NODEID, 0, WeightData(), - false, - false, - false, - false, - true, + false, // forward + false, // backward + false, // roundabout + false, // circular + false, // access restricted + true, // can be startpoint TRAVEL_MODE_INACCESSIBLE, false, INVALID_LANE_DESCRIPTIONID, @@ -115,11 +119,12 @@ struct InternalExtractorEdge MAX_OSM_NODEID, SPECIAL_NODEID, WeightData(), - false, - false, - false, - false, - true, + false, // forward + false, // backward + false, // roundabout + false, // circular + false, // access restricted + true, // can be startpoint TRAVEL_MODE_INACCESSIBLE, false, INVALID_LANE_DESCRIPTIONID, diff --git a/include/extractor/node_based_edge.hpp b/include/extractor/node_based_edge.hpp index f1a658b93..18c1ad44e 100644 --- a/include/extractor/node_based_edge.hpp +++ b/include/extractor/node_based_edge.hpp @@ -22,6 +22,7 @@ struct NodeBasedEdge bool forward, bool backward, bool roundabout, + bool circular, bool access_restricted, bool startpoint, TravelMode travel_mode, @@ -38,6 +39,7 @@ struct NodeBasedEdge bool forward : 1; bool backward : 1; bool roundabout : 1; + bool circular : 1; bool access_restricted : 1; bool startpoint : 1; bool is_split : 1; @@ -55,6 +57,7 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge bool forward, bool backward, bool roundabout, + bool circular, bool access_restricted, bool startpoint, TravelMode travel_mode, @@ -70,7 +73,7 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge inline NodeBasedEdge::NodeBasedEdge() : 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) { } @@ -82,6 +85,7 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source, bool forward, bool backward, bool roundabout, + bool circular, bool access_restricted, bool startpoint, TravelMode travel_mode, @@ -89,7 +93,7 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source, const LaneDescriptionID lane_description_id, guidance::RoadClassification road_classification) : 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), lane_description_id(lane_description_id), road_classification(std::move(road_classification)) { @@ -119,6 +123,7 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source, bool forward, bool backward, bool roundabout, + bool circular, bool access_restricted, bool startpoint, TravelMode travel_mode, @@ -132,6 +137,7 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source, forward, backward, roundabout, + circular, access_restricted, startpoint, travel_mode, diff --git a/include/util/node_based_graph.hpp b/include/util/node_based_graph.hpp index c5981c665..0d276408e 100644 --- a/include/util/node_based_graph.hpp +++ b/include/util/node_based_graph.hpp @@ -20,7 +20,7 @@ struct NodeBasedEdgeData NodeBasedEdgeData() : distance(INVALID_EDGE_WEIGHT), edge_id(SPECIAL_NODEID), name_id(std::numeric_limits::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) { } @@ -31,12 +31,14 @@ struct NodeBasedEdgeData bool access_restricted, bool reversed, bool roundabout, + bool circular, bool startpoint, extractor::TravelMode travel_mode, const LaneDescriptionID lane_description_id) : distance(distance), edge_id(edge_id), name_id(name_id), 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 reversed : 1; bool roundabout : 1; + bool circular : 1; bool startpoint : 1; extractor::TravelMode travel_mode : 4; LaneDescriptionID lane_description_id; @@ -54,7 +57,8 @@ struct NodeBasedEdgeData bool IsCompatibleTo(const NodeBasedEdgeData &other) const { 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) && (road_classification == other.road_classification); } @@ -82,6 +86,7 @@ NodeBasedDynamicGraphFromEdges(NodeID number_of_nodes, BOOST_ASSERT(output_edge.data.distance > 0); 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.access_restricted = input_edge.access_restricted; output_edge.data.travel_mode = input_edge.travel_mode; diff --git a/profiles/car.lua b/profiles/car.lua index 8dedb6aa3..0ac74a051 100644 --- a/profiles/car.lua +++ b/profiles/car.lua @@ -554,6 +554,9 @@ function handle_roundabouts(way,result) if way:get_value_by_key("junction") == "roundabout" then result.roundabout = true end + if way:get_value_by_key("junction") == "circular" then + result.circular = true + end end -- 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 == "true" 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 result.backward_mode = mode.inaccessible diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp index 850d58d0c..4c520c784 100644 --- a/src/extractor/extractor_callbacks.cpp +++ b/src/extractor/extractor_callbacks.cpp @@ -327,6 +327,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti true, false, parsed_way.roundabout, + parsed_way.circular, parsed_way.is_access_restricted, parsed_way.is_startpoint, parsed_way.backward_travel_mode, @@ -359,6 +360,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti true, !forward_only, parsed_way.roundabout, + parsed_way.circular, parsed_way.is_access_restricted, parsed_way.is_startpoint, parsed_way.forward_travel_mode, @@ -381,6 +383,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti false, true, parsed_way.roundabout, + parsed_way.circular, parsed_way.is_access_restricted, parsed_way.is_startpoint, parsed_way.backward_travel_mode, diff --git a/src/extractor/guidance/coordinate_extractor.cpp b/src/extractor/guidance/coordinate_extractor.cpp index fe98de733..5a0c893ae 100644 --- a/src/extractor/guidance/coordinate_extractor.cpp +++ b/src/extractor/guidance/coordinate_extractor.cpp @@ -88,7 +88,7 @@ CoordinateExtractor::GetCoordinateAlongRoad(const NodeID intersection_node, const auto &turn_edge_data = node_based_graph.GetEdgeData(turn_edge); // 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), distance_to_skip_over_due_to_coordinate_inaccuracies) .back(); diff --git a/src/extractor/guidance/intersection_normalizer.cpp b/src/extractor/guidance/intersection_normalizer.cpp index 12210ce6e..6f19d6a4b 100644 --- a/src/extractor/guidance/intersection_normalizer.cpp +++ b/src/extractor/guidance/intersection_normalizer.cpp @@ -56,7 +56,8 @@ bool IntersectionNormalizer::CanMerge(const NodeID node_at_intersection, return false; // 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; // 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]() { 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 false; diff --git a/src/extractor/guidance/roundabout_handler.cpp b/src/extractor/guidance/roundabout_handler.cpp index dcfdb39e8..cefaafd8b 100644 --- a/src/extractor/guidance/roundabout_handler.cpp +++ b/src/extractor/guidance/roundabout_handler.cpp @@ -67,7 +67,7 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags( const NodeID from_nid, const EdgeID via_eid, const Intersection &intersection) const { 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_exit_roundabout_separately = false; @@ -82,7 +82,7 @@ detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags( if (edge_data.reversed || !road.entry_allowed) continue; - if (edge_data.roundabout) + if (edge_data.roundabout || edge_data.circular) { can_enter_roundabout = true; } @@ -106,7 +106,7 @@ void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid, Intersection &intersection) const { 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; bool past_roundabout_angle = false; @@ -121,7 +121,7 @@ void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid, if (edge_data.reversed) { // remember whether we have seen the roundabout in-part - if (edge_data.roundabout) + if (edge_data.roundabout || edge_data.circular) past_roundabout_angle = true; 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 // u-turn against the roundabout direction is invalidated. // 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 && - past_roundabout_angle) + if (!edge_data.roundabout && !edge_data.circular && + node_based_graph.GetTarget(road.eid) != from_nid && past_roundabout_angle) { road.entry_allowed = false; } @@ -176,7 +176,7 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection( for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node)) { const auto edge_data = node_based_graph.GetEdgeData(edge); - if (edge_data.roundabout) + if (edge_data.roundabout || edge_data.circular) continue; // there is a single non-roundabout edge @@ -224,12 +224,14 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const std::unordered_set 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; for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node)) { 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) { @@ -251,7 +253,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const continue_edge = edge; } - else if (!edge_data.roundabout) + else if (!edge_data.roundabout && !edge_data.circular) { // remember all connected road names 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)) { const auto &edge_data = node_based_graph.GetEdgeData(edge); - if (edge_data.roundabout) + if (edge_data.roundabout || edge_data.circular) count++; } return count; @@ -294,6 +296,19 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const double roundabout_length = 0.; 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)) { // only count exits/entry locations @@ -304,7 +319,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const if (countRoundaboutFlags(last_node) != 2) return RoundaboutType::None; - const auto eid = getNextOnRoundabout(last_node); + const auto eid = getNextOnRoundabout(last_node, roundabout, circular); if (eid == SPECIAL_EDGEID) { @@ -324,7 +339,7 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const return RoundaboutType::None; // 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; } @@ -336,13 +351,18 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const // This function can theoretically fail if the roundabout name is partly // used with a reference and without. This will be fixed automatically // when we handle references separately or if the useage is more consistent - const auto is_rotary = 1 == roundabout_name_ids.size() && // - 0 == connected_names.count(*roundabout_name_ids.begin()) && // - radius > MAX_ROUNDABOUT_RADIUS; + const auto is_rotary = (1 == roundabout_name_ids.size()) && + (circular || // + ((0 == connected_names.count(*roundabout_name_ids.begin())) && // + (radius > MAX_ROUNDABOUT_RADIUS))); if (is_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 const auto is_roundabout_intersection = qualifiesAsRoundaboutIntersection(roundabout_nodes) && radius < MAX_ROUNDABOUT_INTERSECTION_RADIUS; @@ -376,7 +396,7 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou auto &road = intersection[idx]; auto &turn = road; 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 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); if (!data_of_leaving_edge.reversed && !data_of_leaving_edge.roundabout && + !data_of_leaving_edge.circular && !data_of_leaving_edge.road_classification.IsLowPriorityRoadClass()) return true; } @@ -433,7 +454,7 @@ Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabou continue; auto &turn = road; 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) turn.instruction = TurnInstruction::ENTER_ROUNDABOUT_AT_EXIT( diff --git a/src/extractor/scripting_environment_lua.cpp b/src/extractor/scripting_environment_lua.cpp index 16be737df..2e36bda42 100644 --- a/src/extractor/scripting_environment_lua.cpp +++ b/src/extractor/scripting_environment_lua.cpp @@ -220,6 +220,7 @@ void LuaScriptingEnvironment::InitContext(LuaScriptingContext &context) &ExtractionWay::GetTurnLanesBackward, &ExtractionWay::SetTurnLanesBackward) .def_readwrite("roundabout", &ExtractionWay::roundabout) + .def_readwrite("circular", &ExtractionWay::circular) .def_readwrite("is_access_restricted", &ExtractionWay::is_access_restricted) .def_readwrite("is_startpoint", &ExtractionWay::is_startpoint) .def_readwrite("duration", &ExtractionWay::duration) diff --git a/taginfo.json b/taginfo.json index 4e1770a02..2ee0214cf 100644 --- a/taginfo.json +++ b/taginfo.json @@ -350,6 +350,11 @@ "object_types": [ "way" ], "value": "roundabout" }, + { + "key": "junction", + "object_types": [ "way" ], + "value": "circular" + }, { "key": "type", "value": "restriction", diff --git a/unit_tests/extractor/graph_compressor.cpp b/unit_tests/extractor/graph_compressor.cpp index 7ef65ae87..f69d31acd 100644 --- a/unit_tests/extractor/graph_compressor.cpp +++ b/unit_tests/extractor/graph_compressor.cpp @@ -16,6 +16,30 @@ using namespace osrm::extractor; using InputEdge = util::NodeBasedDynamicGraph::InputEdge; 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) { // @@ -28,96 +52,14 @@ BOOST_AUTO_TEST_CASE(long_road_test) RestrictionMap map; CompressedEdgeContainer container; - std::vector edges = { - // src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode - {0, - 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, - 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}}; + std::vector edges = {MakeUnitEdge(0, 1), + MakeUnitEdge(1, 0), + MakeUnitEdge(1, 2), + MakeUnitEdge(2, 1), + MakeUnitEdge(2, 3), + MakeUnitEdge(3, 2), + MakeUnitEdge(3, 4), + MakeUnitEdge(4, 3)}; BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[2].data)); BOOST_ASSERT(edges[2].data.IsCompatibleTo(edges[4].data)); @@ -147,141 +89,18 @@ BOOST_AUTO_TEST_CASE(loop_test) RestrictionMap map; CompressedEdgeContainer container; - std::vector edges = { - // src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode - {0, - 1, - 1, - SPECIAL_EDGEID, - 0, - false, - false, - false, - true, - TRAVEL_MODE_INACCESSIBLE, - 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}, - }; + std::vector edges = {MakeUnitEdge(0, 1), + MakeUnitEdge(0, 5), + MakeUnitEdge(1, 0), + MakeUnitEdge(1, 2), + MakeUnitEdge(2, 1), + MakeUnitEdge(2, 3), + MakeUnitEdge(3, 2), + MakeUnitEdge(3, 4), + MakeUnitEdge(4, 3), + MakeUnitEdge(4, 5), + MakeUnitEdge(5, 0), + MakeUnitEdge(5, 4)}; BOOST_ASSERT(edges.size() == 12); BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data)); @@ -322,75 +141,12 @@ BOOST_AUTO_TEST_CASE(t_intersection) RestrictionMap map; CompressedEdgeContainer container; - std::vector edges = { - // src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode - {0, - 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, - 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}, - }; + std::vector edges = {MakeUnitEdge(0, 1), + MakeUnitEdge(1, 0), + MakeUnitEdge(1, 2), + MakeUnitEdge(1, 3), + MakeUnitEdge(2, 1), + MakeUnitEdge(3, 1)}; BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data)); BOOST_ASSERT(edges[1].data.IsCompatibleTo(edges[2].data)); @@ -419,52 +175,8 @@ BOOST_AUTO_TEST_CASE(street_name_changes) CompressedEdgeContainer container; std::vector edges = { - // src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode - {0, - 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}, - }; + MakeUnitEdge(0, 1), MakeUnitEdge(1, 0), MakeUnitEdge(1, 2), MakeUnitEdge(2, 1)}; + edges[2].data.name_id = edges[3].data.name_id = 1; BOOST_ASSERT(edges[0].data.IsCompatibleTo(edges[1].data)); BOOST_ASSERT(edges[2].data.IsCompatibleTo(edges[3].data)); @@ -489,52 +201,9 @@ BOOST_AUTO_TEST_CASE(direction_changes) CompressedEdgeContainer container; std::vector edges = { - // src, tgt, dist, edge_id, name_id, access_restricted, fwd, bkwd, roundabout, travel_mode - {0, - 1, - 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}, - }; + MakeUnitEdge(0, 1), MakeUnitEdge(1, 0), MakeUnitEdge(1, 2), MakeUnitEdge(2, 1)}; + // make first edge point forward + edges[1].data.reversed = true; Graph graph(5, edges); compressor.Compress(barrier_nodes, traffic_lights, map, graph, container);