diff --git a/features/guidance/new-name.feature b/features/guidance/new-name.feature index 748380d12..9ff97561b 100644 --- a/features/guidance/new-name.feature +++ b/features/guidance/new-name.feature @@ -164,3 +164,172 @@ Feature: New-Name Instructions | waypoints | route | turns | | a,e | name,with-name,with-name | depart,new name straight,arrive | | b,e | with-name,with-name | depart,arrive | + + Scenario: Both Name and Ref Empty + Given the node map + | a | | b | | c | + + And the ways + | nodes | name | ref | + | ab | | | + | bc | | | + + When I route I should get + | waypoints | route | turns | + | a,c | , | depart,arrive | + + Scenario: Same Name, Ref Extended + Given the node map + | a | | b | | c | + + And the ways + | nodes | name | ref | + | ab | A | B1 | + | bc | C | B1;B2 | + + When I route I should get + | waypoints | route | turns | + | a,c | A,C,C | depart,new name straight,arrive | + + Scenario: Same Name, Ref Removed + Given the node map + | a | | b | | c | + + And the ways + | nodes | name | ref | + | ab | A | B1;B2 | + | bc | C | B1 | + + When I route I should get + | waypoints | route | turns | + | a,c | A,C,C | depart,new name straight,arrive | + + Scenario: Name Removed, Ref Extended + Given the node map + | a | | b | | c | + + And the ways + | nodes | name | ref | + | ab | A | B1 | + | bc | | B1;B2 | + + When I route I should get + | waypoints | route | turns | + | a,c | A, | depart,arrive | + + Scenario: Name Added, Ref Removed + Given the node map + | a | | b | | c | + + And the ways + | nodes | name | ref | + | ab | | B1 | + | bc | A | | + + When I route I should get + | waypoints | route | turns | + | a,c | ,A,A | depart,new name straight,arrive | + + Scenario: Prefix Change + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | North Central Expressway | US 75 | motorway | + | bc | Central Expressway | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | North Central Expressway,Central Expressway,Central Expressway | depart,new name straight,arrive | + + Scenario: Prefix Change + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ba | North Central Expressway | US 75 | motorway | + | cb | Central Expressway | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | c,a | Central Expressway,North Central Expressway,North Central Expressway | depart,new name straight,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | Central Expressway | US 75 | motorway | + | bc | | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | Central Expressway, | depart,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | | US 75 | motorway | + | bc | Central Expressway | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | ,Central Expressway | depart,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | | US 75;US 69 | motorway | + | bc | | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | , | depart,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | | US 69;US 75 | motorway | + | bc | | US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | , | depart,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | | US 75 | motorway | + | bc | | US 75;US 69 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | , | depart,arrive | + + Scenario: No Name, Same Reference + Given the node map + | a | | | | b | | | | c | + + And the ways + | nodes | name | ref | highway | + | ab | | US 75 | motorway | + | bc | | US 69;US 75 | motorway | + + When I route I should get + | waypoints | route | turns | + | a,c | , | depart,arrive | diff --git a/include/engine/guidance/post_processing.hpp b/include/engine/guidance/post_processing.hpp index b33694c0c..f7203214f 100644 --- a/include/engine/guidance/post_processing.hpp +++ b/include/engine/guidance/post_processing.hpp @@ -1,7 +1,6 @@ #ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP #define ENGINE_GUIDANCE_POST_PROCESSING_HPP -#include "engine/datafacade/datafacade_base.hpp" #include "engine/guidance/leg_geometry.hpp" #include "engine/guidance/route_step.hpp" #include "engine/phantom_node.hpp" diff --git a/include/extractor/guidance/toolkit.hpp b/include/extractor/guidance/toolkit.hpp index 0ac4f1b1c..b505590e3 100644 --- a/include/extractor/guidance/toolkit.hpp +++ b/include/extractor/guidance/toolkit.hpp @@ -11,7 +11,6 @@ #include "extractor/compressed_edge_container.hpp" #include "extractor/query_node.hpp" -#include "extractor/suffix_table.hpp" #include "extractor/guidance/discrete_angle.hpp" #include "extractor/guidance/intersection.hpp" @@ -26,7 +25,6 @@ #include #include -#include #include #include @@ -151,122 +149,6 @@ getRepresentativeCoordinate(const NodeID from_node, } } -inline std::pair getPrefixAndSuffix(const std::string &data) -{ - const auto suffix_pos = data.find_last_of(' '); - if (suffix_pos == std::string::npos) - return {}; - - const auto prefix_pos = data.find_first_of(' '); - auto result = std::make_pair(data.substr(0, prefix_pos), data.substr(suffix_pos + 1)); - boost::to_lower(result.first); - boost::to_lower(result.second); - return result; -} - -inline bool requiresNameAnnounced(const std::string &from, - const std::string &to, - const SuffixTable &suffix_table) -{ - // first is empty and the second is not - if (from.empty() && !to.empty()) - return true; - - // FIXME, handle in profile to begin with? - // this uses the encoding of references in the profile, which is very BAD - // Input for this function should be a struct separating streetname, suffix (e.g. road, - // boulevard, North, West ...), and a list of references - std::string from_name; - std::string from_ref; - std::string to_name; - std::string to_ref; - - // Split from the format "{name} ({ref})" -> name, ref - auto split = [](const std::string &name, std::string &out_name, std::string &out_ref) { - const auto ref_begin = name.find_first_of('('); - if (ref_begin != std::string::npos) - { - if (ref_begin != 0) - out_name = name.substr(0, ref_begin - 1); - const auto ref_end = name.find_first_of(')'); - out_ref = name.substr(ref_begin + 1, ref_end - ref_begin - 1); - } - else - { - out_name = name; - } - }; - - split(from, from_name, from_ref); - split(to, to_name, to_ref); - - // check similarity of names - const auto names_are_empty = from_name.empty() && to_name.empty(); - const auto name_is_contained = - boost::starts_with(from_name, to_name) || boost::starts_with(to_name, from_name); - - const auto checkForPrefixOrSuffixChange = []( - const std::string &first, const std::string &second, const SuffixTable &suffix_table) { - - const auto first_prefix_and_suffixes = getPrefixAndSuffix(first); - const auto second_prefix_and_suffixes = getPrefixAndSuffix(second); - // reverse strings, get suffices and reverse them to get prefixes - const auto checkTable = [&](const std::string &str) { - return str.empty() || suffix_table.isSuffix(str); - }; - - const auto getOffset = [](const std::string &str) -> std::size_t { - if (str.empty()) - return 0; - else - return str.length() + 1; - }; - - const bool is_prefix_change = [&]() -> bool { - if (!checkTable(first_prefix_and_suffixes.first)) - return false; - if (!checkTable(second_prefix_and_suffixes.first)) - return false; - return !first.compare(getOffset(first_prefix_and_suffixes.first), - std::string::npos, - second, - getOffset(second_prefix_and_suffixes.first), - std::string::npos); - }(); - - const bool is_suffix_change = [&]() -> bool { - if (!checkTable(first_prefix_and_suffixes.second)) - return false; - if (!checkTable(second_prefix_and_suffixes.second)) - return false; - return !first.compare(0, - first.length() - getOffset(first_prefix_and_suffixes.second), - second, - 0, - second.length() - getOffset(second_prefix_and_suffixes.second)); - }(); - - return is_prefix_change || is_suffix_change; - }; - - const auto is_suffix_change = checkForPrefixOrSuffixChange(from_name, to_name, suffix_table); - const auto names_are_equal = from_name == to_name || name_is_contained || is_suffix_change; - const auto name_is_removed = !from_name.empty() && to_name.empty(); - // references are contained in one another - const auto refs_are_empty = from_ref.empty() && to_ref.empty(); - const auto ref_is_contained = - from_ref.empty() || to_ref.empty() || - (from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos); - const auto ref_is_removed = !from_ref.empty() && to_ref.empty(); - - const auto obvious_change = - (names_are_empty && refs_are_empty) || (names_are_equal && ref_is_contained) || - (names_are_equal && refs_are_empty) || (ref_is_contained && name_is_removed) || - (names_are_equal && ref_is_removed) || is_suffix_change; - - return !obvious_change; -} - // To simplify handling of Left/Right hand turns, we can mirror turns and write an intersection // handler only for one side. The mirror function turns a left-hand turn in a equivalent right-hand // turn and vice versa. diff --git a/include/extractor/suffix_table.hpp b/include/extractor/suffix_table.hpp index 31830b90e..7785a5e14 100644 --- a/include/extractor/suffix_table.hpp +++ b/include/extractor/suffix_table.hpp @@ -25,6 +25,7 @@ class SuffixTable final private: std::unordered_set suffix_set; }; + } /* namespace extractor */ } /* namespace osrm */ diff --git a/include/util/guidance/toolkit.hpp b/include/util/guidance/toolkit.hpp index 14f239699..d6480f016 100644 --- a/include/util/guidance/toolkit.hpp +++ b/include/util/guidance/toolkit.hpp @@ -12,8 +12,13 @@ #include "util/simple_logger.hpp" #include +#include #include +#include +#include +#include + namespace osrm { namespace util @@ -131,6 +136,128 @@ inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruc return instruction.type == extractor::guidance::TurnType::StayOnRoundabout; } +// Name Change Logic +// Used both during Extraction as well as during Post-Processing + +inline std::pair getPrefixAndSuffix(const std::string &data) +{ + const auto suffix_pos = data.find_last_of(' '); + if (suffix_pos == std::string::npos) + return {}; + + const auto prefix_pos = data.find_first_of(' '); + auto result = std::make_pair(data.substr(0, prefix_pos), data.substr(suffix_pos + 1)); + boost::to_lower(result.first); + boost::to_lower(result.second); + return result; +} + +// Note: there is an overload without suffix checking below. +// (that's the reason we template the suffix table here) +template +inline bool requiresNameAnnounced(const std::string &from_name, + const std::string &from_ref, + const std::string &to_name, + const std::string &to_ref, + const SuffixTable &suffix_table) +{ + // first is empty and the second is not + if ((from_name.empty() && from_ref.empty()) && !(to_name.empty() && to_ref.empty())) + return true; + + // FIXME, handle in profile to begin with? + // Input for this function should be a struct separating streetname, suffix (e.g. road, + // boulevard, North, West ...), and a list of references + + // check similarity of names + const auto names_are_empty = from_name.empty() && to_name.empty(); + const auto name_is_contained = + boost::starts_with(from_name, to_name) || boost::starts_with(to_name, from_name); + + const auto checkForPrefixOrSuffixChange = []( + const std::string &first, const std::string &second, const SuffixTable &suffix_table) { + + const auto first_prefix_and_suffixes = getPrefixAndSuffix(first); + const auto second_prefix_and_suffixes = getPrefixAndSuffix(second); + + // reverse strings, get suffices and reverse them to get prefixes + const auto checkTable = [&](const std::string &str) { + return str.empty() || suffix_table.isSuffix(str); + }; + + const auto getOffset = [](const std::string &str) -> std::size_t { + if (str.empty()) + return 0; + else + return str.length() + 1; + }; + + const bool is_prefix_change = [&]() -> bool { + if (!checkTable(first_prefix_and_suffixes.first)) + return false; + if (!checkTable(second_prefix_and_suffixes.first)) + return false; + return !first.compare(getOffset(first_prefix_and_suffixes.first), + std::string::npos, + second, + getOffset(second_prefix_and_suffixes.first), + std::string::npos); + }(); + + const bool is_suffix_change = [&]() -> bool { + if (!checkTable(first_prefix_and_suffixes.second)) + return false; + if (!checkTable(second_prefix_and_suffixes.second)) + return false; + return !first.compare(0, + first.length() - getOffset(first_prefix_and_suffixes.second), + second, + 0, + second.length() - getOffset(second_prefix_and_suffixes.second)); + }(); + + return is_prefix_change || is_suffix_change; + }; + + const auto is_suffix_change = checkForPrefixOrSuffixChange(from_name, to_name, suffix_table); + const auto names_are_equal = from_name == to_name || name_is_contained || is_suffix_change; + const auto name_is_removed = !from_name.empty() && to_name.empty(); + // references are contained in one another + const auto refs_are_empty = from_ref.empty() && to_ref.empty(); + const auto ref_is_contained = + from_ref.empty() || to_ref.empty() || + (from_ref.find(to_ref) != std::string::npos || to_ref.find(from_ref) != std::string::npos); + const auto ref_is_removed = !from_ref.empty() && to_ref.empty(); + + const auto obvious_change = + (names_are_empty && refs_are_empty) || (names_are_equal && ref_is_contained) || + (names_are_equal && refs_are_empty) || (ref_is_contained && name_is_removed) || + (names_are_equal && ref_is_removed) || is_suffix_change; + + const auto needs_announce = + // " (Ref)" -> "Name " + (from_name.empty() && !from_ref.empty() && !to_name.empty() && to_ref.empty()); + + return !obvious_change || needs_announce; +} + +// Overload without suffix checking +inline bool requiresNameAnnounced(const std::string &from_name, + const std::string &from_ref, + const std::string &to_name, + const std::string &to_ref) +{ + // Dummy since we need to provide a SuffixTable but do not have the data for it. + // (Guidance Post-Processing does not keep the suffix table around at the moment) + struct NopSuffixTable final + { + NopSuffixTable(){} + bool isSuffix(const std::string &) const { return false; } + } static const table; + + return requiresNameAnnounced(from_name, from_ref, to_name, to_ref, table); +} + } // namespace guidance } // namespace util } // namespace osrm diff --git a/include/util/name_table.hpp b/include/util/name_table.hpp index 318cffca1..2f45a71e3 100644 --- a/include/util/name_table.hpp +++ b/include/util/name_table.hpp @@ -23,7 +23,13 @@ class NameTable public: NameTable(const std::string &filename); + + // This class provides a limited view over all the string data we serialize out. + // The following functions are a subset of what is available. + // See the data facades for they provide full access to this serialized string data. + // (at time of writing this: get{Name,Ref,Pronunciation,Destinations}ForID(name_id);) std::string GetNameForID(const unsigned name_id) const; + std::string GetRefForID(const unsigned name_id) const; }; } // namespace util } // namespace osrm diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp index 4e9f03455..7f02b664c 100644 --- a/src/engine/guidance/post_processing.cpp +++ b/src/engine/guidance/post_processing.cpp @@ -1,5 +1,5 @@ -#include "extractor/guidance/turn_instruction.hpp" #include "engine/guidance/post_processing.hpp" +#include "extractor/guidance/turn_instruction.hpp" #include "engine/guidance/assemble_steps.hpp" #include "engine/guidance/lane_processing.hpp" @@ -83,18 +83,6 @@ bool isCollapsableInstruction(const TurnInstruction instruction) bool compatible(const RouteStep &lhs, const RouteStep &rhs) { return lhs.mode == rhs.mode; } -double nameSegmentLength(std::size_t at, const std::vector &steps) -{ - double result = steps[at].distance; - while (at + 1 < steps.size() && steps[at + 1].name_id == steps[at].name_id) - { - ++at; - result += steps[at].distance; - } - - return result; -} - // invalidate a step and set its content to nothing void invalidateStep(RouteStep &step) { step = getInvalidRouteStep(); } @@ -129,6 +117,28 @@ double turn_angle(const double entry_bearing, const double exit_bearing) return angle > 360 ? angle - 360 : angle; } +// Checks if name change happens the user wants to know about. +// Treats e.g. "Name (Ref)" -> "Name" changes still as same name. +bool isNoticeableNameChange(const RouteStep &lhs, const RouteStep &rhs) +{ + // TODO: at some point we might want to think about pronunciation here. + // Also rotary_name is not handled at the moment. + return util::guidance::requiresNameAnnounced(lhs.name, lhs.ref, rhs.name, rhs.ref); +} + +double nameSegmentLength(std::size_t at, const std::vector &steps) +{ + BOOST_ASSERT(at < steps.size()); + + double result = steps[at].distance; + while (at + 1 < steps.size() && !isNoticeableNameChange(steps[at], steps[at + 1])) + { + at += 1; + result += steps[at].distance; + } + return result; +} + OSRM_ATTR_WARN_UNUSED RouteStep forwardInto(RouteStep destination, const RouteStep &source) { @@ -409,12 +419,16 @@ void collapseTurnAt(std::vector &steps, if (compatible(one_back_step, current_step)) { steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]); + if ((TurnType::Continue == one_back_step.maneuver.instruction.type || TurnType::Suppressed == one_back_step.maneuver.instruction.type) && - current_step.name_id != steps[two_back_index].name_id) + isNoticeableNameChange(steps[two_back_index], current_step)) + { + steps[one_back_index].maneuver.instruction.type = TurnType::Turn; + } else if (TurnType::Turn == one_back_step.maneuver.instruction.type && - current_step.name_id == steps[two_back_index].name_id) + !isNoticeableNameChange(steps[two_back_index], current_step)) { steps[one_back_index].maneuver.instruction.type = TurnType::Continue; @@ -460,7 +474,7 @@ void collapseTurnAt(std::vector &steps, { BOOST_ASSERT(two_back_index < steps.size()); // the simple case is a u-turn that changes directly into the in-name again - const bool direct_u_turn = steps[two_back_index].name_id == current_step.name_id; + const bool direct_u_turn = !isNoticeableNameChange(steps[two_back_index], current_step); // however, we might also deal with a dual-collapse scenario in which we have to // additionall collapse a name-change as welll @@ -471,7 +485,7 @@ void collapseTurnAt(std::vector &steps, isCollapsableInstruction(steps[next_step_index].maneuver.instruction)); const bool u_turn_with_name_change = continues_with_name_change && - steps[next_step_index].name_id == steps[two_back_index].name_id; + !isNoticeableNameChange(steps[two_back_index], steps[next_step_index]); if (direct_u_turn || u_turn_with_name_change) { @@ -795,9 +809,8 @@ std::vector collapseTurns(std::vector steps) // Turn Types in the response depend on whether we find the same road name // (sliproad indcating a u-turn) or if we are turning onto a different road, in // which case we use a turn. - if (steps[getPreviousIndex(one_back_index)].name_id == - steps[step_index].name_id && - steps[step_index].name_id != EMPTY_NAMEID) + if (!isNoticeableNameChange(steps[getPreviousIndex(one_back_index)], + steps[step_index])) steps[one_back_index].maneuver.instruction.type = TurnType::Continue; else steps[one_back_index].maneuver.instruction.type = TurnType::Turn; @@ -831,7 +844,7 @@ std::vector collapseTurns(std::vector steps) // These have to be handled in post-processing else if (isCollapsableInstruction(current_step.maneuver.instruction) && current_step.maneuver.instruction.type != TurnType::Suppressed && - steps[getPreviousNameIndex(step_index)].name_id == current_step.name_id && + !isNoticeableNameChange(steps[getPreviousNameIndex(step_index)], current_step) && // canCollapseAll is also checking for compatible(step,step+1) for all indices canCollapseAll(getPreviousNameIndex(step_index) + 1, next_step_index)) { @@ -856,8 +869,7 @@ std::vector collapseTurns(std::vector steps) const auto two_back_index = getPreviousIndex(one_back_index); BOOST_ASSERT(two_back_index < steps.size()); // valid, since one_back is collapsable or a turn and therefore not depart: - const auto &coming_from_name_id = steps[two_back_index].name_id; - if (current_step.name_id == coming_from_name_id) + if (!isNoticeableNameChange(steps[two_back_index], current_step)) { if (compatible(one_back_step, steps[two_back_index])) { @@ -889,7 +901,7 @@ std::vector collapseTurns(std::vector steps) else if (step_index + 2 < steps.size() && current_step.maneuver.instruction.type == TurnType::NewName && steps[next_step_index].maneuver.instruction.type == TurnType::NewName && - one_back_step.name_id == steps[next_step_index].name_id) + !isNoticeableNameChange(one_back_step, steps[next_step_index])) { if (compatible(steps[step_index], steps[next_step_index])) { diff --git a/src/extractor/guidance/intersection_handler.cpp b/src/extractor/guidance/intersection_handler.cpp index 1f521f634..8d80138d2 100644 --- a/src/extractor/guidance/intersection_handler.cpp +++ b/src/extractor/guidance/intersection_handler.cpp @@ -4,6 +4,7 @@ #include "util/coordinate_calculation.hpp" #include "util/guidance/toolkit.hpp" +#include "util/guidance/toolkit.hpp" #include "util/simple_logger.hpp" #include @@ -90,9 +91,11 @@ TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t const auto &in_data = node_based_graph.GetEdgeData(via_edge); const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); if (in_data.name_id != out_data.name_id && - requiresNameAnnounced(name_table.GetNameForID(in_data.name_id), - name_table.GetNameForID(out_data.name_id), - street_name_suffix_table)) + util::guidance::requiresNameAnnounced(name_table.GetNameForID(in_data.name_id), + name_table.GetRefForID(in_data.name_id), + name_table.GetNameForID(out_data.name_id), + name_table.GetRefForID(out_data.name_id), + street_name_suffix_table)) { // obvious turn onto a through street is a merge if (through_street) @@ -597,17 +600,18 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge, 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 + /* 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 + */ if (turn_data.name_id == continue_data.name_id && deviation_ratio < 1.5 * DISTINCTION_RATIO) diff --git a/src/extractor/guidance/roundabout_handler.cpp b/src/extractor/guidance/roundabout_handler.cpp index 24ca74e5f..4018f261c 100644 --- a/src/extractor/guidance/roundabout_handler.cpp +++ b/src/extractor/guidance/roundabout_handler.cpp @@ -222,48 +222,46 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const std::unordered_set roundabout_name_ids; std::unordered_set connected_names; - const auto getNextOnRoundabout = - [this, &roundabout_name_ids, &connected_names](const NodeID node) { - EdgeID continue_edge = SPECIAL_EDGEID; - for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node)) + const auto getNextOnRoundabout = [this, &roundabout_name_ids, &connected_names]( + const NodeID node) { + EdgeID continue_edge = SPECIAL_EDGEID; + for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node)) + { + const auto &edge_data = node_based_graph.GetEdgeData(edge); + if (!edge_data.reversed && edge_data.roundabout) { - const auto &edge_data = node_based_graph.GetEdgeData(edge); - if (!edge_data.reversed && edge_data.roundabout) + if (SPECIAL_EDGEID != continue_edge) { - if (SPECIAL_EDGEID != continue_edge) - { - // fork in roundabout - return SPECIAL_EDGEID; - } - - if (EMPTY_NAMEID != edge_data.name_id) - { - bool add = true; - for (auto name_id : roundabout_name_ids) - { - - if (!requiresNameAnnounced(name_table.GetNameForID(name_id), - name_table.GetNameForID(edge_data.name_id), - street_name_suffix_table)) - { - add = false; - break; - } - } - if (add) - roundabout_name_ids.insert(edge_data.name_id); - } - - continue_edge = edge; + // fork in roundabout + return SPECIAL_EDGEID; } - else if (!edge_data.roundabout) + + if (EMPTY_NAMEID != edge_data.name_id) { - // remember all connected road names - connected_names.insert(edge_data.name_id); + + const auto announce = [&](unsigned id) { + return util::guidance::requiresNameAnnounced( + name_table.GetNameForID(id), + name_table.GetRefForID(id), + name_table.GetNameForID(edge_data.name_id), + name_table.GetRefForID(edge_data.name_id), + street_name_suffix_table); + }; + + if (std::all_of(begin(roundabout_name_ids), end(roundabout_name_ids), announce)) + roundabout_name_ids.insert(edge_data.name_id); } + + continue_edge = edge; } - return continue_edge; - }; + else if (!edge_data.roundabout) + { + // remember all connected road names + connected_names.insert(edge_data.name_id); + } + } + return continue_edge; + }; // the roundabout radius has to be the same for all locations we look at it from // to guarantee this, we search the full roundabout for its vertices // and select the three smallest ids diff --git a/src/util/name_table.cpp b/src/util/name_table.cpp index e15a35f84..66a9bc4d8 100644 --- a/src/util/name_table.cpp +++ b/src/util/name_table.cpp @@ -22,6 +22,9 @@ NameTable::NameTable(const std::string &filename) name_stream >> m_name_table; + if (!name_stream) + throw exception("Unable to deserialize RangeTable for NameTable"); + unsigned number_of_chars = 0; name_stream.read(reinterpret_cast(&number_of_chars), sizeof(number_of_chars)); if (!name_stream) @@ -64,5 +67,23 @@ std::string NameTable::GetNameForID(const unsigned name_id) const } return result; } + +std::string NameTable::GetRefForID(const unsigned name_id) const +{ + // Way string data is stored in blocks based on `name_id` as follows: + // + // | name | destination | pronunciation | ref | + // ^ ^ + // [range) + // ^ name_id + 3 + // + // `name_id + offset` gives us the range of chars. + // + // Offset 0 is name, 1 is destination, 2 is pronunciation, 3 is ref. + // See datafacades and extractor callbacks for details. + const constexpr auto OFFSET_REF = 3u; + return GetNameForID(name_id + OFFSET_REF); +} + } // namespace util } // namespace osrm