less new names, forks consider road classes, api clean-up

This commit is contained in:
Moritz Kobitzsch 2016-03-16 15:47:33 +01:00 committed by Patrick Niklaus
parent 831a536224
commit a3cf7f09e8
10 changed files with 392 additions and 44 deletions

View File

@ -0,0 +1,140 @@
@routing @guidance
Feature: Basic Roundabout
Background:
Given the profile "testbot"
Given a grid size of 10 meters
Scenario: Enter and Exit
Given the node map
| | | a | | |
| | | b | | |
| h | g | | c | d |
| | | e | | |
| | | f | | |
And the ways
| nodes | roundabout |
| ab | false |
| cd | false |
| ef | false |
| gh | false |
| bcegb | true |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart, roundabout-exit-1, arrive |
| a,f | ab,ef,ef | depart, roundabout-exit-2, arrive |
| a,h | ab,gh,gh | depart, roundabout-exit-3, arrive |
| d,f | cd,ef,ef | depart, roundabout-exit-1, arrive |
| d,h | cd,gh,gh | depart, roundabout-exit-2, arrive |
| d,a | cd,ab,ab | depart, roundabout-exit-3, arrive |
| f,h | ef,gh,gh | depart, roundabout-exit-1, arrive |
| f,a | ef,ab,ab | depart, roundabout-exit-2, arrive |
| f,d | ef,cd,cd | depart, roundabout-exit-3, arrive |
| h,a | gh,ab,ab | depart, roundabout-exit-1, arrive |
| h,d | gh,cd,cd | depart, roundabout-exit-2, arrive |
| h,f | gh,ef,ef | depart, roundabout-exit-3, arrive |
Scenario: Only Enter
Given the node map
| | | a | | |
| | | b | | |
| h | g | | c | d |
| | | e | | |
| | | f | | |
And the ways
| nodes | roundabout |
| ab | false |
| cd | false |
| ef | false |
| gh | false |
| bcegb | true |
When I route I should get
| waypoints | route | turns |
| a,b | ab,ab | depart, arrive |
| a,c | ab,bcegb | depart, roundabout-enter, arrive |
| a,e | ab,bcegb | depart, roundabout-enter, arrive |
| a,g | ab,bcegb | depart, roundabout-enter, arrive |
| d,c | cd,cd | depart, arrive |
| d,e | cd,bcegb | depart, roundabout-enter, arrive |
| d,g | cd,bcegb | depart, roundabout-enter, arrive |
| d,b | cd,bcegb | depart, roundabout-enter, arrive |
| f,e | ef,ef | depart, arrive |
| f,g | ef,bcegb | depart, roundabout-enter, arrive |
| f,b | ef,bcegb | depart, roundabout-enter, arrive |
| f,c | ef,bcegb | depart, roundabout-enter, arrive |
| h,g | gh,gh | depart, arrive |
| h,b | gh,bcegb | depart, roundabout-enter, arrive |
| h,c | gh,bcegb | depart, roundabout-enter, arrive |
| h,e | gh,bcegb | depart, roundabout-enter, arrive |
Scenario: Only Exit
Given the node map
| | | a | | |
| | | b | | |
| h | g | | c | d |
| | | e | | |
| | | f | | |
And the ways
| nodes | roundabout |
| ab | false |
| cd | false |
| ef | false |
| gh | false |
| bcegb | true |
When I route I should get
| waypoints | route | turns |
| b,a | ab,ab | depart, arrive |
| b,d | bcegb,cd,cd | depart, roundabout-exit-1, arrive |
| b,f | bcegb,ef,ef | depart, roundabout-exit-2, arrive |
| b,h | bcegb,gh,gh | depart, roundabout-exit-3, arrive |
| c,d | cd,cd | depart, arrive |
| c,f | bcegb,ef,ef | depart, roundabout-exit-1, arrive |
| c,h | bcegb,gh,gh | depart, roundabout-exit-2, arrive |
| c,a | bcegb,ab,ab | depart, roundabout-exit-3, arrive |
| e,f | ef,ef | depart, arrive |
| e,h | bcegb,gh,gh | depart, roundabout-exit-1, arrive |
| e,a | bcegb,ab,ab | depart, roundabout-exit-2, arrive |
| e,d | bcegb,cd,cd | depart, roundabout-exit-3, arrive |
| g,h | gh,gh | depart, arrive |
| g,a | bcegb,ab,ab | depart, roundabout-exit-1, arrive |
| g,d | bcegb,cd,cd | depart, roundabout-exit-2, arrive |
| g,f | bcegb,ef,ef | depart, roundabout-exit-3, arrive |
Scenario: Drive Around
Given the node map
| | | a | | |
| | | b | | |
| h | g | | c | d |
| | | e | | |
| | | f | | |
And the ways
| nodes | roundabout |
| ab | false |
| cd | false |
| ef | false |
| gh | false |
| bcegb | true |
When I route I should get
| waypoints | route | turns |
| b,c | bcegb,bcegb | depart, arrive |
| b,e | bcegb,bcegb | depart, arrive |
| b,g | bcegb,bcegb | depart, arrive |
| c,e | bcegb,bcegb | depart, arrive |
| c,g | bcegb,bcegb | depart, arrive |
| c,b | bcegb,bcegb | depart, arrive |
| e,g | bcegb,bcegb | depart, arrive |
| e,b | bcegb,bcegb | depart, arrive |
| e,c | bcegb,bcegb | depart, arrive |
| g,b | bcegb,bcegb | depart, arrive |
| g,c | bcegb,bcegb | depart, arrive |
| g,e | bcegb,bcegb | depart, arrive |
Scenario: Mixed Entry and Exit

