diff --git a/features/car/destination.feature b/features/car/destination.feature index 8f9077742..43e12386c 100644 --- a/features/car/destination.feature +++ b/features/car/destination.feature @@ -135,12 +135,12 @@ Feature: Car - Destination only, no passing through Scenario: Car - Routing through a parking lot tagged access=destination,service Given the node map """ - a----c++++b+++g----h---i - | + + + | - | + + + | - | + + + | + a----c++++b+++g------h---i + | + + + / + | + + + / + | + + + / | d++++e+f / - z---------------y + z--------------y """ And the ways @@ -155,9 +155,10 @@ Feature: Car - Destination only, no passing through | gf | destination | service | When I route I should get - | from | to | route | - | a | i | azyhi,azyhi | - | b | f | be,def,def | + | from | to | route | + | a | i | azyhi,azyhi | + | b | f | be,def,def | + | b | i | cbg,ghi,azyhi,azyhi | Scenario: Car - Disallow snapping to access=private,highway=service Given a grid size of 20 meters diff --git a/features/guidance/collapse.feature b/features/guidance/collapse.feature index a1723805a..ecc6e03aa 100644 --- a/features/guidance/collapse.feature +++ b/features/guidance/collapse.feature @@ -385,7 +385,7 @@ Feature: Collapse | a,g | first,second,second | depart,turn left,arrive | a,b,g | | d,g | first,second,second | depart,turn right,arrive | d,e,g | | g,f | second,first,first | depart,turn right,arrive | g,e,f | - | g,c | second,first,first | depart,turn left,arrive | g,e,c | + | g,c | second,first,first | depart,end of road left,arrive | g,b,c | Scenario: Do not collapse turning roads Given the node map @@ -1010,7 +1010,7 @@ Feature: Collapse | f,j | hohe,hohe | depart,arrive | f,j | | a,t | hohe,a100,a100 | depart,on ramp right,arrive | a,b,t | | f,e | | | | - | q,j | a100,hohe,hohe | depart,turn right,arrive | q,p,j | + | q,j | a100,hohe,hohe | depart,turn right,arrive | q,i,j | | q,e | a100,hohebruecke,hohe | depart,turn left,arrive | q,p,e | Scenario: Forking before a turn (forky) diff --git a/features/guidance/dedicated-turn-roads.feature b/features/guidance/dedicated-turn-roads.feature index 38d30bdc0..03213bd70 100644 --- a/features/guidance/dedicated-turn-roads.feature +++ b/features/guidance/dedicated-turn-roads.feature @@ -687,7 +687,7 @@ Feature: Slipways and Dedicated Turn Lanes When I route I should get | waypoints | route | turns | locations | - | s,f | sabc,ae,dbef,dbef | depart,turn slight right,turn right,arrive | s,a,e,f | + | s,f | sabc,ae,dbef,dbef | depart,fork slight right,turn right,arrive | s,a,e,f | @sliproads Scenario: Traffic Signal on Sliproad diff --git a/features/guidance/low-priority.feature b/features/guidance/low-priority.feature index 38dd315cc..a71007e73 100644 --- a/features/guidance/low-priority.feature +++ b/features/guidance/low-priority.feature @@ -64,9 +64,9 @@ Feature: Exceptions for routing onto low-priority roads | bc | service | | When I route I should get - | waypoints | route | turns | - | e,c | service, | depart,arrive | - | c,e | ,service,service | depart,turn straight,arrive | + | waypoints | route | turns | + | e,c | service, | depart,arrive | + | c,e | ,service | depart,arrive | Scenario: Straight onto low-priority Given the node map diff --git a/features/guidance/merge-segregated-roads.feature b/features/guidance/merge-segregated-roads.feature index be4fad988..8134a2fc0 100644 --- a/features/guidance/merge-segregated-roads.feature +++ b/features/guidance/merge-segregated-roads.feature @@ -384,9 +384,9 @@ Feature: Merge Segregated Roads | cd | right | no | When I route I should get - | waypoints | route | intersections | - | a,d | left,circle,circle,right,right | true:90;false:90 true:120 false:270;true:60 true:180 false:300;true:90 false:240 true:270;true:270 | - | g,d | bottom,circle,right,right | true:0;true:60 false:180 false:300;true:90 false:240 true:270;true:270 | + | waypoints | route | intersections | + | a,d | left,circle,right,right | true:90,false:90 true:120 false:270;true:60 true:180 false:300;true:90 false:240 true:270;true:270 | + | g,d | bottom,circle,right,right | true:0;true:60 false:180 false:300;true:90 false:240 true:270;true:270 | Scenario: Middle Island Given the node map @@ -644,7 +644,7 @@ Feature: Merge Segregated Roads | k,j | marianne,albrecht,luise,luise | depart,turn left,turn left,arrive | | k,d | marianne,schwert,schwert | depart,turn right,arrive | | i,j | luise,luise | depart,arrive | - | i,d | luise,albrecht,schwert,schwert | depart,turn left,turn straight,arrive | + | i,d | luise,albrecht,schwert | depart,turn left,arrive | | i,l | luise,albrecht,marianne,marianne | depart,turn left,turn left,arrive | # https://www.openstreetmap.org/#map=19/52.46339/13.40272 diff --git a/features/guidance/obvious-turn-discovery.feature b/features/guidance/obvious-turn-discovery.feature index b0cfde53f..b2b1b09a4 100644 --- a/features/guidance/obvious-turn-discovery.feature +++ b/features/guidance/obvious-turn-discovery.feature @@ -300,10 +300,11 @@ Feature: Simple Turns When I route I should get | from | to | route | turns | - | a | d | , | depart,turn right,arrive | + | a | d | ,, | depart,turn right,arrive | # https://www.openstreetmap.org/#map=19/37.61256/-122.40371 + @todo Scenario: Turning Road with Offset at Segregated Intersection Given the node map """ diff --git a/features/guidance/turn.feature b/features/guidance/turn.feature index a76774748..450fddd60 100644 --- a/features/guidance/turn.feature +++ b/features/guidance/turn.feature @@ -899,9 +899,9 @@ Feature: Simple Turns """ And the ways - | nodes | highway | name | - | abc | primary | road | - | bd | residential | in | + | nodes | highway | name | lanes | + | abc | primary | road | 3 | + | bd | residential | in | 1 | When I route I should get | waypoints | turns | route | @@ -1434,21 +1434,21 @@ Feature: Simple Turns Scenario: Turn for roads with no name, ref changes Given the node map """ - x - . - . - d - . . - . . - . . - e. . t . c . p. .f - . . - . . - . . - b - . - . - a + x + . + . + d + . . + . . + . . + e. . t . c . p. .f + . . + . . + . . + b + . + . + a """ And the ways @@ -1460,6 +1460,6 @@ Feature: Simple Turns | etcpf | primary | B 1 | | no | When I route I should get - | waypoints | route | turns | - | e,x | ,,, | depart,turn sharp left,turn right,arrive | - | f,a | ,, | depart,turn left,arrive | + | waypoints | route | turns | + | e,x | ,, | depart,turn left,arrive | + | f,a | ,, | depart,turn left,arrive | diff --git a/features/testbot/bearing_param.feature b/features/testbot/bearing_param.feature index 1d286ca49..ec6708a70 100644 --- a/features/testbot/bearing_param.feature +++ b/features/testbot/bearing_param.feature @@ -108,12 +108,12 @@ Feature: Bearing parameter | ha | yes | ring | When I route I should get - | from | to | bearings | route | bearing | - | 0 | q | 0 90 | ia,ring,ring,ring,ring | 0->0,0->90,180->270,270->0,90->0 | - | 0 | a | 45 90 | jb,ring,ring,ring,ring | 0->45,45->180,180->270,270->0,90->0 | - | 0 | q | 90 90 | kc,ring,ring,ring | 0->90,90->180,270->0,90->0 | - | 0 | a | 135 90 | ld,ring,ring,ring | 0->135,135->270,270->0,90->0 | - | 0 | a | 180 90 | me,ring,ring,ring | 0->180,180->270,270->0,90->0 | - | 0 | a | 225 90 | nf,ring,ring | 0->225,225->0,90->0 | - | 0 | a | 270 90 | og,ring,ring | 0->270,270->0,90->0 | - | 0 | a | 315 90 | ph,ring,ring | 0->315,315->90,90->0 | + | from | to | bearings | route | bearing | + | 0 | q | 0 90 | ia,ring,ring,ring,ring,ring | 0->0,0->90,180->270,270->0,0->90,90->0 | + | 0 | a | 45 90 | jb,ring,ring,ring,ring,ring | 0->45,45->180,180->270,270->0,0->90,90->0 | + | 0 | q | 90 90 | kc,ring,ring,ring,ring | 0->90,90->180,270->0,0->90,90->0 | + | 0 | a | 135 90 | ld,ring,ring,ring,ring | 0->135,135->270,270->0,0->90,90->0 | + | 0 | a | 180 90 | me,ring,ring,ring,ring | 0->180,180->270,270->0,0->90,90->0 | + | 0 | a | 225 90 | nf,ring,ring,ring | 0->225,225->0,0->90,90->0 | + | 0 | a | 270 90 | og,ring,ring,ring | 0->270,270->0,0->90,90->0 | + | 0 | a | 315 90 | ph,ring,ring | 0->315,315->90,90->0 | diff --git a/include/extractor/road_classification.hpp b/include/extractor/road_classification.hpp index 5aca13e1c..ff032069f 100644 --- a/include/extractor/road_classification.hpp +++ b/include/extractor/road_classification.hpp @@ -154,16 +154,17 @@ inline std::uint32_t getRoadGroup(const RoadClassification classification) return upper - dividers; } -// a road classification is strictly less, if it belongs to a lower general category of roads. E.g. -// normal city roads are strictly less of a priority than a motorway and alleys are strictly less -// than inner-city roads +// LHS road classification is strictly less than RHS, if it belongs to a lower general category +// of roads. E.g. normal city roads are strictly less of a priority than a motorway and alleys +// are strictly less than inner-city roads. inline bool strictlyLess(const RoadClassification lhs, const RoadClassification rhs) { - const auto lhs_class = getRoadGroup(lhs); - const auto rhs_class = getRoadGroup(rhs); - // different class, not neighbors - return lhs_class > rhs_class && - ((lhs.GetPriority() - rhs.GetPriority() > 4) || lhs.IsLowPriorityRoadClass()); + if (!lhs.IsLowPriorityRoadClass() && rhs.IsLowPriorityRoadClass()) + return false; + if (lhs.IsLowPriorityRoadClass() && !rhs.IsLowPriorityRoadClass()) + return true; + + return getRoadGroup(lhs) > getRoadGroup(rhs); } // check whether a link class is the fitting link class to a road diff --git a/include/guidance/intersection_handler.hpp b/include/guidance/intersection_handler.hpp index 642a97e4c..30f67995e 100644 --- a/include/guidance/intersection_handler.hpp +++ b/include/guidance/intersection_handler.hpp @@ -7,6 +7,7 @@ #include "guidance/constants.hpp" #include "guidance/intersection.hpp" +#include "util/assert.hpp" #include "util/coordinate_calculation.hpp" #include "util/guidance/name_announcements.hpp" #include "util/name_table.hpp" @@ -19,6 +20,8 @@ #include +static std::mutex mmm; + namespace osrm { namespace guidance @@ -68,16 +71,11 @@ class IntersectionHandler TurnType::Enum areSameClasses(const EdgeID via_edge, const ConnectedRoad &road) const; - template // works with Intersection and IntersectionView - inline bool IsDistinctTurn(const std::size_t index, - const EdgeID via_edge, + template + inline bool IsDistinctTurn(const EdgeID via_edge, + const typename IntersectionType::const_iterator candidate, const IntersectionType &intersection) const; - template // works with Intersection and IntersectionView - inline bool IsDistinctContinue(const std::size_t index, - const EdgeID via_edge, - const IntersectionType &intersection) const; - // Find the most obvious turn to follow. The function returns an index into the intersection // determining whether there is a road that can be seen as obvious turn in the presence of many // other possible turns. The function will consider road categories and other inputs like the @@ -141,69 +139,56 @@ class IntersectionHandler bool isSameName(const EdgeID source_edge_id, const EdgeID target_edge_id) const; }; -// Impl. -using osrm::extractor::getRoadGroup; - template // works with Intersection and IntersectionView -inline bool IntersectionHandler::IsDistinctTurn(const std::size_t index, - const EdgeID via_edge, - const IntersectionType &intersection) const +inline bool +IntersectionHandler::IsDistinctTurn(const EdgeID via_edge, + const typename IntersectionType::const_iterator candidate, + const IntersectionType &intersection) const { - // for comparing road categories const auto &via_edge_data = node_based_graph.GetEdgeData(via_edge); - const auto &candidate = intersection[index]; - const auto &candidate_data = node_based_graph.GetEdgeData(candidate.eid); + const auto &via_edge_annotation = + node_data_container.GetAnnotation(via_edge_data.annotation_data); + + const auto &candidate_data = node_based_graph.GetEdgeData(candidate->eid); + const auto &candidate_annotation = + node_data_container.GetAnnotation(candidate_data.annotation_data); + auto const candidate_deviation = util::angularDeviation(candidate->angle, STRAIGHT_ANGLE); auto const num_lanes = [](auto const &data) { return data.flags.road_classification.GetNumberOfLanes(); }; - auto const override_class_by_lanes = [&](auto const &compare_data) { - // sometimes roads of same size are tagged strangely within a neighborhood, combining - // primary roads with residential roads. If the road with can be deducted from lanes, we - // can override such a classification - if (num_lanes(compare_data) > 0 && num_lanes(via_edge_data) > 0) - { - // check if via-edge has more than one additional lane, relative to the compare data - if (num_lanes(via_edge_data) - num_lanes(compare_data) > 1) - return true; - } - return false; + auto const lanes_number_equal = [&](auto const &compare_data) { + // Check if the lanes number is the same going from the inbound edge to the compare road + return num_lanes(compare_data) > 0 && num_lanes(compare_data) == num_lanes(via_edge_data); }; - // check if a road is distinct to the obvious turn candidate in its road class. This is the case - // only if we pass by a lower road category class or a link to the same category - auto const distinct_by_class = [&](auto const &road) { + 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); + }; + + auto const road_has_lower_class = [&](auto const &road) { + // Check if a road has a strictly lower category and can be ignored: auto const &compare_data = node_based_graph.GetEdgeData(road.eid); - // passing a road of a stricly lower category (e.g. residential driving past driveway, - // primary road passing a residential road) but also exiting a freeway onto a primary in the - // presence of an alley + // 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) && - override_class_by_lanes(compare_data)) + lanes_number_reduced(compare_data)) { return true; } - // passing by a link of the same category + // 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)) - return true; - - // staying on the same road class, encountering a road that is a severe change in class - // (residential-> motorway_link) is considered a fair distinction - if (compare_data.flags.road_classification.IsLinkClass() && - (via_edge_data.flags.road_classification.GetPriority() == - candidate_data.flags.road_classification.GetPriority()) && - (std::abs(static_cast(getRoadGroup(via_edge_data.flags.road_classification)) - - static_cast(getRoadGroup(compare_data.flags.road_classification))) > - 4) && - override_class_by_lanes(compare_data)) { return true; } @@ -211,82 +196,34 @@ inline bool IntersectionHandler::IsDistinctTurn(const std::size_t index, return false; }; - // 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 candidate_deviation = util::angularDeviation(candidate.angle, STRAIGHT_ANGLE); - - const auto &via_edge_annotation = - node_data_container.GetAnnotation(via_edge_data.annotation_data); - const auto &candidate_annotation = - node_data_container.GetAnnotation(candidate_data.annotation_data); - - const auto constexpr max_narrow_deviation = GROUP_ANGLE; - // on cases where the candidate deviation is in a narrow range, we can consider the deviaiton of - // other turns as a distinction criteria - // - // c - // * - // * - // b - d - // | - // a - // for example can be considered obvious as goig straight, while - // - // c - // d * - // * * - // b - // | - // a - // should err on the side of caution (when only comparing deviations) - - if (candidate_deviation <= max_narrow_deviation) + if (candidate_deviation < GROUP_ANGLE) { - // check if the candidate changes it's name - auto const candidate_changes_name = - util::guidance::requiresNameAnnounced(via_edge_annotation.name_id, - candidate_annotation.name_id, - name_table, - street_name_suffix_table); + // 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 + + // 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) { - // skip over our candidate - if (road.eid == candidate.eid) - return false; - // since we have a narrow turn, we only care for roads allowing entry - if (candidate_deviation < NARROW_TURN_ANGLE && !road.entry_allowed) + // 1. Skip the candidate road + if (road.eid == candidate->eid) { return false; } - // detect link roads in segregated intersections - if (!road.entry_allowed && (intersection.size() == 5) && - (std::count_if(intersection.begin(), intersection.end(), [](auto const &road) { - return road.entry_allowed; - }) <= 2)) + // 2. For candidates with narrow turns don't consider not allowed entries + if (candidate_deviation < NARROW_TURN_ANGLE && !road.entry_allowed) { - // if we are on a link road and all other turns form a 4 way intersection, the - // angular differences of all other turns need to be near 90 degrees - bool all_close_to_90 = true; - for (std::size_t i = 1; i < 3; ++i) - { - auto const deviation = - util::angularDeviation(intersection[i].angle, intersection[i + 1].angle); - if (deviation < 75 || deviation > 105) - { - all_close_to_90 = false; - break; - } - } - if (all_close_to_90) - { - return false; - } + return false; } auto const compare_deviation = util::angularDeviation(road.angle, STRAIGHT_ANGLE); @@ -294,232 +231,154 @@ inline bool IntersectionHandler::IsDistinctTurn(const std::size_t index, auto const &compare_annotation = node_data_container.GetAnnotation(compare_data.annotation_data); - // in the states, many small side-roads are marked restricted. We could consider them - // driveways. Passing by one of these should always be obvious - if (candidate_deviation < NARROW_TURN_ANGLE && - (compare_deviation > 1.5 * candidate_deviation) && compare_data.flags.restricted && - !via_edge_data.flags.restricted && !candidate_data.flags.restricted) + 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 false; } - // if we see a roundabout that is a larger turn, we do not consider it similar. This is - // related to throughabouts which often are slightly curved on exits: - // | - // - a d - - // \` e f ` / - // b - - c - if (compare_data.flags.roundabout != via_edge_data.flags.roundabout && - via_edge_data.flags.roundabout == candidate_data.flags.roundabout && + // 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; + } - // to find whether a continuing road is turning, we need to check if it is an actual - // turn, a segregated intersection - - auto const opposing_turn = - intersection.FindClosestBearing(util::bearing::reverse(road.perceived_bearing)); - auto const opposing_data = node_based_graph.GetEdgeData(opposing_turn->eid); - // Check for a situation like: - // - // a a - // a a - // a a + b b + b b - // c ac - // c a c - // - // opposed to - // - // a - // a - // a a + b b - // a - // a - auto const name_changes_onto_compare = + // 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); - auto const opposing_name = - node_data_container.GetAnnotation(opposing_data.annotation_data).name_id; - auto const name_changes_onto_compare_from_opposing = - util::guidance::requiresNameAnnounced(opposing_name, + + 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); - // check if the continuing road takes a turn, and we are turning off it. This is - // required, sicne we could end up announcing `follow X for 2 miles` and if `X` turns, - // we would be inclined to do the turn as well, if it isn't crazy (like a sharp turn) - auto const continue_turns = (via_edge_annotation.name_id != EMPTY_NAMEID) && - !name_changes_onto_compare && - (util::angularDeviation(road.angle, opposing_turn->angle) < - (STRAIGHT_ANGLE - NARROW_TURN_ANGLE) && - name_changes_onto_compare_from_opposing) && - util::angularDeviation(road.angle, 0) > NARROW_TURN_ANGLE; - - auto const continuing_road_takes_a_turn = candidate_changes_name && continue_turns; - // at least a relative and a maximum difference, if the road name does not turn. - // Since we can announce `stay on X for 2 miles, we need to ensure that we announce - // turns off it (even if straight). Otherwise people might follow X further than they - // should - // For roads splitting with the same name, we ask for a larger difference. - auto const minimum_angle_difference = FUZZY_ANGLE_DIFFERENCE; - /* - (via_edge_annotation.name_id != EMPTY_NAMEID && !candidate_changes_name && - !name_changes_onto_compare) - ? NARROW_TURN_ANGLE - : FUZZY_ANGLE_DIFFERENCE; - */ - - // if a turn angle isn't remotely forward, we don't consider a deviation to be distinct - // auto const both_turns_go_into_same_direction = - // (candidate.angle >= STRAIGHT_ANGLE) == - // (road.angle >= STRAIGHT_ANGLE); // are both turns to the left? - auto const roads_deviation_is_distinct = - compare_deviation / std::max(0.1, candidate_deviation) > DISTINCTION_RATIO && - std::abs(compare_deviation - candidate_deviation) > minimum_angle_difference; - - auto const continue_is_main_class = - via_edge_data.flags.road_classification.GetPriority() <= - extractor::RoadPriorityClass::SECONDARY; - if ((!continuing_road_takes_a_turn || !continue_is_main_class) && - roads_deviation_is_distinct) + if (candidate_road_has_same_priority_group && compare_road_has_lower_priority_group && + candidate_and_compare_have_different_names) { return false; } - // in case of slight turns, there can be exits that are also very narrow. If they are on - // a new lane though, we accept smaller distinction angles - // - // a - - - b - - - - c - // ` ` ` `d - // - // A narrow exit lane can be present, but still be distinct from the road - if (num_lanes(via_edge_data) > 0 && - num_lanes(candidate_data) == num_lanes(via_edge_data)) - { - if (compare_deviation > candidate_deviation && - candidate_deviation <= FUZZY_ANGLE_DIFFERENCE && - (compare_deviation - candidate_deviation) > 0.5 * FUZZY_ANGLE_DIFFERENCE) - { - // very slight angle going straight on the exact same number of lanes as coming - // in, one turn branching off in a slight angle with additional lanes - return false; - } - } - - // when crossing an intersection of a similar road category, lower deviations can also - // make sense - // crossing a compare road - auto const crossing_compare = - !name_changes_onto_compare_from_opposing && - (util::angularDeviation(opposing_turn->angle, road.angle) > - STRAIGHT_ANGLE - FUZZY_ANGLE_DIFFERENCE) && - name_changes_onto_compare; - - // in case of a continuing road of higher road class, we accept quite a bit loweer - // distinction - auto const compare_has_lower_class = - (candidate_data.flags.road_classification.GetPriority() == - via_edge_data.flags.road_classification.GetPriority()) && - (candidate_data.flags.road_classification.GetPriority() < - compare_data.flags.road_classification.GetPriority()); - - // for something like a tertiary link, we skip over tertiary, secondary_link, secondary, - // primary_link and require at least a primary road - auto const compare_has_way_higher_class = - (candidate_data.flags.road_classification.GetPriority() == - via_edge_data.flags.road_classification.GetPriority()) && - (std::abs(static_cast( - candidate_data.flags.road_classification.GetPriority()) - - static_cast( - compare_data.flags.road_classification.GetPriority())) > 4); - - if (!candidate_changes_name && !continuing_road_takes_a_turn && - (compare_has_lower_class || compare_has_way_higher_class || crossing_compare) && - compare_deviation / std::max(0.1, candidate_deviation) > 0.7 * DISTINCTION_RATIO) - { - return false; - } - - // since the angle and allowed match, we compare road categories. Passing a low priority - // road allows us to consider it non obvious - if (distinct_by_class(road)) - { - return false; - } - - // switching the general road class within a turn is not a likely maneuver. We consider - // a turn distinct enough (given it's straight/narrow continue), if it's road class - // differs from other turns (and is of a lesser category) - if ((getRoadGroup(via_edge_data.flags.road_classification) != - getRoadGroup(compare_data.flags.road_classification)) && - (via_edge_data.flags.road_classification.GetPriority() == - candidate_data.flags.road_classification.GetPriority())) - return false; - - return true; - }; - - auto const itr = - std::find_if(intersection.begin() + 1, intersection.end(), is_similar_turn); - return itr == intersection.end(); - } - else - { - // 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) { - // 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); - /* - if (strictlyLess(compare_data.flags.road_classification, - candidate_data.flags.road_classification)) - { - std::cout << "Road class is strictly less" << std::endl; - return false; - } - */ - - // if the class is just not on the same level - if (distinct_by_class(road) && !override_class_by_lanes(compare_data)) - { - return false; - } - - // just as above, switching the general road class within a turn is not a likely - // maneuver. We consider - // a turn distinct enough (given it's straight/narrow continue), if it's road class - // differs from other turns. However, the difference in angles between the two needs to - // be reasonable as well. When coming down to tertiary and less, road groups are more or - // less random - if (util::angularDeviation(road.angle, candidate.angle) < 100 && - via_edge_data.flags.road_classification.GetPriority() <= - extractor::RoadPriorityClass::SECONDARY && - ((getRoadGroup(via_edge_data.flags.road_classification) != - getRoadGroup(compare_data.flags.road_classification)) && - (via_edge_data.flags.road_classification.GetPriority() == - candidate_data.flags.road_classification.GetPriority())) && - !override_class_by_lanes(compare_data) && - (via_edge_data.flags.road_classification.GetPriority() != - extractor::RoadPriorityClass::UNCLASSIFIED) && - (compare_data.flags.road_classification.GetPriority() != - extractor::RoadPriorityClass::UNCLASSIFIED)) - { - return false; - } - - // 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 - const auto compare_deviation = util::angularDeviation(road.angle, STRAIGHT_ANGLE); - if (compare_deviation / candidate_deviation > DISTINCTION_RATIO) + if (candidate_road_has_same_priority_group && + compare_data.flags.road_classification.IsLinkClass()) { return false; } @@ -530,28 +389,74 @@ inline bool IntersectionHandler::IsDistinctTurn(const std::size_t index, return std::find_if(intersection.begin() + 1, intersection.end(), is_similar_turn) == intersection.end(); } -} + else + { + // 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) { -template // works with Intersection and IntersectionView -inline bool IntersectionHandler::IsDistinctContinue(const std::size_t index, - const EdgeID via_edge, - const IntersectionType &intersection) const -{ - // if its good enough for a turn, it's good enough for a continue - if (IsDistinctTurn(index, via_edge, intersection)) - return true; + // 1. Skip over our candidate + if (road.eid == candidate->eid) + return false; - auto const in_classification = node_based_graph.GetEdgeData(via_edge).flags.road_classification; - auto const continue_classification = - node_based_graph.GetEdgeData(intersection[index].eid).flags.road_classification; + // 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; - // nearly straight on the same road type - if (in_classification.GetPriority() == continue_classification.GetPriority() && - util::angularDeviation(intersection[index].angle, STRAIGHT_ANGLE) < - MAXIMAL_ALLOWED_NO_TURN_DEVIATION) - return true; + // 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; + } - 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(); + } } // Impl. @@ -559,23 +464,6 @@ template // works with Intersection and Intersection std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge, const IntersectionType &intersection) const { - auto obvious_old = findObviousTurnOld(via_edge, intersection); - auto obvious_new = findObviousTurnNew(via_edge, intersection); - // if (obvious_new != obvious_old) - // { - // std::cout << "via_edge==" << via_edge << " old " << obvious_old << " new " << - // obvious_new - // << "\n"; - // BOOST_ASSERT(false); - // } - (void)obvious_old; - return obvious_new; -} - -template // works with Intersection and IntersectionView -std::size_t IntersectionHandler::findObviousTurnNew(const EdgeID via_edge, - const IntersectionType &intersection) const -{ // no obvious road if (intersection.size() == 1) @@ -601,7 +489,8 @@ std::size_t IntersectionHandler::findObviousTurnNew(const EdgeID via_edge, !via_edge_data.flags.road_classification.IsLowPriorityRoadClass()) return true; - // and we cannot yloose it (roads loosing their name will be handled after this check here) + // and we cannot yloose it (roads loosing their name will be handled after this check + // here) auto const &road_data = node_based_graph.GetEdgeData(road.eid); const auto &road_annotation = node_data_container.GetAnnotation(road_data.annotation_data); if (road_annotation.name_id == EMPTY_NAMEID && @@ -613,7 +502,8 @@ std::size_t IntersectionHandler::findObviousTurnNew(const EdgeID via_edge, (road_annotation.name_id == EMPTY_NAMEID)) return true; - // the priority can only stay the same or increase. We don't consider a primary->residential + // the priority can only stay the same or increase. We don't consider a + // primary->residential // or residential->service as a continuing road if (strictlyLess(road_data.flags.road_classification, via_edge_data.flags.road_classification)) @@ -643,21 +533,13 @@ std::size_t IntersectionHandler::findObviousTurnNew(const EdgeID via_edge, if (from_data.flags.roundabout != to_data.flags.roundabout) return 0; - auto const from_mode = - node_data_container.GetAnnotation(from_data.annotation_data).travel_mode; - auto const to_mode = node_data_container.GetAnnotation(to_data.annotation_data).travel_mode; - - if (from_mode == to_mode) - return std::distance(intersection.begin(), iterator); - else - return 0; + return std::distance(intersection.begin(), iterator); }; - // in case the continuing road is distinct, we prefer continuing on the current road. Only if - // continue does not exist or we are not distinct, we look for other possible candidates + // in case the continuing road is distinct, we prefer continuing on the current road. + // Only if continue does not exist or we are not distinct, we look for other possible candidates if (road_continues_itr != intersection.end() && - IsDistinctContinue( - std::distance(intersection.begin(), road_continues_itr), via_edge, intersection)) + IsDistinctTurn(via_edge, road_continues_itr, intersection)) { return to_index_if_valid(road_continues_itr); } @@ -691,17 +573,15 @@ std::size_t IntersectionHandler::findObviousTurnNew(const EdgeID via_edge, intersection.findClosestTurn(STRAIGHT_ANGLE, valid_of_higher_or_same_category); if (straightmost_turn_itr != intersection.end() && - IsDistinctTurn( - std::distance(intersection.begin(), straightmost_turn_itr), via_edge, intersection)) + IsDistinctTurn(via_edge, straightmost_turn_itr, intersection)) { return to_index_if_valid(straightmost_turn_itr); } - auto const valid_turn = [&](auto const &road) { return !road.entry_allowed; }; - // we cannot find a turn of same or higher priority, so we check if any straightmost turn could // be obvious. We only consider somewhat narrow turns for these cases though - auto const straightmost_valid = intersection.findClosestTurn(STRAIGHT_ANGLE, valid_turn); + auto const straightmost_valid = intersection.findClosestTurn( + STRAIGHT_ANGLE, [&](auto const &road) { return !road.entry_allowed; }); // no valid turns if (straightmost_valid == intersection.end()) return 0; @@ -713,19 +593,17 @@ std::size_t IntersectionHandler::findObviousTurnNew(const EdgeID via_edge, (non_sharp_turns == 1); if ((straightmost_valid != straightmost_turn_itr) && - (straightmost_valid != intersection.end()) && (util::angularDeviation(STRAIGHT_ANGLE, straightmost_valid->angle) <= GROUP_ANGLE || straight_is_only_non_sharp) && !node_based_graph.GetEdgeData(straightmost_valid->eid) .flags.road_classification.IsLowPriorityRoadClass() && - IsDistinctTurn( - std::distance(intersection.begin(), straightmost_valid), via_edge, intersection)) + IsDistinctTurn(via_edge, straightmost_valid, intersection)) { return to_index_if_valid(straightmost_valid); } - // special case handling for motorways, for which nearly narrow / only allowed turns are always - // obvious + // special case handling for motorways, for which nearly narrow / only allowed turns are + // always obvious if (node_based_graph.GetEdgeData(straightmost_valid->eid) .flags.road_classification.IsMotorwayClass() && util::angularDeviation(straightmost_valid->angle, STRAIGHT_ANGLE) <= GROUP_ANGLE && @@ -754,494 +632,6 @@ std::size_t IntersectionHandler::findObviousTurnNew(const EdgeID via_edge, return 0; } -template // works with Intersection and IntersectionView -std::size_t IntersectionHandler::findObviousTurnOld(const EdgeID via_edge, - const IntersectionType &intersection) const -{ - using Road = typename IntersectionType::value_type; - using osrm::util::angularDeviation; - - // no obvious road - if (intersection.size() == 1) - return 0; - - // a single non u-turn is obvious - if (intersection.size() == 2) - return 1; - - const auto &in_way_edge = node_based_graph.GetEdgeData(via_edge); - const auto &in_way_data = node_data_container.GetAnnotation(in_way_edge.annotation_data); - - // the strategy for picking the most obvious turn involves deciding between - // an overall best candidate and a best candidate that shares the same name - // as the in road, i.e. a continue road - std::size_t best_option = 0; - double best_option_deviation = 180; - std::size_t best_continue = 0; - double best_continue_deviation = 180; - - /* helper functions */ - const auto IsContinueRoad = [&](const extractor::NodeBasedEdgeAnnotation &way_data) { - return !util::guidance::requiresNameAnnounced( - in_way_data.name_id, way_data.name_id, name_table, street_name_suffix_table); - }; - auto sameOrHigherPriority = [&](const auto &way_data) { - return way_data.flags.road_classification.GetPriority() <= - in_way_edge.flags.road_classification.GetPriority(); - }; - auto IsLowPriority = [](const auto &way_data) { - return way_data.flags.road_classification.IsLowPriorityRoadClass(); - }; - // These two Compare functions are used for sifting out best option and continue - // candidates by evaluating all the ways in an intersection by what they share - // with the in way. Ideal candidates are of similar road class with the in way - // and are require relatively straight turns. - const auto RoadCompare = [&](const auto &lhs, const auto &rhs) { - const auto &lhs_edge = node_based_graph.GetEdgeData(lhs.eid); - const auto &rhs_edge = node_based_graph.GetEdgeData(rhs.eid); - const auto lhs_deviation = angularDeviation(lhs.angle, STRAIGHT_ANGLE); - const auto rhs_deviation = angularDeviation(rhs.angle, STRAIGHT_ANGLE); - - const bool rhs_same_classification = - rhs_edge.flags.road_classification == in_way_edge.flags.road_classification; - const bool lhs_same_classification = - lhs_edge.flags.road_classification == in_way_edge.flags.road_classification; - const bool rhs_same_or_higher_priority = sameOrHigherPriority(rhs_edge); - const bool rhs_low_priority = IsLowPriority(rhs_edge); - const bool lhs_same_or_higher_priority = sameOrHigherPriority(lhs_edge); - const bool lhs_low_priority = IsLowPriority(lhs_edge); - auto left_tie = std::tie(lhs.entry_allowed, - lhs_same_or_higher_priority, - rhs_low_priority, - rhs_deviation, - lhs_same_classification); - auto right_tie = std::tie(rhs.entry_allowed, - rhs_same_or_higher_priority, - lhs_low_priority, - lhs_deviation, - rhs_same_classification); - return left_tie > right_tie; - }; - const auto RoadCompareSameName = [&](const auto &lhs, const auto &rhs) { - const auto &lhs_data = node_data_container.GetAnnotation( - node_based_graph.GetEdgeData(lhs.eid).annotation_data); - const auto &rhs_data = node_data_container.GetAnnotation( - node_based_graph.GetEdgeData(rhs.eid).annotation_data); - const auto lhs_continues = IsContinueRoad(lhs_data); - const auto rhs_continues = IsContinueRoad(rhs_data); - const auto left_tie = std::tie(lhs.entry_allowed, lhs_continues); - const auto right_tie = std::tie(rhs.entry_allowed, rhs_continues); - return left_tie > right_tie || (left_tie == right_tie && RoadCompare(lhs, rhs)); - }; - - auto best_option_it = std::min_element(begin(intersection), end(intersection), RoadCompare); - - // min element should only return end() when vector is empty - BOOST_ASSERT(best_option_it != end(intersection)); - - best_option = std::distance(begin(intersection), best_option_it); - best_option_deviation = angularDeviation(intersection[best_option].angle, STRAIGHT_ANGLE); - const auto &best_option_edge = node_based_graph.GetEdgeData(intersection[best_option].eid); - const auto &best_option_data = - node_data_container.GetAnnotation(best_option_edge.annotation_data); - - // Unless the in way is also low priority, it is generally undesirable to - // indicate that a low priority road is obvious - if (IsLowPriority(best_option_edge) && - best_option_edge.flags.road_classification != in_way_edge.flags.road_classification) - { - best_option = 0; - best_option_deviation = 180; - } - - // double check if the way with the lowest deviation from straight is still be better choice - const auto straightest = intersection.findClosestTurn(STRAIGHT_ANGLE); - if (straightest != best_option_it) - { - const auto &straightest_edge = node_based_graph.GetEdgeData(straightest->eid); - double straightest_data_deviation = angularDeviation(straightest->angle, STRAIGHT_ANGLE); - const auto deviation_diff = - std::abs(best_option_deviation - straightest_data_deviation) > FUZZY_ANGLE_DIFFERENCE; - const auto not_ramp_class = !straightest_edge.flags.road_classification.IsRampClass(); - const auto not_link_class = !straightest_edge.flags.road_classification.IsLinkClass(); - if (deviation_diff && !IsLowPriority(straightest_edge) && not_ramp_class && - not_link_class && !IsContinueRoad(best_option_data)) - { - best_option = std::distance(begin(intersection), straightest); - best_option_deviation = - angularDeviation(intersection[best_option].angle, STRAIGHT_ANGLE); - } - } - - // No non-low priority roads? Declare no obvious turn - if (best_option == 0) - return 0; - - auto best_continue_it = - std::min_element(begin(intersection), end(intersection), RoadCompareSameName); - const auto best_continue_edge = node_based_graph.GetEdgeData(best_continue_it->eid); - const auto best_continue_data = - node_data_container.GetAnnotation(best_continue_edge.annotation_data); - if (IsContinueRoad(best_continue_data) || - (in_way_data.name_id == EMPTY_NAMEID && best_continue_data.name_id == EMPTY_NAMEID)) - { - best_continue = std::distance(begin(intersection), best_continue_it); - best_continue_deviation = - angularDeviation(intersection[best_continue].angle, STRAIGHT_ANGLE); - } - - // if the best angle is going straight but the road is turning, declare no obvious turn - if (0 != best_continue && best_option != best_continue && - best_option_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION && - best_continue_edge.flags.road_classification == best_option_edge.flags.road_classification) - { - return 0; - } - - // get a count of number of ways from that intersection that qualify to have - // continue instruction because they share a name with the approaching way - const std::int64_t continue_count = - count_if(++begin(intersection), end(intersection), [&](const auto &way) { - return IsContinueRoad(node_data_container.GetAnnotation( - node_based_graph.GetEdgeData(way.eid).annotation_data)); - }); - const std::int64_t continue_count_valid = - count_if(++begin(intersection), end(intersection), [&](const auto &way) { - return IsContinueRoad(node_data_container.GetAnnotation( - node_based_graph.GetEdgeData(way.eid).annotation_data)) && - way.entry_allowed; - }); - - // checks if continue candidates are sharp turns - const bool all_continues_are_narrow = [&]() { - return std::count_if(begin(intersection), end(intersection), [&](const Road &road) { - const auto &road_data = node_data_container.GetAnnotation( - node_based_graph.GetEdgeData(road.eid).annotation_data); - const double &road_angle = angularDeviation(road.angle, STRAIGHT_ANGLE); - return IsContinueRoad(road_data) && (road_angle < NARROW_TURN_ANGLE); - }) == continue_count; - }(); - - // return true if the best_option candidate is more promising than the best_continue candidate - // otherwise return false, the best_continue candidate is more promising - const auto best_over_best_continue = [&]() { - // no continue road exists - if (best_continue == 0) - return true; - - // we have multiple continues and not all are narrow. This suggests that - // the continue candidates are ambiguous - if (!all_continues_are_narrow && (continue_count >= 2 && intersection.size() >= 4)) - return true; - - // if the best continue is not narrow and we also have at least 2 possible choices, the - // intersection size does not matter anymore - if (continue_count_valid >= 2 && best_continue_deviation >= 2 * NARROW_TURN_ANGLE) - return true; - - // continue data now most certainly exists - const auto &continue_edge = node_based_graph.GetEdgeData(intersection[best_continue].eid); - - // best_continue is obvious by road class - if (obviousByRoadClass(in_way_edge.flags.road_classification, - continue_edge.flags.road_classification, - best_option_edge.flags.road_classification)) - return false; - - // best_option is obvious by road class - if (obviousByRoadClass(in_way_edge.flags.road_classification, - best_option_edge.flags.road_classification, - continue_edge.flags.road_classification)) - return true; - - // the best_option deviation is very straight and not a ramp - if (best_option_deviation < best_continue_deviation && - best_option_deviation < FUZZY_ANGLE_DIFFERENCE && - !best_option_edge.flags.road_classification.IsRampClass()) - return true; - - // the continue road is of a lower priority, while the road continues on the same priority - // with a better angle - if (best_option_deviation < best_continue_deviation && - in_way_edge.flags.road_classification == best_option_edge.flags.road_classification && - continue_edge.flags.road_classification.GetPriority() > - best_option_edge.flags.road_classification.GetPriority()) - return true; - - return false; - }(); - - // check whether we turn onto a oneway through street. These typically happen at the end of - // roads and might not seem obvious, since it isn't always as visible that you cannot turn - // left/right. To be on the safe side, we announce these as non-obvious - const auto turns_onto_through_street = [&](const auto &road) { - // find edge opposite to the one we are checking (in-road) - const auto in_through_candidate = - intersection.FindClosestBearing(util::bearing::reverse(road.perceived_bearing)); - - const auto &in_edge = node_based_graph.GetEdgeData(in_through_candidate->eid); - const auto &out_edge = node_based_graph.GetEdgeData(road.eid); - - // by asking for the same class, we ensure that we do not overrule obvious by road-class - // decisions - const auto same_class = - in_edge.flags.road_classification == out_edge.flags.road_classification; - - // only if the entry is allowed for one of the two, but not the other, we need to check. - // Otherwise other handlers do it better - const bool is_oneway = !in_through_candidate->entry_allowed && road.entry_allowed; - - const bool not_roundabout = !(in_edge.flags.roundabout || in_edge.flags.circular || - out_edge.flags.roundabout || out_edge.flags.circular); - - // for the purpose of this check, we do not care about low-priority roads (parking lots, - // mostly). Since we postulate both classes to be the same, checking one of the two is - // enough - const bool not_low_priority = !in_edge.flags.road_classification.IsLowPriorityRoadClass(); - - const auto in_deviation = angularDeviation(in_through_candidate->angle, STRAIGHT_ANGLE); - const auto out_deviaiton = angularDeviation(road.angle, STRAIGHT_ANGLE); - // in case the deviation isn't considerably lower for the road we are turning onto, - // consider it non-obvious. The threshold here requires a slight (60) vs sharp (120) - // degree variation, at lest (120/60 == 2) - return is_oneway && same_class && not_roundabout && not_low_priority && - (in_deviation / (std::max(out_deviaiton, 0.5)) <= 2); - }; - - if (best_over_best_continue) - { - // Find left/right deviation - // skipping over service roads - const std::size_t left_index = [&]() { - const auto index_candidate = (best_option + 1) % intersection.size(); - if (index_candidate == 0) - return index_candidate; - const auto &candidate_edge = - node_based_graph.GetEdgeData(intersection[index_candidate].eid); - if (obviousByRoadClass(in_way_edge.flags.road_classification, - best_option_edge.flags.road_classification, - candidate_edge.flags.road_classification)) - return (index_candidate + 1) % intersection.size(); - else - return index_candidate; - - }(); - const auto right_index = [&]() { - BOOST_ASSERT(best_option > 0); - const auto index_candidate = best_option - 1; - if (index_candidate == 0) - return index_candidate; - const auto &candidate_edge = - node_based_graph.GetEdgeData(intersection[index_candidate].eid); - if (obviousByRoadClass(in_way_edge.flags.road_classification, - best_option_edge.flags.road_classification, - candidate_edge.flags.road_classification)) - return index_candidate - 1; - else - return index_candidate; - }(); - - const double left_deviation = - angularDeviation(intersection[left_index].angle, STRAIGHT_ANGLE); - const double right_deviation = - angularDeviation(intersection[right_index].angle, STRAIGHT_ANGLE); - - // return best_option candidate if it is nearly straight and distinct from the nearest other - // out way - if (best_option_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION && - std::min(left_deviation, right_deviation) > FUZZY_ANGLE_DIFFERENCE) - return best_option; - - const auto &left_edge = node_based_graph.GetEdgeData(intersection[left_index].eid); - const auto &right_edge = node_based_graph.GetEdgeData(intersection[right_index].eid); - - const bool obvious_to_left = - left_index == 0 || obviousByRoadClass(in_way_edge.flags.road_classification, - best_option_edge.flags.road_classification, - left_edge.flags.road_classification); - const bool obvious_to_right = - right_index == 0 || obviousByRoadClass(in_way_edge.flags.road_classification, - best_option_edge.flags.road_classification, - right_edge.flags.road_classification); - - // if the best_option turn isn't narrow, but there is a nearly straight turn, we don't - // consider the turn obvious - const auto check_narrow = [&intersection, best_option_deviation](const std::size_t index) { - return angularDeviation(intersection[index].angle, STRAIGHT_ANGLE) <= - FUZZY_ANGLE_DIFFERENCE && - (best_option_deviation > NARROW_TURN_ANGLE || intersection[index].entry_allowed); - }; - - // other narrow turns? - if (check_narrow(right_index) && !obvious_to_right) - return 0; - - if (check_narrow(left_index) && !obvious_to_left) - return 0; - - // we are turning onto a through street (possibly at the end of the road). Ensure that we - // announce a turn, if it isn't a slight merge - if (turns_onto_through_street(intersection[best_option])) - return 0; - - // checks if a given way in the intersection is distinct enough from the best_option - // candidate - const auto isDistinct = [&](const std::size_t index, const double deviation) { - /* - If the neighbor is not possible to enter, we allow for a lower - distinction rate. If the road category is smaller, its also adjusted. Only - roads of the same priority require the full distinction ratio. - */ - const auto &best_option_edge = - node_based_graph.GetEdgeData(intersection[best_option].eid); - const auto adjusted_distinction_ratio = [&]() { - // obviousness by road classes - if (in_way_edge.flags.road_classification == - best_option_edge.flags.road_classification && - best_option_edge.flags.road_classification.GetPriority() < - node_based_graph.GetEdgeData(intersection[index].eid) - .flags.road_classification.GetPriority()) - return 0.8 * DISTINCTION_RATIO; - // if road classes are the same, we use the full ratio - else - return DISTINCTION_RATIO; - }(); - return index == 0 || deviation / best_option_deviation >= adjusted_distinction_ratio || - (deviation <= NARROW_TURN_ANGLE && !intersection[index].entry_allowed); - }; - - const bool distinct_to_left = isDistinct(left_index, left_deviation); - const bool distinct_to_right = isDistinct(right_index, right_deviation); - // Well distinct turn that is nearly straight - if ((distinct_to_left || obvious_to_left) && (distinct_to_right || obvious_to_right)) - return best_option; - } - else - { - const auto &continue_edge = node_based_graph.GetEdgeData(intersection[best_continue].eid); - const auto &continue_data = - node_data_container.GetAnnotation(continue_edge.annotation_data); - if (std::abs(best_continue_deviation) < 1) - return best_continue; - - // we are turning onto a through street (possibly at the end of the road). Ensure that we - // announce a turn, if it isn't a slight merge - if (turns_onto_through_street(intersection[best_continue])) - return 0; - - // check if any other similar best continues exist - std::size_t i, last = intersection.size(); - for (i = 1; i < last; ++i) - { - if (i == best_continue || !intersection[i].entry_allowed) - continue; - - const auto &turn_edge = node_based_graph.GetEdgeData(intersection[i].eid); - const auto &turn_data = node_data_container.GetAnnotation(turn_edge.annotation_data); - const bool is_obvious_by_road_class = - obviousByRoadClass(in_way_edge.flags.road_classification, - continue_edge.flags.road_classification, - turn_edge.flags.road_classification); - - // if the main road is obvious by class, we ignore the current road as a potential - // prevention of obviousness - if (is_obvious_by_road_class) - continue; - - // continuation could be grouped with a straight turn and the turning road is a ramp - if (turn_edge.flags.road_classification.IsRampClass() && - best_continue_deviation < GROUP_ANGLE && - !continue_edge.flags.road_classification.IsRampClass()) - continue; - - // perfectly straight turns prevent obviousness - const auto turn_deviation = angularDeviation(intersection[i].angle, STRAIGHT_ANGLE); - if (turn_deviation < FUZZY_ANGLE_DIFFERENCE) - return 0; - - const auto deviation_ratio = turn_deviation / best_continue_deviation; - - // in comparison to normal deviations, a continue road can offer a smaller distinction - // ratio. Other roads close to the turn angle are not as obvious, if one road continues. - if (deviation_ratio < DISTINCTION_RATIO / 1.5) - return 0; - - /* in comparison to another continuing road, we need a better distinction. This prevents - situations where the turn is probably less obvious. An example are places that have a - road with the same name entering/exiting: - - d - / - / - a -- b - \ - \ - c - */ - - const auto same_name = !util::guidance::requiresNameAnnounced( - turn_data.name_id, continue_data.name_id, name_table, street_name_suffix_table); - - if (same_name && deviation_ratio < 1.5 * DISTINCTION_RATIO) - return 0; - } - - // Segregated intersections can result in us finding an obvious turn, even though its only - // obvious due to a very short segment in between. So if the segment coming in is very - // short, we check the previous intersection for other continues in the opposite bearing. - const auto node_at_intersection = node_based_graph.GetTarget(via_edge); - - const double constexpr MAX_COLLAPSE_DISTANCE = 30; - const auto distance_at_u_turn = intersection[0].segment_length; - if (distance_at_u_turn < MAX_COLLAPSE_DISTANCE) - { - // this request here actually goes against the direction of the ingoing edgeid. This can - // even reverse the direction. Since we don't want to compute actual turns but simply - // try to find whether there is a turn going to the opposite direction of our obvious - // turn, this should be alright. - const auto previous_intersection = [&]() -> extractor::intersection::IntersectionView { - const auto parameters = extractor::intersection::skipDegreeTwoNodes( - node_based_graph, {node_at_intersection, intersection[0].eid}); - if (node_based_graph.GetTarget(parameters.edge) == node_at_intersection) - return {}; - - return extractor::intersection::getConnectedRoads(node_based_graph, - node_data_container, - node_coordinates, - compressed_geometries, - node_restriction_map, - barrier_nodes, - turn_lanes_data, - parameters); - }(); - - if (!previous_intersection.empty()) - { - const auto continue_road = intersection[best_continue]; - for (const auto &comparison_road : previous_intersection) - { - // since we look at the intersection in the wrong direction, a similar angle - // actually represents a near 180 degree different in bearings between the two - // roads. So if there is a road that is enterable in the opposite direction just - // prior, a turn is not obvious - const auto &turn_edge_data = node_based_graph.GetEdgeData(comparison_road.eid); - const auto &turn_data = - node_data_container.GetAnnotation(turn_edge_data.annotation_data); - if (angularDeviation(comparison_road.angle, STRAIGHT_ANGLE) > GROUP_ANGLE && - angularDeviation(comparison_road.angle, continue_road.angle) < - FUZZY_ANGLE_DIFFERENCE && - !turn_edge_data.reversed && continue_data.CanCombineWith(turn_data)) - return 0; - } - } - } - - return best_continue; - } - - return 0; -} - } // namespace guidance } // namespace osrm diff --git a/profiles/lib/guidance.lua b/profiles/lib/guidance.lua index d920a9c6c..77cdff223 100644 --- a/profiles/lib/guidance.lua +++ b/profiles/lib/guidance.lua @@ -70,24 +70,23 @@ parking_class = Set{ function Guidance.set_classification (highway, result, input_way) if motorway_types[highway] then - result.road_classification.motorway_class = true; + result.road_classification.motorway_class = true end if link_types[highway] then - result.road_classification.link_class = true; + result.road_classification.link_class = true end - -- we distinguish between different service types, if specified, we recognise parking and alleys. - -- If we see an unrecognised type, we assume a pure connectivity road. All unspecified are recognised as alley + -- All service roads are recognised as alley if highway ~= nil and highway == 'service' then local service_type = input_way:get_value_by_key('service'); if service_type ~= nil and parking_class[service_type] then - result.road_classification.road_priority_class = road_priority_class.parking; + result.road_classification.road_priority_class = road_priority_class.alley else if service_type ~= nil and service_type == 'alley' then - result.road_classification.road_priority_class = road_priority_class.alley; + result.road_classification.road_priority_class = road_priority_class.alley else if serice_type == nil then - result.road_classification.road_priority_class = road_priority_class.alley; + result.road_classification.road_priority_class = road_priority_class.alley else result.road_classification.road_priority_class = highway_classes[highway] end diff --git a/src/engine/guidance/collapse_turns.cpp b/src/engine/guidance/collapse_turns.cpp index da60fd911..81c702ab4 100644 --- a/src/engine/guidance/collapse_turns.cpp +++ b/src/engine/guidance/collapse_turns.cpp @@ -240,6 +240,7 @@ void AdjustToCombinedTurnStrategy::operator()(RouteStep &step_at_turn_location, setInstructionType(step_at_turn_location, TurnType::Turn); } else if (hasTurnType(step_at_turn_location, TurnType::Turn) && + !hasTurnType(transfer_from_step, TurnType::Suppressed) && haveSameName(step_prior_to_intersection, transfer_from_step)) { setInstructionType(step_at_turn_location, TurnType::Continue); diff --git a/src/guidance/sliproad_handler.cpp b/src/guidance/sliproad_handler.cpp index cd68aa536..493e9d7c2 100644 --- a/src/guidance/sliproad_handler.cpp +++ b/src/guidance/sliproad_handler.cpp @@ -852,6 +852,11 @@ SliproadHandler::scaledThresholdByRoadClass(const double max_threshold, case extractor::RoadPriorityClass::SIDE_RESIDENTIAL: factor = 0.3; break; + case extractor::RoadPriorityClass::MOTORWAY_LINK: + case extractor::RoadPriorityClass::TRUNK_LINK: + case extractor::RoadPriorityClass::PRIMARY_LINK: + case extractor::RoadPriorityClass::SECONDARY_LINK: + case extractor::RoadPriorityClass::TERTIARY_LINK: case extractor::RoadPriorityClass::LINK_ROAD: factor = 0.3; break;