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:
committed by
Moritz Kobitzsch
parent
7ffe832ff8
commit
60010dd998
@@ -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]))
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user