View File

@ -17,6 +17,7 @@
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp"
#include "util/deallocating_vector.hpp"
#include "util/name_table.hpp"
#include <algorithm>
#include <cstdint>
@ -51,7 +52,8 @@ class EdgeBasedGraphFactory
const std::unordered_set<NodeID> &traffic_lights,
std::shared_ptr<const RestrictionMap> restriction_map,
const std::vector<QueryNode> &node_info_list,
SpeedProfileProperties speed_profile);
SpeedProfileProperties speed_profile,
const util::NameTable &name_table);
void Run(const std::string &original_edge_data_filename,
lua_State *lua_state,
@ -106,6 +108,8 @@ class EdgeBasedGraphFactory
SpeedProfileProperties speed_profile;
const util::NameTable &name_table;
void CompressGeometry();
unsigned RenumberEdges();
void GenerateEdgeExpandedNodes();

View File

@ -12,9 +12,12 @@
#include "extractor/guidance/classification_data.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include <algorithm>
#include <map>
#include <cmath>
#include <cstdint>
#include <string>
#include <iostream>
namespace osrm
{
@ -331,6 +334,73 @@ inline bool isDistinct(const DirectionModifier first, const DirectionModifier se
return true;
}
inline bool requiresNameAnnounced(const std::string &from, const std::string &to)
{
// 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 = "", from_ref = "", to_name = "", to_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)
{
out_name = name.substr(0, ref_begin);
out_ref = name.substr(ref_begin + 1, name.find_first_of(')') - 1);
}
else
{
out_name = name;
out_ref = "";
}
};
split(from, from_name, from_ref);
split(to, to_name, to_ref);
// check similarity of names
if (from_name != "" && to_name != "")
{
if ((from_name.back() >= '0' && from_name.back() <= '9') ||
(to_name.back() >= '0' && to_name.back() <= '9'))
return from_name != to_name;
if (from.find("Weg ") == 0 && to_name.find("Weg ") == 0)
return from_name != to_name;
auto from_itr = from_name.begin();
auto to_itr = to_name.begin();
for (; from_itr != from_name.end() && to_itr != to_name.end() && *from_itr == *to_itr;
++to_itr, ++from_itr)
{
/* do nothing */
}
const auto common_length = std::distance(from_name.begin(), from_itr);
return (100 * common_length / std::min(to_name.length(), from_name.length())) < 80;
}
else if (from_ref != "" && to_ref != "")
{
// references are contained in one another
if (from_ref.find(to_ref) != std::string::npos ||
to_ref.find(from_ref) != std::string::npos)
return false;
}
return true;
}
inline int getPriority( const FunctionalRoadClass road_class )
{
const constexpr int road_priority[] = {10, 0, 10, 2, 10, 4, 10, 6, 10, 8, 10, 11, 10, 12, 10, 14};
return road_priority[static_cast<int>(road_class)];
}
inline bool canBeSeenAsFork(const FunctionalRoadClass first, const FunctionalRoadClass second)
{
// forks require similar road categories
return std::abs(getPriority(first) - getPriority(second)) <= 1;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -6,6 +6,8 @@
#include "extractor/restriction_map.hpp"
#include "extractor/compressed_edge_container.hpp"
#include "util/name_table.hpp"
#include <cstdint>
#include <string>
@ -53,7 +55,8 @@ class TurnAnalysis
const std::vector<QueryNode> &node_info_list,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const CompressedEdgeContainer &compressed_edge_container);
const CompressedEdgeContainer &compressed_edge_container,
const util::NameTable &name_table);
// the entry into the turn analysis
std::vector<TurnCandidate> getTurns(const NodeID from_node, const EdgeID via_eid) const;
@ -64,6 +67,7 @@ class TurnAnalysis
const RestrictionMap &restriction_map;
const std::unordered_set<NodeID> &barrier_nodes;
const CompressedEdgeContainer &compressed_edge_container;
const util::NameTable &name_table;
// Check for restrictions/barriers and generate a list of valid and invalid turns present at the
// node reached

