diff --git a/features/guidance/obvious-turn-discovery.feature b/features/guidance/obvious-turn-discovery.feature index b2b1b09a4..c510bc790 100644 --- a/features/guidance/obvious-turn-discovery.feature +++ b/features/guidance/obvious-turn-discovery.feature @@ -78,9 +78,9 @@ Feature: Simple Turns | a | c | ab,bc | depart,arrive | | a | d | ab,bd,bd | depart,turn right,arrive | - #https://www.openstreetmap.org/#map=19/52.50602/13.25468 - # this scenario detects obvious correctly, but requires changes in collapsing roads - @todo + + #https://www.openstreetmap.org/#map=19/52.50602/13.25468 + # this scenario detects obvious correctly, but requires changes in collapsing roads Scenario: Small offset due to large Intersection Given the node map """ @@ -99,12 +99,13 @@ Feature: Simple Turns | df | residential | When I route I should get - | from | to | route | turns | - | a | e | abc,bde,bde | depart,turn right,arrive | - | a | f | abc,df,df | depart,turn sharp right,arrive | + | from | to | route | turns | + | a | e | abc,bde,bde,bde | depart,turn right,continue left,arrive | + | a | f | abc,bde,df,df | depart,turn right,turn right,arrive | # https://www.openstreetmap.org/#map=19/52.49709/13.26620 # https://www.openstreetmap.org/#map=19/52.49458/13.26273 + # scenario requires handling in post-processing @todo Scenario: Offsets in road Given a grid size of 3 meters @@ -160,7 +161,6 @@ Feature: Simple Turns # https://www.openstreetmap.org/#map=19/52.49198/13.28069 - @todo Scenario: Curved roads at turn Given the node map """ @@ -182,12 +182,11 @@ Feature: Simple Turns | befg | residential | casper | yes | When I route I should get - | from | to | route | turns | - | a | d | herbert,herbert | depart,arrive | - | a | g | herbert,casper,casper | depart,turn left,arrive | + | from | to | route | turns | + | a | d | herbert,herbert,herbert | depart,continue right,arrive | + | a | g | herbert,casper,casper | depart,turn left,arrive | # https://www.openstreetmap.org/#map=19/52.49189/13.28431 - @todo Scenario: Turning residential Given the node map """ @@ -205,12 +204,11 @@ Feature: Simple Turns | bd | residential | caspar | yes | When I route I should get - | from | to | route | turns | - | a | c | bismark,bismark | depart,arrive | - | a | d | bismark,caspar,caspar | depart,turn left,arrive | + | from | to | route | turns | + | a | c | bismark,bismark,bismark | depart,continue right,arrive | + | a | d | bismark,caspar,caspar | depart,turn left,arrive | # https://www.openstreetmap.org/#map=19/52.48681/13.28547 - @todo Scenario: Slight Loss in Category with turning road Given the node map """ @@ -225,7 +223,7 @@ Feature: Simple Turns | nodes | highway | name | | ab | tertiary | warm | | bcd | residential | warm | - | begg | tertiary | paul | + | befg | tertiary | paul | When I route I should get | from | to | route | turns | @@ -304,7 +302,6 @@ Feature: Simple Turns # https://www.openstreetmap.org/#map=19/37.61256/-122.40371 - @todo Scenario: Turning Road with Offset at Segregated Intersection Given the node map """ @@ -327,10 +324,10 @@ Feature: Simple Turns | icj | primary | camino | yes | When I route I should get - | from | to | route | turns | - | a | g | park,diego,diego | depart,turn slight left,arrive | - | a | h | park,camino,camino | depart,turn left,arrive | - | a | j | park,camino,camino | depart,turn right,arrive | + | from | to | route | turns | + | a | g | park,diego,diego | depart,turn slight left,arrive | + | a | h | park,diego,camino,camino | depart,turn slight left,turn left,arrive | + | a | j | park,camino,camino | depart,turn right,arrive | # https://www.openstreetmap.org/#map=19/37.76407/-122.49642 @@ -356,6 +353,7 @@ Feature: Simple Turns # https://www.openstreetmap.org/#map=19/37.77072/-122.41727 + # requires changes in post-processing @todo Scenario: Splitting road at traffic island before a turn Given the node map @@ -390,6 +388,7 @@ Feature: Simple Turns | e | a | ness,howard,howard | depart,turn slight right,arrive | | # https://www.openstreetmap.org/#map=19/37.63171/-122.46205 + # requires changes in post-processing @todo Scenario: Weird combination of turning roads Given the node map @@ -432,7 +431,6 @@ Feature: Simple Turns | d | n | depart,turn right,arrive | loop,drive,drive | # https://www.openstreetmap.org/#map=19/37.26591/-121.84474 - @todo Scenario: Road splitting (unmerged) Given the node map """ @@ -456,15 +454,14 @@ Feature: Simple Turns | hgfe | primary | lane | no | When I route I should get - | from | to | route | turns | - | a | h | lane,lane | depart,arrive | - | a | i | lane,, | depart,turn right,arrive | - | h | d | lane,lane | depart,arrive | - | h | i | lane,, | depart,turn left,arrive | + | from | to | route | turns | + | a | h | lane,lane | depart,arrive | + | a | i | lane,, | depart,turn sharp right,arrive | + | h | d | lane,lane | depart,arrive | + | h | i | lane,, | depart,turn left,arrive | # https://www.openstreetmap.org/#map=19/37.85108/-122.27078 - @todo Scenario: Turning Road category Given the node map """ @@ -474,9 +471,9 @@ Feature: Simple Turns f c i | | ` | j` - | ` | l - |k |.` - | | + | ` | + |k | l + | |.` g b | | | | @@ -493,13 +490,12 @@ Feature: Simple Turns When I route I should get | from | to | route | turns | - | e | h | martin,adeline | depart,arrive | - | a | d | adeline,martin,martin | depart,turn slight left, arrive | + | e | h | martin,adeline,adeline | depart,turn straight,arrive | + | a | d | adeline,martin,martin | depart,turn slight left,arrive | | a | l | adeline,adeline,adeline | depart,continue slight right,arrive | | i | h | adeline,adeline | depart,arrive | # https://www.openstreetmap.org/#map=19/37.76471/-122.49639 - @todo Scenario: Turning road Given the node map """ @@ -519,13 +515,12 @@ Feature: Simple Turns | eghbi | secondary | 37 | yes | When I route I should get - | from | to | route | turns | - | d | i | lincoln,37,37 | depart,turn left,arrive | - | d | f | lincoln,lincoln | depart,arrive | + | from | to | route | turns | + | d | i | lincoln,37,37 | depart,turn slight left,arrive | + | d | f | lincoln,lincoln | depart,arrive | # https://www.openstreetmap.org/#map=19/37.63541/-122.48343 # https://www.openstreetmap.org/#map=19/52.47752/13.28864 - @todo Scenario: Road Splitting up Given the node map """ @@ -536,7 +531,7 @@ Feature: Simple Turns a - - - - - - - - - b ` ` - `c + ` c """ @@ -551,6 +546,7 @@ Feature: Simple Turns | a | d | vista,sierra,sierra | depart,turn left,arrive | # https://www.openstreetmap.org/#map=19/52.45191/13.44113 + # check service road handling in `is_similar_turn` @todo Scenario: Road Splitting up at a Driveway Given the node map @@ -578,6 +574,7 @@ Feature: Simple Turns | a | e | britz,, | depart,turn straight,arrive | # https://www.openstreetmap.org/#map=20/37.62997/-122.49246 + # test is mutually exclusive with features/guidance/fork.feature:27 "Scenario: Don't Fork On Single Road" @todo Scenario: Curving road with name-handoff Given the node map @@ -601,11 +598,11 @@ Feature: Simple Turns | lkjihd | residential | clare | yes | When I route I should get - | from | to | route | turns | - | a | g | palm,clare,clare | depart,turn left,arrive | - | g | a | clare,palm,palm | depart,turn right,arrive | - | l | g | clare,clare | depart,arrive | - | l | a | clare,palm,palm | depart,turn left,arrive | + | from | to | route | turns | + | a | g | palm,clare,clare | depart,new name slight left,arrive | + | g | a | clare,palm,palm | depart,turn right,arrive | + | l | g | clare,clare | depart,arrive | + | l | a | clare,palm,palm | depart,turn left,arrive | # https://www.openstreetmap.org/#map=19/37.84291/-122.23681 Scenario: Two roads turning into the same direction @@ -632,6 +629,9 @@ Feature: Simple Turns # https://www.openstreetmap.org/#map=19/37.82815/-122.28733 + # the similarity check considers `bc` as non-similar to `bd` due to a name change "mandela"->"horton" + # this behavior is captured by features/guidance/turn.feature:126 "Scenario: Three Way Intersection" + # and is mutually exclusive with the test expectation @todo Scenario: Turning Secondary Next to Residential Given the node map @@ -718,7 +718,6 @@ Feature: Simple Turns # https://www.openstreetmap.org/#map=19/37.63866/-122.46677 - @todo Scenario: Slightly offset traffic circle Given the node map """ @@ -735,13 +734,14 @@ Feature: Simple Turns | abcdefghib | residential | road | When I route I should get - | from | to | route | turns | - | a | e | road,road | depart,arrive | - | i | a | road,road | depart,arrive | - | d | a | road,road | depart,arrive | + | from | to | route | turns | + | a | e | road,road,road | depart,continue right,arrive | + | i | a | road,road,road | depart,continue right,arrive | + | d | a | road,road,road | depart,continue left,arrive | # https://www.openstreetmap.org/#map=19/37.63829/-122.46345 + # scenario geometry must be updated to catch the OSM map @todo Scenario: Entering a motorway (curved) Given the node map @@ -767,7 +767,6 @@ Feature: Simple Turns # https://www.openstreetmap.org/#map=18/48.99155/8.43520 - @todo Scenario: Passing a motorway link on a trunk road Given the node map """ @@ -784,7 +783,7 @@ Feature: Simple Turns When I route I should get | from | to | route | turns | | a | c | sued,sued | depart,arrive | - | a | d | sued,, | depart,on ramp right,arrive | + | a | d | sued,, | depart,off ramp slight right,arrive | # https://www.openstreetmap.org/#map=19/48.98900/8.43800 @@ -901,6 +900,8 @@ Feature: Simple Turns # https://www.openstreetmap.org/#map=19/48.99810/8.46749 + # the test case depends on geometry: for `da` route both `no_name_change_to_candidate` + # and `compare_road_deviation_is_distinct` are true so `bc` is considered non-similar @todo Scenario: Slight End of Road Given the node map @@ -952,6 +953,7 @@ Feature: Simple Turns # https://www.openstreetmap.org/#map=19/52.56562/13.39109 + # the obviousness check depends on geometry at node `c` @todo Scenario: Dented Straight Given the node map @@ -976,7 +978,6 @@ Feature: Simple Turns # https://www.openstreetmap.org/#map=17/52.57124/13.39892 - @todo Scenario: Primary road curved turn Given the node map """ @@ -996,23 +997,24 @@ Feature: Simple Turns """ And the ways - | nodes | highway | name | oneway | - | abc | primary | schoen | yes | - | cdef | primary | grab | yes | - | klm | primary | schoen | yes | - | ghijk | primary | grab | yes | - | cj | secondary_link | mann | yes | - | jo | secondary | mann | yes | - | pk | secondary | mann | yes | + | nodes | highway | name | oneway | lanes | + | abc | primary | schoen | yes | 2 | + | cdef | primary | grab | yes | 1 | + | klm | primary | schoen | yes | 2 | + | ghijk | primary | grab | yes | 1 | + | cj | secondary_link | mann | yes | 1 | + | jo | secondary | mann | yes | 1 | + | pk | secondary | mann | yes | 1 | When I route I should get - | from | to | route | turns | - | a | f | schoen,grab,grab | depart,turn right,arrive | - | g | m | grab,schoen | depart,arrive | - | a | o | schoen,mann,mann | depart,turn slight left,arrive | + | from | to | route | turns | + | a | f | schoen,grab,grab | depart,turn slight right,arrive | + | g | m | grab,schoen | depart,arrive | + | a | o | schoen,mann,mann | depart,turn straight,arrive | # https://www.openstreetmap.org/#map=18/52.55374/13.41462 + # scenario for internal intersection collapsing @todo Scenario: Turn Links as Straights Given the node map @@ -1047,7 +1049,6 @@ Feature: Simple Turns # https://www.openstreetmap.org/#map=19/52.56934/13.40131 - @todo Scenario: Crossing Segregated before a turn Given the node map """ @@ -1055,8 +1056,8 @@ Feature: Simple Turns f - - - e -- ` ` | _-c a - - -.b -- ` ` - . ` - g` + . ` + g` """ And the ways @@ -1069,15 +1070,14 @@ Feature: Simple Turns | be | secondary_link | no | woll | When I route I should get - | from | to | route | turns | - | g | c | woll,brei,brei | depart,turn right,arrive | - | g | f | woll,scho,scho | depart,turn sharp left,arrive | - | a | c | scho,brei | depart,arrive | - | d | f | brei,scho | depart,arrive | + | from | to | route | turns | + | g | c | woll,brei,brei | depart,turn slight right,arrive | + | g | f | woll,scho,scho | depart,continue sharp left,arrive | + | a | c | scho,brei | depart,arrive | + | d | f | brei,scho | depart,arrive | # https://www.openstreetmap.org/#map=19/52.58072/13.42985 - @todo Scenario: Trunk Turning into Motorway Given the node map """ @@ -1094,9 +1094,9 @@ Feature: Simple Turns | bd | trunk_link | | yes | When I route I should get - | from | to | route | turns | - | a | c | prenz, | depart,arrive | - | a | d | prenz,,, | depart,turn slight right,arrive | + | from | to | route | turns | + | a | c | prenz, | depart,arrive | + | a | d | prenz,, | depart,off ramp slight right,arrive | # https://www.openstreetmap.org/#map=19/52.57800/13.42900 @@ -1118,11 +1118,10 @@ Feature: Simple Turns When I route I should get | from | to | route | turns | - | a | c | dame,pase,pase | depart,new name straight,arrive | + | a | c | dame,pase,pase | depart,new name straight,arrive | | a | e | dame,feuc,feuc | depart,turn slight right,arrive | # https://www.openstreetmap.org/#map=19/52.48468/13.34532 - @todo Scenario: Forking into Tertiary Given the node map """ @@ -1139,8 +1138,8 @@ Feature: Simple Turns When I route I should get | from | to | route | turns | - | a | c | kenny,kenny,kenny | depart,fork slight right,arrive | - | a | d | kenny,domi,domi | depart,fork slight left,arrie | + | a | c | kenny,kenny,kenny | depart,continue straight,arrive | + | a | d | kenny,domi,domi | depart,turn slight left,arrive | # https://www.openstreetmap.org/#map=18/52.56960/13.43815 Scenario: Turn onto classified @@ -1167,7 +1166,6 @@ Feature: Simple Turns # https://www.openstreetmap.org/#map=18/52.50908/13.27312 - @todo Scenario: Merging onto a different street Given the node map """ @@ -1182,9 +1180,9 @@ Feature: Simple Turns | dbc | primary | theo | yes | When I route I should get - | from | to | route | turns | - | a | c | masu,theo | depart,arrive | - | d | c | theo,theo | depart,arrive | + | from | to | route | turns | # | + | a | c | masu,theo,theo | depart,turn straight,arrive | theo is a through street | + | d | c | theo,theo | depart,arrive | | # https://www.openstreetmap.org/#map=18/52.51299/13.28936 Scenario: Lanes override road classes diff --git a/features/guidance/turn-angles.feature b/features/guidance/turn-angles.feature index 809739464..316d973f1 100644 --- a/features/guidance/turn-angles.feature +++ b/features/guidance/turn-angles.feature @@ -848,6 +848,7 @@ Feature: Simple Turns | h,a | Heide,Perle,Perle | depart,turn left,arrive | true:16;true:90 true:195 true:270 true:345;true:90 | #http://www.openstreetmap.org/#map=19/52.53293/13.32956 + # adjusted ways to reflect the case geometry for 2/3/2018 Scenario: Curved Exit from Curved Road Given the node map """ @@ -874,16 +875,16 @@ Feature: Simple Turns And the ways | nodes | name | oneway | lanes | highway | - | abcd | Siemens | no | 5 | secondary | - | defg | Erna | no | 3 | secondary | + | ab | Siemens | no | 5 | secondary | + | bcdefg | Erna | no | 3 | secondary | | dhij | Siemens | no | | residential | When I route I should get - | waypoints | route | turns | - | a,j | Siemens,Siemens,Siemens | depart,continue slight right,arrive | - | a,g | Siemens,Erna | depart,arrive | - | g,j | Erna,Siemens,Siemens | depart,turn left,arrive | - | g,a | Erna,Siemens | depart,arrive | + | waypoints | route | turns | + | a,j | Siemens,Siemens,Siemens | depart,turn slight right,arrive | + | a,g | Siemens,Erna | depart,arrive | + | g,j | Erna,Siemens,Siemens | depart,turn left,arrive | + | g,a | Erna,Siemens | depart,arrive | #http://www.openstreetmap.org/#map=19/52.51303/13.32170 Scenario: Ernst Reuter Platz diff --git a/include/extractor/road_classification.hpp b/include/extractor/road_classification.hpp index ff032069f..da7b2638a 100644 --- a/include/extractor/road_classification.hpp +++ b/include/extractor/road_classification.hpp @@ -135,23 +135,34 @@ inline bool canBeSeenAsFork(const RoadClassification first, const RoadClassifica } // priority groups are road classes that can be categoriesed as somewhat similar -inline std::uint32_t getRoadGroup(const RoadClassification classification) +inline std::uint8_t getRoadGroup(const RoadClassification classification) { - // a list of dividers (inclusive) specifying the end of a class - const auto constexpr num_dividers = 7; - // dividers point one past the entry we want, so motorways will be pre-primary - const constexpr RoadPriorityClass::Enum dividers[num_dividers] = { - RoadPriorityClass::PRIMARY, - RoadPriorityClass::TERTIARY_LINK, - RoadPriorityClass::ALLEY, - RoadPriorityClass::LINK_ROAD, - RoadPriorityClass::UNCLASSIFIED, - RoadPriorityClass::BIKE_PATH, - RoadPriorityClass::CONNECTIVITY + 1}; + const constexpr std::uint8_t groups[RoadPriorityClass::CONNECTIVITY + 1] = { + 0, // MOTORWAY + 0, // MOTORWAY_LINK + 1, // TRUNK + 1, // TRUNK_LINK + 2, // PRIMARY + 2, // PRIMARY_LINK + 2, // SECONDARY + 2, // SECONDARY_LINK + 2, // TERTIARY + 2, // TERTIARY_LINK + 3, // MAIN_RESIDENTIAL + 3, // SIDE_RESIDENTIAL + 3, // ALLEY + 3, // PARKING + 4, // LINK_ROAD + 4, // UNCLASSIFIED + 5, // BIKE_PATH + 6, // FOOT_PATH + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, + 7 // CONNECTIVITY + }; - const auto upper = - std::upper_bound(dividers, dividers + num_dividers, classification.GetPriority()); - return upper - dividers; + BOOST_ASSERT(groups[RoadPriorityClass::CONNECTIVITY] == 7); + + return groups[classification.GetPriority()]; } // LHS road classification is strictly less than RHS, if it belongs to a lower general category diff --git a/include/guidance/intersection_handler.hpp b/include/guidance/intersection_handler.hpp index 30f67995e..30b087bc8 100644 --- a/include/guidance/intersection_handler.hpp +++ b/include/guidance/intersection_handler.hpp @@ -20,8 +20,6 @@ #include -static std::mutex mmm; - namespace osrm { namespace guidance @@ -71,6 +69,14 @@ class IntersectionHandler TurnType::Enum areSameClasses(const EdgeID via_edge, const ConnectedRoad &road) const; + template + inline bool IsDistinctNarrowTurn(const EdgeID via_edge, + const typename IntersectionType::const_iterator candidate, + const IntersectionType &intersection) const; + template + inline bool IsDistinctWideTurn(const EdgeID via_edge, + const typename IntersectionType::const_iterator candidate, + const IntersectionType &intersection) const; template inline bool IsDistinctTurn(const EdgeID via_edge, const typename IntersectionType::const_iterator candidate, @@ -83,14 +89,6 @@ class IntersectionHandler template // works with Intersection and IntersectionView std::size_t findObviousTurn(const EdgeID via_edge, const IntersectionType &intersection) const; - template // works with Intersection and IntersectionView - std::size_t findObviousTurnOld(const EdgeID via_edge, - const IntersectionType &intersection) const; - - template // works with Intersection and IntersectionView - std::size_t findObviousTurnNew(const EdgeID via_edge, - const IntersectionType &intersection) const; - // Obvious turns can still take multiple forms. This function looks at the turn onto a road // candidate when coming from a via_edge and determines the best instruction to emit. // `through_street` indicates if the street turned onto is a through sreet (think mergees and @@ -139,11 +137,48 @@ class IntersectionHandler bool isSameName(const EdgeID source_edge_id, const EdgeID target_edge_id) const; }; +// Implementation + +namespace +{ + +inline bool roadHasLowerClass(const util::NodeBasedEdgeData &from_data, + const util::NodeBasedEdgeData &to_data, + const util::NodeBasedEdgeData &compare_data) +{ + // Check if a road has a strictly lower category + const auto from_classification = from_data.flags.road_classification; + const auto to_classification = to_data.flags.road_classification; + const auto compare_classification = compare_data.flags.road_classification; + const auto from_lanes_number = from_classification.GetNumberOfLanes(); + const auto compare_lanes_number = compare_classification.GetNumberOfLanes(); + + // 1) if the road has strictly less classification than the incoming candidate roads + // and has smaller number of lanes + const auto lanes_number_reduced = + compare_lanes_number > 0 && compare_lanes_number + 1 < from_lanes_number; + if (strictlyLess(compare_classification, from_classification) && + strictlyLess(compare_classification, to_classification) && lanes_number_reduced) + { + return true; + } + + // 2) if a link of the same category + if (isLinkTo(compare_classification, from_classification) && + isLinkTo(compare_classification, to_classification)) + { + return true; + } + + return false; +} +} + template // works with Intersection and IntersectionView inline bool -IntersectionHandler::IsDistinctTurn(const EdgeID via_edge, - const typename IntersectionType::const_iterator candidate, - const IntersectionType &intersection) const +IntersectionHandler::IsDistinctNarrowTurn(const EdgeID via_edge, + const typename IntersectionType::const_iterator candidate, + const IntersectionType &intersection) const { const auto &via_edge_data = node_based_graph.GetEdgeData(via_edge); const auto &via_edge_annotation = @@ -163,300 +198,293 @@ IntersectionHandler::IsDistinctTurn(const EdgeID via_edge, return num_lanes(compare_data) > 0 && num_lanes(compare_data) == num_lanes(via_edge_data); }; - auto const lanes_number_reduced = [&](auto const &compare_data) { - // Check if the lanes number is reduced going from the inbound edge to the compare road - return num_lanes(compare_data) > 0 && - num_lanes(compare_data) + 1 < num_lanes(via_edge_data); - }; + // In case of narrow turns, we apply different criteria than for actual turns. In case of a + // narrow turn, having two choices one of which is forbidden is fine. In case of a end of + // the road turn, having two directions and not being allowed to turn onto one of them isn't + // always as clear - auto const road_has_lower_class = [&](auto const &road) { - // Check if a road has a strictly lower category and can be ignored: + // check if the candidate road changes it's name + auto const no_name_change_to_candidate = + !util::guidance::requiresNameAnnounced(via_edge_annotation.name_id, + candidate_annotation.name_id, + name_table, + street_name_suffix_table); + + // check if there are other narrow turns are not considered passing a low category or simply + // a link of the same type as the potentially obvious turn + auto const is_similar_turn = [&](auto const &road) { + + // 1. Skip the candidate road + if (road.eid == candidate->eid) + { + return false; + } + + // 2. For candidates with narrow turns don't consider not allowed entries + if (candidate_deviation < NARROW_TURN_ANGLE && !road.entry_allowed) + { + return false; + } + + auto const compare_deviation = util::angularDeviation(road.angle, STRAIGHT_ANGLE); auto const &compare_data = node_based_graph.GetEdgeData(road.eid); + auto const &compare_annotation = + node_data_container.GetAnnotation(compare_data.annotation_data); - // 1) if the road has strictly less classification than the incoming candidate roads - // and has smaller number of lanes - if (strictlyLess(compare_data.flags.road_classification, - via_edge_data.flags.road_classification) && - strictlyLess(compare_data.flags.road_classification, - candidate_data.flags.road_classification) && - lanes_number_reduced(compare_data)) + auto const is_lane_fork = + num_lanes(compare_data) > 0 && num_lanes(candidate_data) == num_lanes(compare_data) && + num_lanes(via_edge_data) == num_lanes(candidate_data) + num_lanes(compare_data) && + util::angularDeviation(candidate->angle, road.angle) < GROUP_ANGLE; + + auto const compare_road_deviation_is_distinct = + compare_deviation > DISTINCTION_RATIO * candidate_deviation && + std::abs(compare_deviation - candidate_deviation) > FUZZY_ANGLE_DIFFERENCE / 2.; + + const auto compare_road_deviation_is_slightly_distinct = + compare_deviation > 0.7 * DISTINCTION_RATIO * candidate_deviation; + + // 3. Small side-roads that are marked restricted are not similar to unrestricted roads + if (!via_edge_data.flags.restricted && !candidate_data.flags.restricted && + compare_data.flags.restricted && compare_road_deviation_is_distinct) { - return true; + return false; } - // 2) if a link of the same category - if (isLinkTo(compare_data.flags.road_classification, - via_edge_data.flags.road_classification) && - isLinkTo(compare_data.flags.road_classification, - candidate_data.flags.road_classification)) + // 4. Roundabout exits with larger deviations wrt candidate roads are not similar + if (via_edge_data.flags.roundabout == candidate_data.flags.roundabout && + via_edge_data.flags.roundabout != compare_data.flags.roundabout && + candidate_deviation < compare_deviation) { - return true; + return false; } - return false; - }; + // 5. Similarity check based on name changes + auto const name_changes_to_compare = + util::guidance::requiresNameAnnounced(via_edge_annotation.name_id, + compare_annotation.name_id, + name_table, + street_name_suffix_table); - if (candidate_deviation < GROUP_ANGLE) - { - // In case of narrow turns, we apply different criteria than for actual turns. In case of a - // narrow turn, having two choices one of which is forbidden is fine. In case of a end of - // the road turn, having two directions and not being allowed to turn onto one of them isn't - // always as clear + if ((no_name_change_to_candidate || name_changes_to_compare) && !is_lane_fork && + compare_road_deviation_is_distinct) + { + return false; + } - // check if the candidate road changes it's name - auto const no_name_change_to_candidate = - !util::guidance::requiresNameAnnounced(via_edge_annotation.name_id, - candidate_annotation.name_id, + // 6. If the road has a continuation on the opposite side of intersection + // it can not be similar to the candidate road + auto const opposing_turn = + intersection.FindClosestBearing(util::bearing::reverse(road.perceived_bearing)); + auto const &opposing_data = node_based_graph.GetEdgeData(opposing_turn->eid); + auto const &opposing_annotation = + node_data_container.GetAnnotation(opposing_data.annotation_data); + + auto const four_or_more_ways_intersection = intersection.size() >= 4; + auto const no_name_change_to_compare_from_opposing = + !util::guidance::requiresNameAnnounced(opposing_annotation.name_id, + compare_annotation.name_id, name_table, street_name_suffix_table); - // check if there are other narrow turns are not considered passing a low category or simply - // a link of the same type as the potentially obvious turn - auto const is_similar_turn = [&](auto const &road) { + const auto opposing_to_compare_angle = + util::angularDeviation(road.angle, opposing_turn->angle); - // 1. Skip the candidate road - if (road.eid == candidate->eid) + auto const opposing_to_compare_road_is_distinct = + no_name_change_to_compare_from_opposing || + opposing_to_compare_angle > (STRAIGHT_ANGLE - NARROW_TURN_ANGLE); + + if (four_or_more_ways_intersection && opposing_to_compare_road_is_distinct && + compare_road_deviation_is_distinct) + { + return false; + } + + if (four_or_more_ways_intersection && no_name_change_to_candidate && + name_changes_to_compare && compare_road_deviation_is_slightly_distinct && + no_name_change_to_compare_from_opposing && + opposing_to_compare_angle > STRAIGHT_ANGLE - FUZZY_ANGLE_DIFFERENCE) + { + return false; + } + + if (!four_or_more_ways_intersection && no_name_change_to_candidate && + name_changes_to_compare && compare_road_deviation_is_distinct && !is_lane_fork && + opposing_to_compare_angle > STRAIGHT_ANGLE - GROUP_ANGLE) + { + return false; + } + + // 7. If the inbound road has low priority, consider all distinct roads as non-similar + auto const from_non_main_road_class = + via_edge_data.flags.road_classification.GetPriority() > + extractor::RoadPriorityClass::SECONDARY; + + if (from_non_main_road_class && compare_road_deviation_is_distinct) + { + return false; + } + + // 8. Consider roads non-similar if the candidate road has the same number + // of lanes and has quite small deviation from straightforward direction + // a=a=a + b=b=b + // ` c-c + if (lanes_number_equal(candidate_data) && candidate_deviation < FUZZY_ANGLE_DIFFERENCE && + compare_road_deviation_is_distinct) + { + return false; + } + + // 9. Priority checks + const auto same_priority_to_candidate = + via_edge_data.flags.road_classification.GetPriority() == + candidate_data.flags.road_classification.GetPriority(); + + const auto compare_has_lower_class = + candidate_data.flags.road_classification.GetPriority() < + compare_data.flags.road_classification.GetPriority(); + + const auto compare_has_higher_class = + candidate_data.flags.road_classification.GetPriority() > + compare_data.flags.road_classification.GetPriority() + 4; + + if (same_priority_to_candidate && compare_has_lower_class && no_name_change_to_candidate && + compare_road_deviation_is_slightly_distinct) + { + return false; + } + + if (same_priority_to_candidate && compare_has_higher_class && no_name_change_to_candidate && + compare_road_deviation_is_slightly_distinct) + { + return false; + } + + if (roadHasLowerClass(via_edge_data, candidate_data, compare_data)) + { + return false; + } + + const auto candidate_road_has_same_priority_group = + via_edge_data.flags.road_classification.GetPriority() == + candidate_data.flags.road_classification.GetPriority(); + const auto compare_road_has_lower_priority_group = + extractor::getRoadGroup(via_edge_data.flags.road_classification) < + extractor::getRoadGroup(compare_data.flags.road_classification); + auto const candidate_and_compare_have_different_names = + util::guidance::requiresNameAnnounced(candidate_annotation.name_id, + compare_annotation.name_id, + name_table, + street_name_suffix_table); + + if (candidate_road_has_same_priority_group && compare_road_has_lower_priority_group && + candidate_and_compare_have_different_names && name_changes_to_compare) + { + return false; + } + + if (candidate_road_has_same_priority_group && + compare_data.flags.road_classification.IsLinkClass()) + { + return false; + } + + return true; + }; + + return std::find_if(intersection.begin() + 1, intersection.end(), is_similar_turn) == + intersection.end(); +} + +template +inline bool +IntersectionHandler::IsDistinctWideTurn(const EdgeID via_edge, + const typename IntersectionType::const_iterator candidate, + const IntersectionType &intersection) const +{ + const auto &via_edge_data = node_based_graph.GetEdgeData(via_edge); + const auto &candidate_data = node_based_graph.GetEdgeData(candidate->eid); + auto const candidate_deviation = util::angularDeviation(candidate->angle, STRAIGHT_ANGLE); + + // Deviation is larger than NARROW_TURN_ANGLE0 here for the candidate + // check if there is any turn, that might look just as obvious, even though it might not + // be allowed. Entry-allowed isn't considered a valid distinction criterion here + auto const is_similar_turn = [&](auto const &road) { + + // 1. Skip over our candidate + if (road.eid == candidate->eid) + return false; + + // we do not consider roads of far lesser category to be more obvious + const auto &compare_data = node_based_graph.GetEdgeData(road.eid); + const auto compare_deviation = util::angularDeviation(road.angle, STRAIGHT_ANGLE); + const auto is_compare_straight = + getTurnDirection(road.angle) == DirectionModifier::Straight; + + // 2. Don't consider similarity if a compare road is non-straight and has lower class + if (!is_compare_straight && roadHasLowerClass(via_edge_data, candidate_data, compare_data)) + { + return false; + } + + // 3. If the turn is much stronger, we are also fine (note that we do not have to check + // absolutes, since candidate is at least > NARROW_TURN_ANGLE) + auto const compare_road_deviation_is_distinct = + compare_deviation > DISTINCTION_RATIO * candidate_deviation; + + if (compare_road_deviation_is_distinct) + { + return false; + } + + // 4. If initial and adjusted bearings are quite different then check deviations + // computed in the vicinity of the intersection point based in initial bearings: + // road is not similar to candidate if a road-to-candidate is not a straight direction + // and road has distinctive deviation. + if (util::angularDeviation(intersection[0].initial_bearing, + intersection[0].perceived_bearing) > FUZZY_ANGLE_DIFFERENCE) + { + using osrm::util::bearing::reverse; + using osrm::util::bearing::angleBetween; + using osrm::util::angularDeviation; + + const auto via_edge_initial_bearing = reverse(intersection[0].initial_bearing); + const auto candidate_deviation_initial = angularDeviation( + angleBetween(via_edge_initial_bearing, candidate->initial_bearing), STRAIGHT_ANGLE); + const auto road_deviation_initial = angularDeviation( + angleBetween(via_edge_initial_bearing, road.initial_bearing), STRAIGHT_ANGLE); + const auto road_to_candidate_angle = + angleBetween(reverse(road.initial_bearing), candidate->initial_bearing); + const auto is_straight_road_to_candidate = + getTurnDirection(road_to_candidate_angle) == DirectionModifier::Straight; + + if (!is_straight_road_to_candidate && + road_deviation_initial > DISTINCTION_RATIO * candidate_deviation_initial) { return false; } + } - // 2. For candidates with narrow turns don't consider not allowed entries - if (candidate_deviation < NARROW_TURN_ANGLE && !road.entry_allowed) - { - return false; - } + return true; + }; - auto const compare_deviation = util::angularDeviation(road.angle, STRAIGHT_ANGLE); - auto const &compare_data = node_based_graph.GetEdgeData(road.eid); - auto const &compare_annotation = - node_data_container.GetAnnotation(compare_data.annotation_data); + return std::find_if(intersection.begin() + 1, intersection.end(), is_similar_turn) == + intersection.end(); +} - auto const compare_road_deviation_is_distinct = - compare_deviation > DISTINCTION_RATIO * candidate_deviation && - std::abs(compare_deviation - candidate_deviation) > FUZZY_ANGLE_DIFFERENCE / 2.; +template +inline bool +IntersectionHandler::IsDistinctTurn(const EdgeID via_edge, + const typename IntersectionType::const_iterator candidate, + const IntersectionType &intersection) const +{ + auto const candidate_deviation = util::angularDeviation(candidate->angle, STRAIGHT_ANGLE); - const auto compare_road_deviation_is_slightly_distinct = - compare_deviation > 0.7 * DISTINCTION_RATIO * candidate_deviation; - - // 3. Small side-roads that are marked restricted are not similar to unrestricted roads - if (!via_edge_data.flags.restricted && !candidate_data.flags.restricted && - compare_data.flags.restricted && compare_road_deviation_is_distinct) - { - return false; - } - - // 4. Roundabout exits with larger deviations wrt candidate roads are not similar - if (via_edge_data.flags.roundabout == candidate_data.flags.roundabout && - via_edge_data.flags.roundabout != compare_data.flags.roundabout && - candidate_deviation < compare_deviation) - { - return false; - } - - // 5. Similarity check based on name changes - auto const name_changes_to_compare = - util::guidance::requiresNameAnnounced(via_edge_annotation.name_id, - compare_annotation.name_id, - name_table, - street_name_suffix_table); - - if ((no_name_change_to_candidate || name_changes_to_compare) && - compare_road_deviation_is_distinct) - { - return false; - } - - // 6. If the road has a continuation on the opposite side of intersection - // it can not be similar to the candidate road - auto const opposing_turn = - intersection.FindClosestBearing(util::bearing::reverse(road.perceived_bearing)); - auto const &opposing_data = node_based_graph.GetEdgeData(opposing_turn->eid); - auto const &opposing_annotation = - node_data_container.GetAnnotation(opposing_data.annotation_data); - - auto const four_or_more_ways_intersection = intersection.size() >= 4; - auto const no_name_change_to_compare_from_opposing = - !util::guidance::requiresNameAnnounced(opposing_annotation.name_id, - compare_annotation.name_id, - name_table, - street_name_suffix_table); - - const auto opposing_to_compare_angle = - util::angularDeviation(road.angle, opposing_turn->angle); - - auto const opposing_to_compare_road_is_distinct = - no_name_change_to_compare_from_opposing || - opposing_to_compare_angle > (STRAIGHT_ANGLE - NARROW_TURN_ANGLE); - - if (four_or_more_ways_intersection && opposing_to_compare_road_is_distinct && - compare_road_deviation_is_distinct) - { - return false; - } - - if (four_or_more_ways_intersection && no_name_change_to_candidate && - name_changes_to_compare && compare_road_deviation_is_slightly_distinct && - no_name_change_to_compare_from_opposing && - opposing_to_compare_angle > STRAIGHT_ANGLE - FUZZY_ANGLE_DIFFERENCE) - { - return false; - } - - if (!four_or_more_ways_intersection && no_name_change_to_candidate && - name_changes_to_compare && compare_road_deviation_is_distinct && - opposing_to_compare_angle > STRAIGHT_ANGLE - GROUP_ANGLE) - { - return false; - } - - // 7. If the inbound road has low priority, consider all distinct roads as non-similar - auto const from_non_main_road_class = - via_edge_data.flags.road_classification.GetPriority() > - extractor::RoadPriorityClass::SECONDARY; - - if (from_non_main_road_class && compare_road_deviation_is_distinct) - { - return false; - } - - // 8. Consider roads non-similar if the candidate road has the same number - // of lanes and has quite small deviation from straightforward direction - // a=a=a + b=b=b - // ` c-c - if (lanes_number_equal(candidate_data) && - candidate_deviation < FUZZY_ANGLE_DIFFERENCE && compare_road_deviation_is_distinct) - { - return false; - } - - // 9. Priority checks - const auto same_priority_to_candidate = - via_edge_data.flags.road_classification.GetPriority() == - candidate_data.flags.road_classification.GetPriority(); - - const auto compare_has_lower_class = - candidate_data.flags.road_classification.GetPriority() < - compare_data.flags.road_classification.GetPriority(); - - const auto compare_has_higher_class = - candidate_data.flags.road_classification.GetPriority() > - compare_data.flags.road_classification.GetPriority() + 4; - - if (same_priority_to_candidate && compare_has_lower_class && - no_name_change_to_candidate && compare_road_deviation_is_slightly_distinct) - { - return false; - } - - if (same_priority_to_candidate && compare_has_higher_class && - no_name_change_to_candidate && compare_road_deviation_is_slightly_distinct) - { - return false; - } - - if (road_has_lower_class(road)) - { - return false; - } - - const auto candidate_road_has_same_priority_group = - via_edge_data.flags.road_classification.GetPriority() == - candidate_data.flags.road_classification.GetPriority(); - const auto compare_road_has_lower_priority_group = - extractor::getRoadGroup(via_edge_data.flags.road_classification) < - extractor::getRoadGroup(compare_data.flags.road_classification); - auto const candidate_and_compare_have_different_names = - util::guidance::requiresNameAnnounced(candidate_annotation.name_id, - compare_annotation.name_id, - name_table, - street_name_suffix_table); - - if (candidate_road_has_same_priority_group && compare_road_has_lower_priority_group && - candidate_and_compare_have_different_names) - { - return false; - } - - if (candidate_road_has_same_priority_group && - compare_data.flags.road_classification.IsLinkClass()) - { - return false; - } - - return true; - }; - - return std::find_if(intersection.begin() + 1, intersection.end(), is_similar_turn) == - intersection.end(); - } - else + if (candidate_deviation < GROUP_ANGLE) { - // Deviation is larger than NARROW_TURN_ANGLE0 here for the candidate - // check if there is any turn, that might look just as obvious, even though it might not - // be allowed. Entry-allowed isn't considered a valid distinction criterion here - auto const is_similar_turn = [&](auto const &road) { - - // 1. Skip over our candidate - if (road.eid == candidate->eid) - return false; - - // we do not consider roads of far lesser category to be more obvious - // const auto &compare_data = node_based_graph.GetEdgeData(road.eid); - const auto compare_deviation = util::angularDeviation(road.angle, STRAIGHT_ANGLE); - const auto is_compare_straight = - getTurnDirection(road.angle) == DirectionModifier::Straight; - - // 2. Don't consider similarity if a compare road is non-straight and has lower class - if (!is_compare_straight && road_has_lower_class(road)) - { - return false; - } - - // 3. If the turn is much stronger, we are also fine (note that we do not have to check - // absolutes, since candidate is at least > NARROW_TURN_ANGLE) - auto const compare_road_deviation_is_distinct = - compare_deviation > DISTINCTION_RATIO * candidate_deviation; - - if (compare_road_deviation_is_distinct) - { - return false; - } - - // 4. If initial and adjusted bearings are quite different then check deviations - // computed in the vicinity of the intersection point based in initial bearings: - // road is not similar to candidate if a road-to-candidate is not a straight direction - // and road has distinctive deviation. - if (util::angularDeviation(intersection[0].initial_bearing, - intersection[0].perceived_bearing) > FUZZY_ANGLE_DIFFERENCE) - { - using osrm::util::bearing::reverse; - using osrm::util::bearing::angleBetween; - using osrm::util::angularDeviation; - - const auto via_edge_initial_bearing = reverse(intersection[0].initial_bearing); - const auto candidate_deviation_initial = angularDeviation( - angleBetween(via_edge_initial_bearing, candidate->initial_bearing), - STRAIGHT_ANGLE); - const auto road_deviation_initial = angularDeviation( - angleBetween(via_edge_initial_bearing, road.initial_bearing), STRAIGHT_ANGLE); - const auto road_to_candidate_angle = - angleBetween(reverse(road.initial_bearing), candidate->initial_bearing); - const auto is_straight_road_to_candidate = - getTurnDirection(road_to_candidate_angle) == DirectionModifier::Straight; - - if (!is_straight_road_to_candidate && - road_deviation_initial > DISTINCTION_RATIO * candidate_deviation_initial) - { - return false; - } - } - - return true; - }; - - return std::find_if(intersection.begin() + 1, intersection.end(), is_similar_turn) == - intersection.end(); + return IsDistinctNarrowTurn(via_edge, candidate, intersection); } + + return IsDistinctWideTurn(via_edge, candidate, intersection); } // Impl. @@ -613,14 +641,16 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge, } // Special case handling for roads splitting up, all the same name (exactly the same) - if (intersection.size() == 3 && + const auto all_roads_have_same_name = std::all_of(intersection.begin(), intersection.end(), [ id = via_edge_annotation.name_id, this ](auto const &road) { auto const data_id = node_based_graph.GetEdgeData(road.eid).annotation_data; auto const name_id = node_data_container.GetAnnotation(data_id).name_id; return (name_id != EMPTY_NAMEID) && (name_id == id); - }) && + }); + + if (intersection.size() == 3 && all_roads_have_same_name && intersection.countEnterable() == 1 && // ensure that we do not lookt at a end of the road turn in a segregated intersection (util::angularDeviation(intersection[1].angle, 90) > NARROW_TURN_ANGLE ||