Reduce NewName Instructructions / Name Changes

With @karenzshea's name / ref split (ref. #2857) in master we want to
make use of it and reduce `NewName` instructions when ever possible.
This is a first step towards #2744 by using the already existing name
change heuristic from the extractor now in post-processing as well.

Limitations: at the moment we don't have the `SuffixTable` in
post-processing; this would require us serializing and subsequently
deserializing the table, passing it through from the profiles to the
API.
This commit is contained in:
Daniel J. Hofmann 2016-09-12 15:24:15 +02:00 committed by Moritz Kobitzsch
parent 7ffe832ff8
commit 60010dd998
10 changed files with 412 additions and 193 deletions

View File

@ -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 |

View File

@ -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"

View File

@ -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 <utility>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/functional/hash.hpp>
#include <boost/tokenizer.hpp>
@ -151,122 +149,6 @@ getRepresentativeCoordinate(const NodeID from_node,
}
}
inline std::pair<std::string, std::string> 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.

View File

@ -25,6 +25,7 @@ class SuffixTable final
private:
std::unordered_set<std::string> suffix_set;
};
} /* namespace extractor */
} /* namespace osrm */

View File

@ -12,8 +12,13 @@
#include "util/simple_logger.hpp"
#include <algorithm>
#include <string>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/functional/hash.hpp>
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<std::string, std::string> 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 <typename SuffixTable>
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

View File

@ -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

View File

@ -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<RouteStep> &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<RouteStep> &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<RouteStep> &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<RouteStep> &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<RouteStep> &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<RouteStep> collapseTurns(std::vector<RouteStep> 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<RouteStep> collapseTurns(std::vector<RouteStep> 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<RouteStep> collapseTurns(std::vector<RouteStep> 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<RouteStep> collapseTurns(std::vector<RouteStep> 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]))
{

View File

@ -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 <algorithm>
@ -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)

View File

@ -222,48 +222,46 @@ RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
std::unordered_set<unsigned> roundabout_name_ids;
std::unordered_set<unsigned> 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

View File

@ -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<char *>(&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