View File

@ -0,0 +1,26 @@
#ifndef OSRM_UTIL_NAME_TABLE_HPP
#define OSRM_UTIL_NAME_TABLE_HPP
#include "util/shared_memory_vector_wrapper.hpp"
#include "util/range_table.hpp"
#include <string>
namespace osrm
{
namespace util
{
class NameTable
{
private:
//FIXME should this use shared memory
RangeTable<16, false> m_name_table;
ShM<char, false>::vector m_names_char_list;
public:
NameTable( const std::string &filename );
std::string get_name_for_id(const unsigned name_id) const;
};
} // namespace util
} // namespace osrm
#endif // OSRM_UTIL_NAME_TABLE_HPP

View File

@ -28,17 +28,23 @@ namespace json
namespace detail
{
const constexpr char *modifier_names[] = {"uturn", "sharp right", "right", "slight right",
"straight", "slight left", "left", "sharp left"};
const constexpr char *modifier_names[] = {"uturn",
"sharp right",
"right",
"slight right",
"straight",
"slight left",
"left",
"sharp left"};
// translations of TurnTypes. Not all types are exposed to the outside world.
// invalid types should never be returned as part of the API
const constexpr char *turn_type_names[] = {
"invalid", "no turn", "invalid", "new name", "continue", "turn",
"turn", "turn", "turn", "merge", "ramp", "ramp",
"ramp", "ramp", "fork", "end of road", "roundabout", "invalid",
"roundabout", "invalid", "traffic circle", "invalid", "traffic circle", "invalid",
"invalid", "restriction", "notification"};
"turn", "turn", "turn", "turn", "merge", "ramp",
"ramp", "ramp", "ramp", "ramp", "fork", "end of road",
"roundabout", "invalid", "roundabout", "invalid", "traffic circle", "invalid",
"traffic circle", "invalid", "invalid", "restriction", "notification"};
const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"};
// Check whether to include a modifier in the result of the API
@ -50,23 +56,6 @@ inline bool isValidModifier(const guidance::StepManeuver maneuver)
return true;
}
inline bool isMultiTurn(const TurnType type)
{
return (type == TurnType::FirstTurn || type == TurnType::SecondTurn ||
type == TurnType::ThirdTurn);
}
inline std::string getCount(const TurnType type)
{
if (type == TurnType::FirstTurn)
return "1";
if (type == TurnType::SecondTurn)
return "2";
if (type == TurnType::ThirdTurn)
return "3";
return "0";
}
std::string instructionTypeToString(const TurnType type)
{
return turn_type_names[static_cast<std::size_t>(type)];
@ -152,8 +141,6 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
else
step_maneuver.values["type"] = detail::waypointTypeToString(maneuver.waypoint_type);
if (detail::isMultiTurn(maneuver.instruction.type))
step_maneuver.values["count"] = detail::getCount(maneuver.instruction.type);
if (detail::isValidModifier(maneuver))
step_maneuver.values["modifier"] =
detail::instructionModifierToString(maneuver.instruction.direction_modifier);

View File

@ -35,12 +35,13 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory(
const std::unordered_set<NodeID> &traffic_lights,
std::shared_ptr<const RestrictionMap> restriction_map,
const std::vector<QueryNode> &node_info_list,
SpeedProfileProperties speed_profile)
SpeedProfileProperties speed_profile,
const util::NameTable &name_table)
: m_max_edge_id(0), m_node_info_list(node_info_list),
m_node_based_graph(std::move(node_based_graph)),
m_restriction_map(std::move(restriction_map)), m_barrier_nodes(barrier_nodes),
m_traffic_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container),
speed_profile(std::move(speed_profile))
speed_profile(std::move(speed_profile)), name_table(name_table)
{
}
@ -124,10 +125,9 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeI
// traverse arrays from start and end respectively
for (const auto i : util::irange(0UL, geometry_size))
{
BOOST_ASSERT(
current_edge_source_coordinate_id ==
m_compressed_edge_container.GetBucketReference(edge_id_2)[geometry_size - 1 - i]
.node_id);
BOOST_ASSERT(current_edge_source_coordinate_id ==
m_compressed_edge_container.GetBucketReference(
edge_id_2)[geometry_size - 1 - i].node_id);
const NodeID current_edge_target_coordinate_id = forward_geometry[i].node_id;
BOOST_ASSERT(current_edge_target_coordinate_id != current_edge_source_coordinate_id);
@ -305,8 +305,8 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
// Three nested loop look super-linear, but we are dealing with a (kind of)
// linear number of turns only.
util::Percent progress(m_node_based_graph->GetNumberOfNodes());
guidance::TurnAnalysis turn_analysis( *m_node_based_graph, m_node_info_list,
*m_restriction_map, m_barrier_nodes, m_compressed_edge_container );
guidance::TurnAnalysis turn_analysis(*m_node_based_graph, m_node_info_list, *m_restriction_map,
m_barrier_nodes, m_compressed_edge_container, name_table);
for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes()))
{
// progress.printStatus(node_u);

View File

@ -15,6 +15,7 @@
#include "util/timing_util.hpp"
#include "util/lua_util.hpp"
#include "util/graph_loader.hpp"
#include "util/name_table.hpp"
#include "util/typedefs.hpp"
@ -521,10 +522,12 @@ Extractor::BuildEdgeExpandedGraph(std::vector<QueryNode> &internal_to_external_n
compressed_edge_container.SerializeInternalVector(config.geometry_output_path);
util::NameTable name_table(config.names_file_name);
EdgeBasedGraphFactory edge_based_graph_factory(
node_based_graph, compressed_edge_container, barrier_nodes, traffic_lights,
std::const_pointer_cast<RestrictionMap const>(restriction_map),
internal_to_external_node_map, speed_profile);
internal_to_external_node_map, speed_profile, name_table);
edge_based_graph_factory.Run(config.edge_output_path, lua_state,
config.edge_segment_lookup_path, config.edge_penalty_path,

View File

@ -55,10 +55,11 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
const std::vector<QueryNode> &node_info_list,
const RestrictionMap &restriction_map,
const std::unordered_set<NodeID> &barrier_nodes,
const CompressedEdgeContainer &compressed_edge_container)
const CompressedEdgeContainer &compressed_edge_container,
const util::NameTable &name_table)
: node_based_graph(node_based_graph), node_info_list(node_info_list),
restriction_map(restriction_map), barrier_nodes(barrier_nodes),
compressed_edge_container(compressed_edge_container)
compressed_edge_container(compressed_edge_container), name_table(name_table)
{
}
@ -784,7 +785,9 @@ TurnInstruction TurnAnalysis::getInstructionForObvious(const std::size_t num_can
{
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
const auto &out_data = node_based_graph.GetEdgeData(candidate.eid);
if (in_data.name_id != out_data.name_id)
if (in_data.name_id != out_data.name_id &&
requiresNameAnnounced(name_table.get_name_for_id(in_data.name_id),
name_table.get_name_for_id(out_data.name_id)))
return {TurnType::NewName, getTurnDirection(candidate.angle)};
else
return {TurnType::Suppressed, getTurnDirection(candidate.angle)};
@ -859,6 +862,7 @@ std::vector<TurnCandidate> TurnAnalysis::handleThreeWayTurn(
angularDeviation(turn.angle, STRAIGHT_ANGLE) >
1.4);
};
/* Two nearly straight turns -> FORK
OOOOOOO
/
@ -871,7 +875,26 @@ std::vector<TurnCandidate> TurnAnalysis::handleThreeWayTurn(
{
if (turn_candidates[1].valid && turn_candidates[2].valid)
{
const auto left_class =
node_based_graph.GetEdgeData(turn_candidates[2].eid).road_classification.road_class;
const auto right_class =
node_based_graph.GetEdgeData(turn_candidates[1].eid).road_classification.road_class;
if (canBeSeenAsFork(left_class, right_class))
assignFork(via_edge, turn_candidates[2], turn_candidates[1]);
else if (getPriority(left_class) > getPriority(right_class))
{
turn_candidates[1].instruction =
getInstructionForObvious(turn_candidates.size(), via_edge, turn_candidates[1]);
turn_candidates[2].instruction = {findBasicTurnType(via_edge, turn_candidates[2]),
DirectionModifier::SlightLeft};
}
else
{
turn_candidates[2].instruction =
getInstructionForObvious(turn_candidates.size(), via_edge, turn_candidates[2]);
turn_candidates[1].instruction = {findBasicTurnType(via_edge, turn_candidates[1]),
DirectionModifier::SlightRight};
}
}
else
{
@ -1073,7 +1096,27 @@ void TurnAnalysis::handleDistinctConflict(const EdgeID via_edge,
if (getTurnDirection(left.angle) == DirectionModifier::Straight ||
getTurnDirection(left.angle) == DirectionModifier::SlightLeft ||
getTurnDirection(right.angle) == DirectionModifier::SlightRight)
{
const auto left_class =
node_based_graph.GetEdgeData(left.eid).road_classification.road_class;
const auto right_class =
node_based_graph.GetEdgeData(right.eid).road_classification.road_class;
if (canBeSeenAsFork(left_class, right_class))
assignFork(via_edge, left, right);
else if (getPriority(left_class) > getPriority(right_class))
{
// FIXME this should possibly know about the actual candidates?
right.instruction = getInstructionForObvious(4, via_edge, right);
left.instruction = {findBasicTurnType(via_edge, left), DirectionModifier::SlightLeft};
}
else
{
// FIXME this should possibly know about the actual candidates?
left.instruction = getInstructionForObvious(4, via_edge, left);
right.instruction = {findBasicTurnType(via_edge, right),
DirectionModifier::SlightRight};
}
}
const auto left_type = findBasicTurnType(via_edge, left);
const auto right_type = findBasicTurnType(via_edge, right);
@ -1196,8 +1239,27 @@ std::vector<TurnCandidate> TurnAnalysis::handleComplexTurn(
{
if (fork_range.second - fork_range.first == 1)
{
assignFork(via_edge, turn_candidates[fork_range.second],
turn_candidates[fork_range.first]);
auto &left = turn_candidates[fork_range.second];
auto &right = turn_candidates[fork_range.first];
const auto left_class =
node_based_graph.GetEdgeData(left.eid).road_classification.road_class;
const auto right_class =
node_based_graph.GetEdgeData(right.eid).road_classification.road_class;
if (canBeSeenAsFork(left_class, right_class))
assignFork(via_edge, left, right);
else if (getPriority(left_class) > getPriority(right_class))
{
right.instruction =
getInstructionForObvious(turn_candidates.size(), via_edge, right);
left.instruction = {findBasicTurnType(via_edge, left),
DirectionModifier::SlightLeft};
}
else
{
left.instruction = getInstructionForObvious(turn_candidates.size(), via_edge, left);
right.instruction = {findBasicTurnType(via_edge, right),
DirectionModifier::SlightRight};
}
}
else if (fork_range.second - fork_range.second == 2)
{

52
src/util/name_table.cpp Normal file
View File

@ -0,0 +1,52 @@
#include "util/name_table.hpp"
#include "util/simple_logger.hpp"
#include <algorithm>
#include <limits>
#include <fstream>
#include <boost/filesystem/fstream.hpp>
namespace osrm
{
namespace util
{
NameTable::NameTable(const std::string &filename)
{
boost::filesystem::ifstream name_stream(filename, std::ios::binary);
name_stream >> m_name_table;
unsigned number_of_chars = 0;
name_stream.read(reinterpret_cast<char *>(&number_of_chars), sizeof(number_of_chars));
BOOST_ASSERT_MSG(0 != number_of_chars, "name file broken");
m_names_char_list.resize(number_of_chars + 1); //+1 gives sentinel element
name_stream.read(reinterpret_cast<char *>(&m_names_char_list[0]),
number_of_chars * sizeof(m_names_char_list[0]));
if (0 == m_names_char_list.size())
{
util::SimpleLogger().Write(logWARNING) << "list of street names is empty";
}
}
std::string NameTable::get_name_for_id(const unsigned name_id) const
{
if (std::numeric_limits<unsigned>::max() == name_id)
{
return "";
}
auto range = m_name_table.GetRange(name_id);
std::string result;
result.reserve(range.size());
if (range.begin() != range.end())
{
result.resize(range.back() - range.front() + 1);
std::copy(m_names_char_list.begin() + range.front(),
m_names_char_list.begin() + range.back() + 1, result.begin());
}
return result;
}
} // namespace util
} // namespace osrm