diff --git a/features/guidance/motorway.feature b/features/guidance/motorway.feature new file mode 100644 index 00000000000..bcde32271ba --- /dev/null +++ b/features/guidance/motorway.feature @@ -0,0 +1,202 @@ +@routing @guidance +Feature: Basic Roundabout + + Background: + Given the profile "testbot" + Given a grid size of 10 meters + + Scenario: Ramp Exit Right + Given the node map + | a | b | c | d | e | + | | | | f | g | + + And the ways + | nodes | highway | + | abcde | motorway | + | bfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde, abcde | depart, arrive | + | a,g | abcde, bfg, bfg | depart, ramp-slight-right, arrive | + + Scenario: Ramp Exit Right Curved Right + Given the node map + | a | b | c | | | + | | | f | d | | + | | | | g | e | + + And the ways + | nodes | highway | + | abcde | motorway | + | bfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde, abcde | depart, arrive | + | a,g | abcde, bfg, bfg | depart, ramp-slight-right, arrive | + + Scenario: Ramp Exit Right Curved Left + Given the node map + | | | | | e | + | | | | d | g | + | a | b | c | f | | + + + And the ways + | nodes | highway | + | abcde | motorway | + | cfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde, abcde | depart, arrive | + | a,g | abcde, cfg, cfg | depart, ramp-slight-right, arrive | + + + Scenario: Ramp Exit Left + Given the node map + | | | | f | g | + | a | b | c | d | e | + + And the ways + | nodes | highway | + | abcde | motorway | + | bfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde, abcde | depart, arrive | + | a,g | abcde, bfg, bfg | depart, ramp-slight-left, arrive | + + Scenario: Ramp Exit Left Curved Left + Given the node map + | | | | g | e | + | | | f | d | | + | a | b | c | | | + + And the ways + | nodes | highway | + | abcde | motorway | + | bfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde, abcde | depart, arrive | + | a,g | abcde, bfg, bfg | depart, ramp-slight-left, arrive | + + Scenario: Ramp Exit Left Curved Right + Given the node map + | a | b | c | f | | + | | | | d | g | + | | | | | e | + + And the ways + | nodes | highway | + | abcde | motorway | + | cfg | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde, abcde | depart, arrive | + | a,g | abcde, cfg, cfg | depart, ramp-slight-left, arrive | + + Scenario: On Ramp Right + Given the node map + | a | b | c | d | e | + | f | g | | | | + + And the ways + | nodes | highway | + | abcde | motorway | + | fgd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde, abcde | depart, arrive | + | f,e | abcde, fgd, fgd | depart, merge-slight-left, arrive | + + Scenario: On Ramp Left + Given the node map + | f | g | | | | + | a | b | c | d | e | + + And the ways + | nodes | highway | + | abcde | motorway | + | fgd | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde, abcde | depart, arrive | + | f,e | abcde, fgd, fgd | depart, merge-slight-right, arrive | + + Scenario: Highway Fork + Given the node map + | | | | | d | e | + | a | b | c | | | | + | | | | | f | g | + + And the ways + | nodes | highway | + | abcde | motorway | + | cfg | motorway | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde, abcde, abcde | depart, fork-left, arrive | + | a,g | abcde, cfg, cfg | depart, fork-right, arrive | + + Scenario: Fork After Ramp + Given the node map + | | | | | d | e | + | a | b | c | | | | + | | | | | f | g | + + And the ways + | nodes | highway | + | abc | motorway_link | + | cde | motorway | + | cfg | motorway | + + When I route I should get + | waypoints | route | turns | + | a,e | abc, cde, cde | depart, fork-left, arrive | + | a,g | abc, cfg, cfg | depart, fork-right, arrive | + + Scenario: On And Off Ramp Right + Given the node map + | a | b | | c | | d | e | + | f | g | | | | h | i | + + And the ways + | nodes | highway | + | abcde | motorway | + | fgc | motorway_link | + | chi | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde, abcde | depart, arrive | + | f,e | fgc, abcde, abcde | depart, merge-slight-left, arrive | + | a,i | abcde, chi, chi | depart, ramp-slight-right, arrive | + | f,i | fgc, chi, chi | depart, turn-slight-right, arrive | + + Scenario: On And Off Ramp Left + Given the node map + | f | g | | | | h | i | + | a | b | | c | | d | e | + + And the ways + | nodes | highway | + | abcde | motorway | + | fgc | motorway_link | + | chi | motorway_link | + + When I route I should get + | waypoints | route | turns | + | a,e | abcde, abcde | depart, arrive | + | f,e | fgc, abcde, abcde | depart, merge-slight-right, arrive | + | a,i | abcde, chi, chi | depart, ramp-slight-left, arrive | + | f,i | fgc, chi, chi | depart, turn-slight-left, arrive | + diff --git a/features/guidance/roundabout.feature b/features/guidance/roundabout.feature new file mode 100644 index 00000000000..f29ef8cdfd2 --- /dev/null +++ b/features/guidance/roundabout.feature @@ -0,0 +1,173 @@ +@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 | no | + | cd | no | + | ef | no | + | gh | no | + | bcegb | yes | + + 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 | no | + | cd | no | + | ef | no | + | gh | no | + | bcegb | yes | + + 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 | no | + | cd | no | + | ef | no | + | gh | no | + | bcegb | yes | + + 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 | no | + | cd | no | + | ef | no | + | gh | no | + | bcegb | yes | + + 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 + Given the node map + | | a | | c | | + | l | | b | | d | + | | k | | e | | + | j | | h | | f | + | | i | | g | | + + And the ways + | nodes | roundabout | oneway | + | abc | no | yes | + | def | no | yes | + | ghi | no | yes | + | jkl | no | yes | + | behkb | yes | yes | + + When I route I should get + | waypoints | route | turns | + | a,c | abc,abc,abc | depart, roundabout-exit-1, arrive | + | a,f | abc,def,def | depart, roundabout-exit-2, arrive | + | a,i | abc,ghi,ghi | depart, roundabout-exit-3, arrive | + | a,l | abc,jkl,jkl | depart, roundabout-exit-4, arrive | + | d,f | def,def,def | depart, roundabout-exit-1, arrive | + | d,i | def,ghi,ghi | depart, roundabout-exit-2, arrive | + | d,l | def,jkl,jkl | depart, roundabout-exit-3, arrive | + | d,c | def,abc,abc | depart, roundabout-exit-4, arrive | + | g,i | ghi,ghi,ghi | depart, roundabout-exit-1, arrive | + | g,l | ghi,jkl,jkl | depart, roundabout-exit-2, arrive | + | g,c | ghi,abc,abc | depart, roundabout-exit-3, arrive | + | g,f | ghi,edf,edf | depart, roundabout-exit-4, arrive | + | j,l | jkl,jkl,jkl | depart, roundabout-exit-1, arrive | + | j,c | jkl,abc,abc | depart, roundabout-exit-2, arrive | + | j,f | jkl,def,def | depart, roundabout-exit-3, arrive | + | j,i | jkl,ghi,ghi | depart, roundabout-exit-4, arrive | diff --git a/include/engine/api/base_api.hpp b/include/engine/api/base_api.hpp index a16b931fa94..48ad4200455 100644 --- a/include/engine/api/base_api.hpp +++ b/include/engine/api/base_api.hpp @@ -48,7 +48,7 @@ class BaseAPI protected: util::json::Object MakeWaypoint(const PhantomNode &phantom) const { - return json::makeWaypoint(phantom.location, facade.get_name_for_id(phantom.name_id), + return json::makeWaypoint(phantom.location, facade.GetNameForID(phantom.name_id), Hint{phantom, facade.GetCheckSum()}); } diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index 743ed8b3fca..cd45be71cc9 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -138,7 +138,7 @@ class BaseDataFacade virtual unsigned GetNameIndexFromEdgeID(const unsigned id) const = 0; - virtual std::string get_name_for_id(const unsigned name_id) const = 0; + virtual std::string GetNameForID(const unsigned name_id) const = 0; virtual std::size_t GetCoreSize() const = 0; diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp index 4137555cc92..3c0c84d7cc9 100644 --- a/include/engine/datafacade/internal_datafacade.hpp +++ b/include/engine/datafacade/internal_datafacade.hpp @@ -551,7 +551,7 @@ class InternalDataFacade final : public BaseDataFacade return m_name_ID_list.at(id); } - std::string get_name_for_id(const unsigned name_id) const override final + std::string GetNameForID(const unsigned name_id) const override final { if (std::numeric_limits::max() == name_id) { diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp index 423592b25d1..6838c63eff4 100644 --- a/include/engine/datafacade/shared_datafacade.hpp +++ b/include/engine/datafacade/shared_datafacade.hpp @@ -621,7 +621,7 @@ class SharedDataFacade final : public BaseDataFacade return m_name_ID_list.at(id); } - std::string get_name_for_id(const unsigned name_id) const override final + std::string GetNameForID(const unsigned name_id) const override final { if (std::numeric_limits::max() == name_id) { diff --git a/include/engine/guidance/assemble_leg.hpp b/include/engine/guidance/assemble_leg.hpp index 6f0b168c0c2..90fa583ac28 100644 --- a/include/engine/guidance/assemble_leg.hpp +++ b/include/engine/guidance/assemble_leg.hpp @@ -139,12 +139,12 @@ RouteLeg assembleLeg(const DataFacadeT &facade, BOOST_ASSERT(summary_array.begin() != summary_array.end()); std::string summary = std::accumulate(std::next(summary_array.begin()), summary_array.end(), - facade.get_name_for_id(summary_array.front()), + facade.GetNameForID(summary_array.front()), [&facade](std::string previous, const std::uint32_t name_id) { if (name_id != 0) { - previous += ", " + facade.get_name_for_id(name_id); + previous += ", " + facade.GetNameForID(name_id); } return previous; }); diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp index 9a7b0e5fa8d..e32c00a9151 100644 --- a/include/engine/guidance/assemble_steps.hpp +++ b/include/engine/guidance/assemble_steps.hpp @@ -92,7 +92,7 @@ std::vector assembleSteps(const DataFacadeT &facade, if (path_point.turn_instruction != extractor::guidance::TurnInstruction::NO_TURN()) { BOOST_ASSERT(segment_duration >= 0); - const auto name = facade.get_name_for_id(path_point.name_id); + const auto name = facade.GetNameForID(path_point.name_id); const auto distance = leg_geometry.segment_distances[segment_index]; steps.push_back(RouteStep{path_point.name_id, name, @@ -113,7 +113,7 @@ std::vector assembleSteps(const DataFacadeT &facade, const int duration = segment_duration + target_duration; BOOST_ASSERT(duration >= 0); steps.push_back(RouteStep{target_node.name_id, - facade.get_name_for_id(target_node.name_id), + facade.GetNameForID(target_node.name_id), duration / 10., distance, target_mode, @@ -141,7 +141,7 @@ std::vector assembleSteps(const DataFacadeT &facade, BOOST_ASSERT(duration >= 0); steps.push_back(RouteStep{source_node.name_id, - facade.get_name_for_id(source_node.name_id), + facade.GetNameForID(source_node.name_id), duration / 10., leg_geometry.segment_distances[segment_index], source_mode, @@ -164,7 +164,7 @@ std::vector assembleSteps(const DataFacadeT &facade, // This step has length zero, the only reason we need it is the target location steps.push_back( RouteStep{target_node.name_id, - facade.get_name_for_id(target_node.name_id), + facade.GetNameForID(target_node.name_id), ZERO_DURACTION, ZERO_DISTANCE, target_mode, diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp index ede4deb024d..a6765dc67e5 100644 --- a/include/extractor/edge_based_graph_factory.hpp +++ b/include/extractor/edge_based_graph_factory.hpp @@ -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 #include @@ -51,7 +52,8 @@ class EdgeBasedGraphFactory const std::unordered_set &traffic_lights, std::shared_ptr restriction_map, const std::vector &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(); diff --git a/include/extractor/guidance/toolkit.hpp b/include/extractor/guidance/toolkit.hpp index 3dcff509d03..45ca897ccbf 100644 --- a/include/extractor/guidance/toolkit.hpp +++ b/include/extractor/guidance/toolkit.hpp @@ -12,9 +12,11 @@ #include "extractor/guidance/classification_data.hpp" #include "extractor/guidance/turn_instruction.hpp" +#include #include #include #include +#include namespace osrm { @@ -249,10 +251,11 @@ inline double angularDeviation(const double angle, const double from) return std::min(360 - deviation, deviation); } -inline double getAngularPenalty(const double angle, TurnInstruction instruction) +inline double getAngularPenalty(const double angle, DirectionModifier modifier) { + // these are not aligned with getTurnDirection but represent an ideal center const double center[] = {0, 45, 90, 135, 180, 225, 270, 315}; - return angularDeviation(center[static_cast(instruction.direction_modifier)], angle); + return angularDeviation(center[static_cast(modifier)], angle); } inline double getTurnConfidence(const double angle, TurnInstruction instruction) @@ -262,8 +265,8 @@ inline double getTurnConfidence(const double angle, TurnInstruction instruction) if (!isBasic(instruction.type) || instruction.direction_modifier == DirectionModifier::UTurn) return 1.0; - const double deviations[] = {0, 45, 50, 35, 10, 35, 50, 45}; - const double difference = getAngularPenalty(angle, instruction); + const double deviations[] = {0, 45, 50, 30, 20, 30, 50, 45}; + const double difference = getAngularPenalty(angle, instruction.direction_modifier); const double max_deviation = deviations[static_cast(instruction.direction_modifier)]; return 1.0 - (difference / max_deviation) * (difference / max_deviation); } @@ -281,7 +284,7 @@ inline DirectionModifier getTurnDirection(const double angle) return DirectionModifier::Right; if (angle >= 140 && angle < 170) return DirectionModifier::SlightRight; - if (angle >= 170 && angle <= 190) + if (angle >= 165 && angle <= 195) return DirectionModifier::Straight; if (angle > 190 && angle <= 220) return DirectionModifier::SlightLeft; @@ -295,10 +298,14 @@ inline DirectionModifier getTurnDirection(const double angle) // swaps left <-> right modifier types inline DirectionModifier mirrorDirectionModifier(const DirectionModifier modifier) { - const constexpr DirectionModifier results[] = { - DirectionModifier::UTurn, DirectionModifier::SharpLeft, DirectionModifier::Left, - DirectionModifier::SlightLeft, DirectionModifier::Straight, DirectionModifier::SlightRight, - DirectionModifier::Right, DirectionModifier::SharpRight}; + const constexpr DirectionModifier results[] = {DirectionModifier::UTurn, + DirectionModifier::SharpLeft, + DirectionModifier::Left, + DirectionModifier::SlightLeft, + DirectionModifier::Straight, + DirectionModifier::SlightRight, + DirectionModifier::Right, + DirectionModifier::SharpRight}; return results[modifier]; } @@ -315,6 +322,81 @@ inline bool isLowPriorityRoadClass(const FunctionalRoadClass road_class) road_class == FunctionalRoadClass::SERVICE; } +inline bool isDistinct(const DirectionModifier first, const DirectionModifier second) +{ + if ((first + 1) % detail::num_direction_modifiers == second) + return false; + + if ((second + 1) % detail::num_direction_modifiers == first) + return false; + + 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; + 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) + { + out_name = name.substr(0, ref_begin); + out_ref = name.substr(ref_begin + 1, name.find_first_of(')') - 1); + } + else + { + out_name = name; + } + }; + + split(from, from_name, from_ref); + split(to, to_name, to_ref); + + // check similarity of names + auto names_are_empty = from_name.empty() && to_name.empty(); + auto names_are_equal = from_name == to_name; + auto name_is_removed = !from_name.empty() && to_name.empty(); + // references are contained in one another + auto refs_are_empty = from_ref.empty() && to_ref.empty(); + 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); + auto ref_is_removed = !from_ref.empty() && to_ref.empty(); + + auto obvious_change = ref_is_contained || names_are_equal || + (names_are_empty && refs_are_empty) || name_is_removed || ref_is_removed; + + return !obvious_change; +} + +inline int getPriority( const FunctionalRoadClass road_class ) +{ + //The road priorities indicate which roads can bee seen as more or less equal. + //They are used in Fork-Discovery. Possibly should be moved to profiles post v5? + //A fork can happen between road types that are at most 1 priority apart from each other + 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(road_class)]; +} + +inline bool canBeSeenAsFork(const FunctionalRoadClass first, const FunctionalRoadClass second) +{ + // forks require similar road categories + // Based on the priorities assigned above, we can set forks only if the road priorities match closely. + // Potentially we could include features like number of lanes here and others? + // Should also be moved to profiles + return std::abs(getPriority(first) - getPriority(second)) <= 1; +} + } // namespace guidance } // namespace extractor } // namespace osrm diff --git a/include/extractor/guidance/turn_analysis.hpp b/include/extractor/guidance/turn_analysis.hpp index 628ded03d67..538fabc1353 100644 --- a/include/extractor/guidance/turn_analysis.hpp +++ b/include/extractor/guidance/turn_analysis.hpp @@ -6,11 +6,14 @@ #include "extractor/restriction_map.hpp" #include "extractor/compressed_edge_container.hpp" +#include "util/name_table.hpp" + #include #include #include #include +#include #include namespace osrm @@ -20,42 +23,59 @@ namespace extractor namespace guidance { -struct TurnCandidate +// What is exposed to the outside +struct TurnOperation final +{ + EdgeID eid; + double angle; + TurnInstruction instruction; +}; + +// For the turn analysis, we require a full list of all connected roads to determine the outcome. +// Invalid turns can influence the perceived angles +// +// aaa(2)aa +// a - bbbbb +// aaa(1)aa +// +// will not be perceived as a turn from (1) -> b, and as a U-turn from (1) -> (2). +// In addition, they can influence whether a turn is obvious or not. +struct ConnectedRoad final { - EdgeID eid; // the id of the arc - bool valid; // a turn may be relevant to good instructions, even if we cannot take the road - double angle; // the approximated angle of the turn - TurnInstruction instruction; // a proposed instruction - double confidence; // how close to the border is the turn? + ConnectedRoad(const TurnOperation turn, const bool entry_allowed = false); + + TurnOperation turn; + bool entry_allowed; // a turn may be relevant to good instructions, even if we cannot take + // the road std::string toString() const { - std::string result = "[turn] "; - result += std::to_string(eid); - result += " valid: "; - result += std::to_string(valid); + std::string result = "[connection] "; + result += std::to_string(turn.eid); + result += " allows entry: "; + result += std::to_string(entry_allowed); result += " angle: "; - result += std::to_string(angle); + result += std::to_string(turn.angle); result += " instruction: "; - result += std::to_string(static_cast(instruction.type)) + " " + - std::to_string(static_cast(instruction.direction_modifier)); - result += " confidence: "; - result += std::to_string(confidence); + result += std::to_string(static_cast(turn.instruction.type)) + " " + + std::to_string(static_cast(turn.instruction.direction_modifier)); return result; } }; class TurnAnalysis { + public: TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph, const std::vector &node_info_list, const RestrictionMap &restriction_map, const std::unordered_set &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 getTurns(const NodeID from_node, const EdgeID via_eid) const; + std::vector getTurns(const NodeID from_node, const EdgeID via_eid) const; private: const util::NodeBasedDynamicGraph &node_based_graph; @@ -63,12 +83,14 @@ class TurnAnalysis const RestrictionMap &restriction_map; const std::unordered_set &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 + // Check for restrictions/barriers and generate a list of valid and invalid turns present at + // the // node reached // from `from_node` via `via_eid` // The resulting candidates have to be analysed for their actual instructions later on. - std::vector getTurnCandidates(const NodeID from_node, + std::vector getConnectedRoads(const NodeID from_node, const EdgeID via_eid) const; // Merge segregated roads to omit invalid turns in favor of treating segregated roads as @@ -82,10 +104,7 @@ class TurnAnalysis // // The treatment results in a straight turn angle of 180ยบ rather than a turn angle of approx // 160 - std::vector - mergeSegregatedRoads(const NodeID from_node, - const EdgeID via_eid, - std::vector turn_candidates) const; + std::vector mergeSegregatedRoads(std::vector intersection) const; // TODO distinguish roundabouts and rotaries // TODO handle bike/walk cases that allow crossing a roundabout! @@ -93,110 +112,86 @@ class TurnAnalysis // Processing of roundabouts // Produces instructions to enter/exit a roundabout or to stay on it. // Performs the distinction between roundabout and rotaries. - std::vector handleRoundabouts(const NodeID from, - const EdgeID via_edge, + std::vector handleRoundabouts(const EdgeID via_edge, const bool on_roundabout, - const bool can_enter_roundabout, const bool can_exit_roundabout, - std::vector turn_candidates) const; + std::vector intersection) const; // Indicates a Junction containing a motoryway - bool isMotorwayJunction(const NodeID from, - const EdgeID via_edge, - const std::vector &turn_candidates) const; + bool isMotorwayJunction(const EdgeID via_edge, + const std::vector &intersection) const; // Decide whether a turn is a turn or a ramp access - TurnType findBasicTurnType(const EdgeID via_edge, const TurnCandidate &candidate) const; + TurnType findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const; // Get the Instruction for an obvious turn // Instruction will be a silent instruction TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates, const EdgeID via_edge, - const TurnCandidate &candidate) const; + const ConnectedRoad &candidate) const; // Helper Function that decides between NoTurn or NewName TurnInstruction - noTurnOrNewName(const NodeID from, const EdgeID via_edge, const TurnCandidate &candidate) const; + noTurnOrNewName(const NodeID from, const EdgeID via_edge, const ConnectedRoad &candidate) const; // Basic Turn Handling // Dead end. - std::vector handleOneWayTurn(const NodeID from, - const EdgeID via_edge, - std::vector turn_candidates) const; + std::vector handleOneWayTurn(std::vector intersection) const; // Mode Changes, new names... - std::vector handleTwoWayTurn(const NodeID from, - const EdgeID via_edge, - std::vector turn_candidates) const; + std::vector handleTwoWayTurn(const EdgeID via_edge, + std::vector intersection) const; // Forks, T intersections and similar - std::vector handleThreeWayTurn(const NodeID from, - const EdgeID via_edge, - std::vector turn_candidates) const; - - // Normal Intersection. Can still contain forks... - std::vector handleFourWayTurn(const NodeID from, - const EdgeID via_edge, - std::vector turn_candidates) const; + std::vector handleThreeWayTurn(const EdgeID via_edge, + std::vector intersection) const; - // Fallback for turns of high complexion - std::vector handleComplexTurn(const NodeID from, - const EdgeID via_edge, - std::vector turn_candidates) const; + // Handling of turns larger then degree three + std::vector handleComplexTurn(const EdgeID via_edge, + std::vector intersection) const; // Any Junction containing motorways - std::vector handleMotorwayJunction( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const; + std::vector handleMotorwayJunction( + const EdgeID via_edge, std::vector intersection) const; - std::vector handleFromMotorway( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const; + std::vector handleFromMotorway(const EdgeID via_edge, + std::vector intersection) const; - std::vector handleMotorwayRamp( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const; + std::vector handleMotorwayRamp(const EdgeID via_edge, + std::vector intersection) const; // Utility function, setting basic turn types. Prepares for normal turn handling. - std::vector setTurnTypes(const NodeID from, + std::vector setTurnTypes(const NodeID from, const EdgeID via_edge, - std::vector turn_candidates) const; - - // Utility function to handle direction modifier conflicts if reasonably possible - std::vector handleConflicts(const NodeID from, - const EdgeID via_edge, - std::vector turn_candidates) const; - - // Old fallbacks, to be removed - std::vector optimizeRamps(const EdgeID via_edge, - std::vector turn_candidates) const; - - std::vector optimizeCandidates(const EdgeID via_eid, - std::vector turn_candidates) const; - - bool isObviousChoice(const EdgeID via_eid, - const std::size_t turn_index, - const std::vector &turn_candidates) const; - - std::vector suppressTurns(const EdgeID via_eid, - std::vector turn_candidates) const; - - // node_u -- (edge_1) --> node_v -- (edge_2) --> node_w - TurnInstruction AnalyzeTurn(const NodeID node_u, - const EdgeID edge1, - const NodeID node_v, - const EdgeID edge2, - const NodeID node_w, - const double angle) const; + std::vector intersection) const; // Assignment of specific turn types - void assignFork(const EdgeID via_edge, TurnCandidate &left, TurnCandidate &right) const; + void assignFork(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const; void assignFork(const EdgeID via_edge, - TurnCandidate &left, - TurnCandidate ¢er, - TurnCandidate &right) const; - - //Type specific fallbacks - std::vector - fallbackTurnAssignmentMotorway(std::vector turn_candidates) const; + ConnectedRoad &left, + ConnectedRoad ¢er, + ConnectedRoad &right) const; + + void + handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const; + + // Type specific fallbacks + std::vector + fallbackTurnAssignmentMotorway(std::vector intersection) const; + + // Classification + std::size_t findObviousTurn(const EdgeID via_edge, + const std::vector &intersection) const; + std::pair + findFork(const std::vector &intersection) const; + + std::vector assignLeftTurns(const EdgeID via_edge, + std::vector intersection, + const std::size_t starting_at) const; + std::vector assignRightTurns(const EdgeID via_edge, + std::vector intersection, + const std::size_t up_to) const; }; // class TurnAnalysis diff --git a/include/extractor/guidance/turn_instruction.hpp b/include/extractor/guidance/turn_instruction.hpp index efa6dc80fa8..da3da62e867 100644 --- a/include/extractor/guidance/turn_instruction.hpp +++ b/include/extractor/guidance/turn_instruction.hpp @@ -42,8 +42,16 @@ enum TurnType // at the moment we can support 32 turn types, without increasing NewName, // no turn, but name changes Continue, // remain on a street Turn, // basic turn + FirstTurn, // First of x turns + SecondTurn, // Second of x turns + ThirdTurn, // Third of x turns + FourthTurn, // Fourth of x turns Merge, // merge onto a street Ramp, // special turn (highway ramp exits) + FirstRamp, // first turn onto a ramp + SecondRamp, // second turn onto a ramp + ThirdRamp, // third turn onto a ramp + FourthRamp, // fourth turn onto a ramp Fork, // fork road splitting up EndOfRoad, // T intersection EnterRoundabout, // Entering a small Roundabout diff --git a/include/util/name_table.hpp b/include/util/name_table.hpp new file mode 100644 index 00000000000..2f1887a355b --- /dev/null +++ b/include/util/name_table.hpp @@ -0,0 +1,31 @@ +#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 + +namespace osrm +{ +namespace util +{ + +// While this could, theoretically, hold any names in the fitting format, +// the NameTable allows access to a part of the Datafacade to allow +// processing based on name indices. +class NameTable +{ + private: + // FIXME should this use shared memory + RangeTable<16, false> m_name_table; + ShM::vector m_names_char_list; + + public: + NameTable(const std::string &filename); + std::string GetNameForID(const unsigned name_id) const; +}; +} // namespace util +} // namespace osrm + +#endif // OSRM_UTIL_NAME_TABLE_HPP diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp index d86d69d982e..85f8e847890 100644 --- a/src/engine/api/json_factory.cpp +++ b/src/engine/api/json_factory.cpp @@ -40,11 +40,11 @@ const constexpr char *modifier_names[] = {"uturn", // 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", - "merge", "ramp", "fork", "end of road", "roundabout", "invalid", - "roundabout", "invalid", "traffic circle", "invalid", "traffic circle", "invalid", - "invalid", "restriction", "notification"}; - + "invalid", "no turn", "invalid", "new name", "continue", "turn", + "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 diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index 5c262673647..4eb67bf1dec 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -34,12 +34,13 @@ EdgeBasedGraphFactory::EdgeBasedGraphFactory( const std::unordered_set &traffic_lights, std::shared_ptr restriction_map, const std::vector &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) { } @@ -123,10 +124,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); @@ -302,8 +302,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); @@ -315,15 +315,12 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( } ++node_based_edge_counter; - auto turn_candidates = turn_analysis.getTurns(node_u, edge_from_u); + auto possible_turns = turn_analysis.getTurns(node_u, edge_from_u); const NodeID node_v = m_node_based_graph->GetTarget(edge_from_u); - for (const auto turn : turn_candidates) + for (const auto turn : possible_turns) { - if (!turn.valid) - continue; - const double turn_angle = turn.angle; // only add an edge if turn is not prohibited diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index 7123bfce731..3e84fd30da9 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -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 &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(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, diff --git a/src/extractor/guidance/turn_analysis.cpp b/src/extractor/guidance/turn_analysis.cpp index 38fb1b80325..0b26c1ec7d5 100644 --- a/src/extractor/guidance/turn_analysis.cpp +++ b/src/extractor/guidance/turn_analysis.cpp @@ -15,16 +15,14 @@ namespace guidance { // configuration of turn classification const bool constexpr INVERT = true; -const bool constexpr RESOLVE_TO_RIGHT = true; -const bool constexpr RESOLVE_TO_LEFT = false; // what angle is interpreted as going straight const double constexpr STRAIGHT_ANGLE = 180.; // if a turn deviates this much from going straight, it will be kept straight -const double constexpr MAXIMAL_ALLOWED_NO_TURN_DEVIATION = 2.; +const double constexpr MAXIMAL_ALLOWED_NO_TURN_DEVIATION = 3.; // angle that lies between two nearly indistinguishable roads -const double constexpr NARROW_TURN_ANGLE = 35.; -const double constexpr WELL_DISTINCT_ANGLE = 50; +const double constexpr NARROW_TURN_ANGLE = 30.; +const double constexpr GROUP_ANGLE = 90; // angle difference that can be classified as straight, if its the only narrow turn const double constexpr FUZZY_ANGLE_DIFFERENCE = 15.; const double constexpr DISTINCTION_RATIO = 2; @@ -32,6 +30,11 @@ const unsigned constexpr INVALID_NAME_ID = 0; using EdgeData = util::NodeBasedDynamicGraph::EdgeData; +ConnectedRoad::ConnectedRoad(const TurnOperation turn, const bool entry_allowed) + : turn(turn), entry_allowed(entry_allowed) +{ +} + bool requiresAnnouncement(const EdgeData &from, const EdgeData &to) { return !from.IsCompatibleTo(to); @@ -57,16 +60,24 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph, const std::vector &node_info_list, const RestrictionMap &restriction_map, const std::unordered_set &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) { } +// some small tool functions to simplify decisions down the line namespace detail { +inline FunctionalRoadClass roadClass(const ConnectedRoad &road, + const util::NodeBasedDynamicGraph &graph) +{ + return graph.GetEdgeData(road.turn.eid).road_classification.road_class; +} + inline bool isMotorwayClass(FunctionalRoadClass road_class) { return road_class == FunctionalRoadClass::MOTORWAY || road_class == FunctionalRoadClass::TRUNK; @@ -84,11 +95,10 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base } // namespace detail -#define PRINT_DEBUG_CANDIDATES 0 -std::vector TurnAnalysis::getTurns(const NodeID from, const EdgeID via_edge) const +std::vector TurnAnalysis::getTurns(const NodeID from, const EdgeID via_edge) const { localizer.node_info_list = &node_info_list; - auto turn_candidates = getTurnCandidates(from, via_edge); + auto intersection = getConnectedRoads(from, via_edge); const auto &in_edge_data = node_based_graph.GetEdgeData(via_edge); @@ -96,9 +106,9 @@ std::vector TurnAnalysis::getTurns(const NodeID from, const EdgeI bool on_roundabout = in_edge_data.roundabout; bool can_enter_roundabout = false; bool can_exit_roundabout = false; - for (const auto &candidate : turn_candidates) + for (const auto &road : intersection) { - const auto &edge_data = node_based_graph.GetEdgeData(candidate.eid); + const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid); // only check actual outgoing edges if (edge_data.reversed) continue; @@ -114,88 +124,59 @@ std::vector TurnAnalysis::getTurns(const NodeID from, const EdgeI } if (on_roundabout || can_enter_roundabout) { - return handleRoundabouts(from, via_edge, on_roundabout, can_enter_roundabout, - can_exit_roundabout, std::move(turn_candidates)); - } - - // set initial defaults for normal turns and modifier based on angle - turn_candidates = setTurnTypes(from, via_edge, std::move(turn_candidates)); - - if (isMotorwayJunction(from, via_edge, turn_candidates)) - { - return handleMotorwayJunction(from, via_edge, std::move(turn_candidates)); + intersection = handleRoundabouts(via_edge, on_roundabout, can_exit_roundabout, + std::move(intersection)); } - - if (turn_candidates.size() <= 4) // TODO change when larger junctions are handled + else { - if (turn_candidates.size() == 1) + // set initial defaults for normal turns and modifier based on angle + intersection = setTurnTypes(from, via_edge, std::move(intersection)); + if (isMotorwayJunction(via_edge, intersection)) { - turn_candidates = handleOneWayTurn(from, via_edge, std::move(turn_candidates)); + intersection = handleMotorwayJunction(via_edge, std::move(intersection)); } - else if (turn_candidates.size() == 2) + + else if (intersection.size() == 1) { - turn_candidates = handleTwoWayTurn(from, via_edge, std::move(turn_candidates)); + intersection = handleOneWayTurn(std::move(intersection)); } - else if (turn_candidates.size() == 3) + else if (intersection.size() == 2) { - turn_candidates = handleThreeWayTurn(from, via_edge, std::move(turn_candidates)); + intersection = handleTwoWayTurn(via_edge, std::move(intersection)); } - else if (turn_candidates.size() == 4) + else if (intersection.size() == 3) { - turn_candidates = handleFourWayTurn(from, via_edge, std::move(turn_candidates)); + intersection = handleThreeWayTurn(via_edge, std::move(intersection)); } else { - turn_candidates = handleComplexTurn(from, via_edge, std::move(turn_candidates)); + intersection = handleComplexTurn(via_edge, std::move(intersection)); } // complex intersection, potentially requires conflict resolution - return handleConflicts(from, via_edge, std::move(turn_candidates)); - } - -#if PRINT_DEBUG_CANDIDATES - std::cout << "Initial Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << " name: " << node_based_graph.GetEdgeData(tc.eid).name_id << std::endl; -#endif - turn_candidates = optimizeCandidates(via_edge, std::move(turn_candidates)); -#if PRINT_DEBUG_CANDIDATES - std::cout << "Optimized Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << std::endl; -#endif - turn_candidates = suppressTurns(via_edge, std::move(turn_candidates)); -#if PRINT_DEBUG_CANDIDATES - std::cout << "Suppressed Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << std::endl; -#endif - return turn_candidates; + } + + std::vector turns; + for (auto road : intersection) + if (road.entry_allowed) + turns.emplace_back(road.turn); + + return turns; } -inline std::size_t countValid(const std::vector &turn_candidates) +inline std::size_t countValid(const std::vector &intersection) { - return std::count_if(turn_candidates.begin(), turn_candidates.end(), - [](const TurnCandidate &candidate) + return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) { - return candidate.valid; + return road.entry_allowed; }); } -std::vector -TurnAnalysis::handleRoundabouts(const NodeID from, - const EdgeID via_edge, +std::vector +TurnAnalysis::handleRoundabouts(const EdgeID via_edge, const bool on_roundabout, - const bool can_enter_roundabout, const bool can_exit_roundabout, - std::vector turn_candidates) const + std::vector intersection) const { - (void)from; // TODO requires differentiation between roundabouts and rotaries // detect via radius (get via circle through three vertices) NodeID node_v = node_based_graph.GetTarget(via_edge); @@ -203,151 +184,134 @@ TurnAnalysis::handleRoundabouts(const NodeID from, { // Shoule hopefully have only a single exit and continue // at least for cars. How about bikes? - for (auto &candidate : turn_candidates) + for (auto &road : intersection) { - const auto &out_data = node_based_graph.GetEdgeData(candidate.eid); + auto &turn = road.turn; + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); if (out_data.roundabout) { // TODO can forks happen in roundabouts? E.g. required lane changes if (1 == node_based_graph.GetDirectedOutDegree(node_v)) { // No turn possible. - candidate.instruction = TurnInstruction::NO_TURN(); + turn.instruction = TurnInstruction::NO_TURN(); } else { - candidate.instruction = - TurnInstruction::REMAIN_ROUNDABOUT(getTurnDirection(candidate.angle)); + turn.instruction = + TurnInstruction::REMAIN_ROUNDABOUT(getTurnDirection(turn.angle)); } } else { - candidate.instruction = - TurnInstruction::EXIT_ROUNDABOUT(getTurnDirection(candidate.angle)); + turn.instruction = TurnInstruction::EXIT_ROUNDABOUT(getTurnDirection(turn.angle)); } } -#if PRINT_DEBUG_CANDIDATES - std::cout << "On Roundabout Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << " name: " << node_based_graph.GetEdgeData(tc.eid).name_id << std::endl; -#endif - return turn_candidates; + return intersection; } else { - (void)can_enter_roundabout; - BOOST_ASSERT(can_enter_roundabout); - for (auto &candidate : turn_candidates) + for (auto &road : intersection) { - if (!candidate.valid) + if (!road.entry_allowed) continue; - const auto &out_data = node_based_graph.GetEdgeData(candidate.eid); + auto &turn = road.turn; + const auto &out_data = node_based_graph.GetEdgeData(turn.eid); if (out_data.roundabout) { - candidate.instruction = - TurnInstruction::ENTER_ROUNDABOUT(getTurnDirection(candidate.angle)); + turn.instruction = TurnInstruction::ENTER_ROUNDABOUT(getTurnDirection(turn.angle)); if (can_exit_roundabout) { - if (candidate.instruction.type == TurnType::EnterRotary) - candidate.instruction.type = TurnType::EnterRotaryAtExit; - if (candidate.instruction.type == TurnType::EnterRoundabout) - candidate.instruction.type = TurnType::EnterRoundaboutAtExit; + if (turn.instruction.type == TurnType::EnterRotary) + turn.instruction.type = TurnType::EnterRotaryAtExit; + if (turn.instruction.type == TurnType::EnterRoundabout) + turn.instruction.type = TurnType::EnterRoundaboutAtExit; } } else { - candidate.instruction = {TurnType::EnterAndExitRoundabout, - getTurnDirection(candidate.angle)}; + turn.instruction = {TurnType::EnterAndExitRoundabout, getTurnDirection(turn.angle)}; } } -#if PRINT_DEBUG_CANDIDATES - std::cout << "Into Roundabout Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << " name: " << node_based_graph.GetEdgeData(tc.eid).name_id << std::endl; -#endif - return turn_candidates; + return intersection; } } -std::vector -TurnAnalysis::fallbackTurnAssignmentMotorway(std::vector turn_candidates) const +std::vector +TurnAnalysis::fallbackTurnAssignmentMotorway(std::vector intersection) const { - for (auto &candidate : turn_candidates) + for (auto &road : intersection) { - const auto &out_data = node_based_graph.GetEdgeData(candidate.eid); + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); util::SimpleLogger().Write(logWARNING) - << "Candidate: " << candidate.toString() << " Name: " << out_data.name_id + << "road: " << road.toString() << " Name: " << out_data.name_id << " Road Class: " << (int)out_data.road_classification.road_class - << " At: " << localizer(node_based_graph.GetTarget(candidate.eid)); + << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid)); - if (!candidate.valid) + if (!road.entry_allowed) continue; const auto type = detail::isMotorwayClass(out_data.road_classification.road_class) ? TurnType::Merge : TurnType::Turn; - if (angularDeviation(candidate.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE) - candidate.instruction = {type, DirectionModifier::Straight}; + if (angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE) + road.turn.instruction = {type, DirectionModifier::Straight}; else { - candidate.instruction = {type, candidate.angle > STRAIGHT_ANGLE - ? DirectionModifier::SlightLeft - : DirectionModifier::SlightRight}; + road.turn.instruction = {type, + road.turn.angle > STRAIGHT_ANGLE + ? DirectionModifier::SlightLeft + : DirectionModifier::SlightRight}; } } - return turn_candidates; + return intersection; } -std::vector TurnAnalysis::handleFromMotorway( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const +std::vector TurnAnalysis::handleFromMotorway( + const EdgeID via_edge, std::vector intersection) const { - (void)from; const auto &in_data = node_based_graph.GetEdgeData(via_edge); BOOST_ASSERT(detail::isMotorwayClass(in_data.road_classification.road_class)); - const auto countExitingMotorways = [this](const std::vector &turn_candidates) + const auto countExitingMotorways = [this](const std::vector &intersection) { unsigned count = 0; - for (const auto &candidate : turn_candidates) + for (const auto &road : intersection) { - if (candidate.valid && detail::isMotorwayClass(candidate.eid, node_based_graph)) + if (road.entry_allowed && detail::isMotorwayClass(road.turn.eid, node_based_graph)) ++count; } return count; }; // find the angle that continues on our current highway - const auto getContinueAngle = [this, in_data](const std::vector &turn_candidates) + const auto getContinueAngle = [this, in_data](const std::vector &intersection) { - for (const auto &candidate : turn_candidates) + for (const auto &road : intersection) { - const auto &out_data = node_based_graph.GetEdgeData(candidate.eid); - if (candidate.angle != 0 && in_data.name_id == out_data.name_id && + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + if (road.turn.angle != 0 && in_data.name_id == out_data.name_id && in_data.name_id != 0 && detail::isMotorwayClass(out_data.road_classification.road_class)) - return candidate.angle; + return road.turn.angle; } - return turn_candidates[0].angle; + return intersection[0].turn.angle; }; - const auto getMostLikelyContinue = [this, - in_data](const std::vector &turn_candidates) + const auto getMostLikelyContinue = + [this, in_data](const std::vector &intersection) { - double angle = turn_candidates[0].angle; + double angle = intersection[0].turn.angle; double best = 180; - for (const auto &candidate : turn_candidates) + for (const auto &road : intersection) { - const auto &out_data = node_based_graph.GetEdgeData(candidate.eid); + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); if (detail::isMotorwayClass(out_data.road_classification.road_class) && - angularDeviation(candidate.angle, STRAIGHT_ANGLE) < best) + angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < best) { - best = angularDeviation(candidate.angle, STRAIGHT_ANGLE); - angle = candidate.angle; + best = angularDeviation(road.turn.angle, STRAIGHT_ANGLE); + angle = road.turn.angle; } } return angle; @@ -355,115 +319,123 @@ std::vector TurnAnalysis::handleFromMotorway( const auto findBestContinue = [&]() { - const double continue_angle = getContinueAngle(turn_candidates); - if (continue_angle != turn_candidates[0].angle) + const double continue_angle = getContinueAngle(intersection); + if (continue_angle != intersection[0].turn.angle) return continue_angle; else - return getMostLikelyContinue(turn_candidates); + return getMostLikelyContinue(intersection); }; // find continue angle const double continue_angle = findBestContinue(); // highway does not continue and has no obvious choice - if (continue_angle == turn_candidates[0].angle) + if (continue_angle == intersection[0].turn.angle) { - if (turn_candidates.size() == 2) + if (intersection.size() == 2) { // do not announce ramps at the end of a highway - turn_candidates[1].instruction = {TurnType::NoTurn, - getTurnDirection(turn_candidates[1].angle)}; + intersection[1].turn.instruction = {TurnType::NoTurn, + getTurnDirection(intersection[1].turn.angle)}; } - else if (turn_candidates.size() == 3) + else if (intersection.size() == 3) { // splitting ramp at the end of a highway - if (turn_candidates[1].valid && turn_candidates[2].valid) + if (intersection[1].entry_allowed && intersection[2].entry_allowed) { - assignFork(via_edge, turn_candidates[2], turn_candidates[1]); + assignFork(via_edge, intersection[2], intersection[1]); } else { // ending in a passing ramp - if (turn_candidates[1].valid) - turn_candidates[1].instruction = {TurnType::NoTurn, - getTurnDirection(turn_candidates[1].angle)}; + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = { + TurnType::NoTurn, getTurnDirection(intersection[1].turn.angle)}; else - turn_candidates[2].instruction = {TurnType::NoTurn, - getTurnDirection(turn_candidates[2].angle)}; + intersection[2].turn.instruction = { + TurnType::NoTurn, getTurnDirection(intersection[2].turn.angle)}; } } - else if (countValid(turn_candidates) > 0) // check whether turns exist at all + else if (intersection.size() == 4 && + detail::roadClass(intersection[1], node_based_graph) == + detail::roadClass(intersection[2], node_based_graph) && + detail::roadClass(intersection[2], node_based_graph) == + detail::roadClass(intersection[3], node_based_graph)) + { + // tripple fork at the end + assignFork(via_edge, intersection[3], intersection[2], intersection[1]); + } + else if (countValid(intersection) > 0) // check whether turns exist at all { // FALLBACK, this should hopefully never be reached auto coord = localizer(node_based_graph.GetTarget(via_edge)); util::SimpleLogger().Write(logWARNING) << "Fallback reached from motorway at " << std::setprecision(12) << toFloating(coord.lat) << " " << toFloating(coord.lon) << ", no continue angle, " - << turn_candidates.size() << " candidates, " << countValid(turn_candidates) - << " valid ones."; - fallbackTurnAssignmentMotorway(turn_candidates); + << intersection.size() << " roads, " << countValid(intersection) << " valid ones."; + fallbackTurnAssignmentMotorway(intersection); } } else { - const unsigned exiting_motorways = countExitingMotorways(turn_candidates); + const unsigned exiting_motorways = countExitingMotorways(intersection); if (exiting_motorways == 0) { // Ending in Ramp - for (auto &candidate : turn_candidates) + for (auto &road : intersection) { - if (candidate.valid) + if (road.entry_allowed) { - BOOST_ASSERT(detail::isRampClass(candidate.eid, node_based_graph)); - candidate.instruction = - TurnInstruction::SUPPRESSED(getTurnDirection(candidate.angle)); + BOOST_ASSERT(detail::isRampClass(road.turn.eid, node_based_graph)); + road.turn.instruction = + TurnInstruction::SUPPRESSED(getTurnDirection(road.turn.angle)); } } } else if (exiting_motorways == 1) { // normal motorway passing some ramps or mering onto another motorway - if (turn_candidates.size() == 2) + if (intersection.size() == 2) { - BOOST_ASSERT(!detail::isRampClass(turn_candidates[1].eid, node_based_graph)); + BOOST_ASSERT(!detail::isRampClass(intersection[1].turn.eid, node_based_graph)); - turn_candidates[1].instruction = - getInstructionForObvious(turn_candidates.size(), via_edge, turn_candidates[1]); + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[1]); } else { // continue on the same highway - bool continues = (getContinueAngle(turn_candidates) != turn_candidates[0].angle); + bool continues = (getContinueAngle(intersection) != intersection[0].turn.angle); // Normal Highway exit or merge - for (auto &candidate : turn_candidates) + for (auto &road : intersection) { // ignore invalid uturns/other - if (!candidate.valid) + if (!road.entry_allowed) continue; - if (candidate.angle == continue_angle) + if (road.turn.angle == continue_angle) { if (continues) - candidate.instruction = + road.turn.instruction = TurnInstruction::SUPPRESSED(DirectionModifier::Straight); else // TODO handle turn direction correctly - candidate.instruction = {TurnType::Merge, DirectionModifier::Straight}; + road.turn.instruction = {TurnType::Merge, DirectionModifier::Straight}; } - else if (candidate.angle < continue_angle) + else if (road.turn.angle < continue_angle) { - candidate.instruction = { - detail::isRampClass(candidate.eid, node_based_graph) ? TurnType::Ramp + road.turn.instruction = { + detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::Ramp : TurnType::Turn, - (candidate.angle < 145) ? DirectionModifier::Right + (road.turn.angle < 145) ? DirectionModifier::Right : DirectionModifier::SlightRight}; } - else if (candidate.angle > continue_angle) + else if (road.turn.angle > continue_angle) { - candidate.instruction = { - detail::isRampClass(candidate.eid, node_based_graph) ? TurnType::Ramp + road.turn.instruction = { + detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::Ramp : TurnType::Turn, - (candidate.angle > 215) ? DirectionModifier::Left + (road.turn.angle > 215) ? DirectionModifier::Left : DirectionModifier::SlightLeft}; } } @@ -472,26 +444,26 @@ std::vector TurnAnalysis::handleFromMotorway( // handle motorway forks else if (exiting_motorways > 1) { - if (exiting_motorways == 2 && turn_candidates.size() == 2) + if (exiting_motorways == 2 && intersection.size() == 2) { - turn_candidates[1].instruction = - getInstructionForObvious(turn_candidates.size(), via_edge, turn_candidates[1]); + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[1]); util::SimpleLogger().Write(logWARNING) << "Disabled U-Turn on a freeway at " << localizer(node_based_graph.GetTarget(via_edge)); - turn_candidates[0].valid = false; // UTURN on the freeway + intersection[0].entry_allowed = false; // UTURN on the freeway } else if (exiting_motorways == 2) { // standard fork std::size_t first_valid = std::numeric_limits::max(), second_valid = std::numeric_limits::max(); - for (std::size_t i = 0; i < turn_candidates.size(); ++i) + for (std::size_t i = 0; i < intersection.size(); ++i) { - if (turn_candidates[i].valid && - detail::isMotorwayClass(turn_candidates[i].eid, node_based_graph)) + if (intersection[i].entry_allowed && + detail::isMotorwayClass(intersection[i].turn.eid, node_based_graph)) { - if (first_valid < turn_candidates.size()) + if (first_valid < intersection.size()) { second_valid = i; break; @@ -502,7 +474,7 @@ std::vector TurnAnalysis::handleFromMotorway( } } } - assignFork(via_edge, turn_candidates[second_valid], turn_candidates[first_valid]); + assignFork(via_edge, intersection[second_valid], intersection[first_valid]); } else if (exiting_motorways == 3) { @@ -510,17 +482,17 @@ std::vector TurnAnalysis::handleFromMotorway( std::size_t first_valid = std::numeric_limits::max(), second_valid = std::numeric_limits::max(), third_valid = std::numeric_limits::max(); - for (std::size_t i = 0; i < turn_candidates.size(); ++i) + for (std::size_t i = 0; i < intersection.size(); ++i) { - if (turn_candidates[i].valid && - detail::isMotorwayClass(turn_candidates[i].eid, node_based_graph)) + if (intersection[i].entry_allowed && + detail::isMotorwayClass(intersection[i].turn.eid, node_based_graph)) { - if (second_valid < turn_candidates.size()) + if (second_valid < intersection.size()) { third_valid = i; break; } - else if (first_valid < turn_candidates.size()) + else if (first_valid < intersection.size()) { second_valid = i; } @@ -530,52 +502,42 @@ std::vector TurnAnalysis::handleFromMotorway( } } } - assignFork(via_edge, turn_candidates[third_valid], turn_candidates[second_valid], - turn_candidates[first_valid]); + assignFork(via_edge, intersection[third_valid], intersection[second_valid], + intersection[first_valid]); } else { auto coord = localizer(node_based_graph.GetTarget(via_edge)); util::SimpleLogger().Write(logWARNING) << "Found motorway junction with more than " - "2 exiting motorways or additional ramps at " - << std::setprecision(12) << toFloating(coord.lat) << " " - << toFloating(coord.lon); - fallbackTurnAssignmentMotorway(turn_candidates); + "2 exiting motorways or additional ramps at " << std::setprecision(12) + << toFloating(coord.lat) << " " << toFloating(coord.lon); + fallbackTurnAssignmentMotorway(intersection); } } // done for more than one highway exit } - -#if PRINT_DEBUG_CANDIDATES - std::cout << "From Motorway Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << " name: " << node_based_graph.GetEdgeData(tc.eid).name_id << std::endl; -#endif - return turn_candidates; + return intersection; } -std::vector TurnAnalysis::handleMotorwayRamp( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const +std::vector TurnAnalysis::handleMotorwayRamp( + const EdgeID via_edge, std::vector intersection) const { - (void)from; - auto num_valid_turns = countValid(turn_candidates); + auto num_valid_turns = countValid(intersection); // ramp straight into a motorway/ramp - if (turn_candidates.size() == 2 && num_valid_turns == 1) + if (intersection.size() == 2 && num_valid_turns == 1) { - BOOST_ASSERT(!turn_candidates[0].valid); - BOOST_ASSERT(detail::isMotorwayClass(turn_candidates[1].eid, node_based_graph)); + BOOST_ASSERT(!intersection[0].entry_allowed); + BOOST_ASSERT(detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph)); - turn_candidates[1].instruction = - getInstructionForObvious(turn_candidates.size(), via_edge, turn_candidates[1]); + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[1]); } - else if (turn_candidates.size() == 3) + else if (intersection.size() == 3) { // merging onto a passing highway / or two ramps merging onto the same highway if (num_valid_turns == 1) { - BOOST_ASSERT(!turn_candidates[0].valid); + BOOST_ASSERT(!intersection[0].entry_allowed); // check order of highways // 4 // 5 3 @@ -584,58 +546,58 @@ std::vector TurnAnalysis::handleMotorwayRamp( // // 7 1 // 0 - if (turn_candidates[1].valid) + if (intersection[1].entry_allowed) { - if (detail::isMotorwayClass(turn_candidates[1].eid, node_based_graph)) + if (detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph)) { // circular order indicates a merge to the left (0-3 onto 4 - if (angularDeviation(turn_candidates[1].angle, STRAIGHT_ANGLE) < + if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) - turn_candidates[1].instruction = {TurnType::Merge, - DirectionModifier::SlightLeft}; + intersection[1].turn.instruction = {TurnType::Merge, + DirectionModifier::SlightLeft}; else // fallback - turn_candidates[1].instruction = { - TurnType::Merge, getTurnDirection(turn_candidates[1].angle)}; + intersection[1].turn.instruction = { + TurnType::Merge, getTurnDirection(intersection[1].turn.angle)}; } else // passing by the end of a motorway - turn_candidates[1].instruction = getInstructionForObvious( - turn_candidates.size(), via_edge, turn_candidates[1]); + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[1]); } else { - BOOST_ASSERT(turn_candidates[2].valid); - if (detail::isMotorwayClass(turn_candidates[2].eid, node_based_graph)) + BOOST_ASSERT(intersection[2].entry_allowed); + if (detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph)) { // circular order (5-0) onto 4 - if (angularDeviation(turn_candidates[2].angle, STRAIGHT_ANGLE) < + if (angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) - turn_candidates[2].instruction = {TurnType::Merge, - DirectionModifier::SlightRight}; + intersection[2].turn.instruction = {TurnType::Merge, + DirectionModifier::SlightRight}; else // fallback - turn_candidates[2].instruction = { - TurnType::Merge, getTurnDirection(turn_candidates[2].angle)}; + intersection[2].turn.instruction = { + TurnType::Merge, getTurnDirection(intersection[2].turn.angle)}; } else // passing the end of a highway - turn_candidates[1].instruction = getInstructionForObvious( - turn_candidates.size(), via_edge, turn_candidates[1]); + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[1]); } } else { BOOST_ASSERT(num_valid_turns == 2); // UTurn on ramps is not possible - BOOST_ASSERT(!turn_candidates[0].valid); - BOOST_ASSERT(turn_candidates[1].valid); - BOOST_ASSERT(turn_candidates[2].valid); + BOOST_ASSERT(!intersection[0].entry_allowed); + BOOST_ASSERT(intersection[1].entry_allowed); + BOOST_ASSERT(intersection[2].entry_allowed); // two motorways starting at end of ramp (fork) // M M // \ / // | // R - if (detail::isMotorwayClass(turn_candidates[1].eid, node_based_graph) && - detail::isMotorwayClass(turn_candidates[2].eid, node_based_graph)) + if (detail::isMotorwayClass(intersection[1].turn.eid, node_based_graph) && + detail::isMotorwayClass(intersection[2].turn.eid, node_based_graph)) { - assignFork(via_edge, turn_candidates[2], turn_candidates[1]); + assignFork(via_edge, intersection[2], intersection[1]); } else { @@ -644,108 +606,97 @@ std::vector TurnAnalysis::handleMotorwayRamp( // M R // | / // R - if (detail::isMotorwayClass(node_based_graph.GetEdgeData(turn_candidates[1].eid) + if (detail::isMotorwayClass(node_based_graph.GetEdgeData(intersection[1].turn.eid) .road_classification.road_class)) { - turn_candidates[1].instruction = {TurnType::Merge, - DirectionModifier::SlightRight}; - turn_candidates[2].instruction = {TurnType::Fork, - DirectionModifier::SlightLeft}; + intersection[1].turn.instruction = {TurnType::Merge, + DirectionModifier::SlightRight}; + intersection[2].turn.instruction = {TurnType::Fork, + DirectionModifier::SlightLeft}; } else { - turn_candidates[1].instruction = {TurnType::Fork, - DirectionModifier::SlightRight}; - turn_candidates[2].instruction = {TurnType::Merge, - DirectionModifier::SlightLeft}; + intersection[1].turn.instruction = {TurnType::Fork, + DirectionModifier::SlightRight}; + intersection[2].turn.instruction = {TurnType::Merge, + DirectionModifier::SlightLeft}; } } } } // On - Off Ramp on passing Motorway, Ramp onto Fork(?) - else if (turn_candidates.size() == 4) + else if (intersection.size() == 4) { bool passed_highway_entry = false; - for (auto &candidate : turn_candidates) + for (auto &road : intersection) { - const auto &edge_data = node_based_graph.GetEdgeData(candidate.eid); - if (!candidate.valid && + const auto &edge_data = node_based_graph.GetEdgeData(road.turn.eid); + if (!road.entry_allowed && detail::isMotorwayClass(edge_data.road_classification.road_class)) { passed_highway_entry = true; } else if (detail::isMotorwayClass(edge_data.road_classification.road_class)) { - candidate.instruction = {TurnType::Merge, passed_highway_entry - ? DirectionModifier::SlightRight + road.turn.instruction = {TurnType::Merge, + passed_highway_entry ? DirectionModifier::SlightRight : DirectionModifier::SlightLeft}; } else { BOOST_ASSERT(isRampClass(edge_data.road_classification.road_class)); - candidate.instruction = {TurnType::Ramp, getTurnDirection(candidate.angle)}; + road.turn.instruction = {TurnType::Ramp, getTurnDirection(road.turn.angle)}; } } } else { // FALLBACK, hopefully this should never been reached util::SimpleLogger().Write(logWARNING) << "Reached fallback on motorway ramp with " - << turn_candidates.size() << " candidates and " - << countValid(turn_candidates) << " valid turns."; - fallbackTurnAssignmentMotorway(turn_candidates); - } - -#if PRINT_DEBUG_CANDIDATES - std::cout << "Onto Motorway Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << " name: " << node_based_graph.GetEdgeData(tc.eid).name_id << std::endl; -#endif - return turn_candidates; + << intersection.size() << " roads and " + << countValid(intersection) << " valid turns."; + fallbackTurnAssignmentMotorway(intersection); + } + return intersection; } -std::vector TurnAnalysis::handleMotorwayJunction( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const +std::vector TurnAnalysis::handleMotorwayJunction( + const EdgeID via_edge, std::vector intersection) const { - (void)from; - // BOOST_ASSERT(!turn_candidates[0].valid); //This fails due to @themarex handling of dead end + // BOOST_ASSERT(!intersection[0].entry_allowed); //This fails due to @themarex handling of dead + // end // streets const auto &in_data = node_based_graph.GetEdgeData(via_edge); // coming from motorway if (detail::isMotorwayClass(in_data.road_classification.road_class)) { - return handleFromMotorway(from, via_edge, std::move(turn_candidates)); + return handleFromMotorway(via_edge, std::move(intersection)); } else // coming from a ramp { - return handleMotorwayRamp(from, via_edge, std::move(turn_candidates)); + return handleMotorwayRamp(via_edge, std::move(intersection)); // ramp merging straight onto motorway } } -bool TurnAnalysis::isMotorwayJunction(const NodeID from, - const EdgeID via_edge, - const std::vector &turn_candidates) const +bool TurnAnalysis::isMotorwayJunction(const EdgeID via_edge, + const std::vector &intersection) const { - (void)from; - bool has_motorway = false; bool has_normal_roads = false; - for (const auto &candidate : turn_candidates) + for (const auto &road : intersection) { - const auto &out_data = node_based_graph.GetEdgeData(candidate.eid); + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); // not merging or forking? - if ((angularDeviation(candidate.angle, 0) > 35 && - angularDeviation(candidate.angle, 180) > 35) || - (candidate.valid && angularDeviation(candidate.angle, 0) < 35)) + if ((angularDeviation(road.turn.angle, 0) > 35 && + angularDeviation(road.turn.angle, 180) > 35) || + (road.entry_allowed && angularDeviation(road.turn.angle, 0) < 35)) return false; else if (out_data.road_classification.road_class == FunctionalRoadClass::MOTORWAY || out_data.road_classification.road_class == FunctionalRoadClass::TRUNK) { - if (candidate.valid) + if (road.entry_allowed) has_motorway = true; } else if (!isRampClass(out_data.road_classification.road_class)) @@ -761,12 +712,11 @@ bool TurnAnalysis::isMotorwayJunction(const NodeID from, in_data.road_classification.road_class == FunctionalRoadClass::TRUNK; } -TurnType TurnAnalysis::findBasicTurnType(const EdgeID via_edge, - const TurnCandidate &candidate) const +TurnType TurnAnalysis::findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &road) const { const auto &in_data = node_based_graph.GetEdgeData(via_edge); - const auto &out_data = node_based_graph.GetEdgeData(candidate.eid); + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); bool on_ramp = isRampClass(in_data.road_classification.road_class); @@ -783,94 +733,77 @@ TurnType TurnAnalysis::findBasicTurnType(const EdgeID via_edge, return TurnType::Turn; } -TurnInstruction TurnAnalysis::getInstructionForObvious(const std::size_t num_candidates, +TurnInstruction TurnAnalysis::getInstructionForObvious(const std::size_t num_roads, const EdgeID via_edge, - const TurnCandidate &candidate) const + const ConnectedRoad &road) const { - const auto type = findBasicTurnType(via_edge, candidate); + const auto type = findBasicTurnType(via_edge, road); if (type == TurnType::Ramp) { - return {TurnType::Ramp, getTurnDirection(candidate.angle)}; + return {TurnType::Ramp, getTurnDirection(road.turn.angle)}; } - if (angularDeviation(candidate.angle, 0) < 0.01) + if (angularDeviation(road.turn.angle, 0) < 0.01) { return {TurnType::Turn, DirectionModifier::UTurn}; } if (type == TurnType::Turn) { - return {TurnType::NewName, getTurnDirection(candidate.angle)}; + 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))) + return {TurnType::NewName, getTurnDirection(road.turn.angle)}; + else + return {TurnType::Suppressed, getTurnDirection(road.turn.angle)}; } BOOST_ASSERT(type == TurnType::Continue); - if (num_candidates > 2) + if (num_roads > 2) { - return {TurnType::Suppressed, getTurnDirection(candidate.angle)}; + return {TurnType::Suppressed, getTurnDirection(road.turn.angle)}; } else { - return {TurnType::NoTurn, getTurnDirection(candidate.angle)}; + return {TurnType::NoTurn, getTurnDirection(road.turn.angle)}; } } -std::vector TurnAnalysis::handleOneWayTurn( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const +std::vector +TurnAnalysis::handleOneWayTurn(std::vector intersection) const { - BOOST_ASSERT(turn_candidates[0].angle < 0.001); - (void)from, (void)via_edge; - -#if PRINT_DEBUG_CANDIDATES - std::cout << "Basic (one) Turn Candidates:\n"; - for (auto tc : turn_candidates) - { - std::cout << "\t" << tc.toString() << " "; - if (tc.eid != SPECIAL_EDGEID) - { - std::cout << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class << - "name: " << node_based_graph.GetEdgeData(tc.eid).name_id << std::endl; - } - else - { - std::cout << " dead end" << std::endl; - } - } -#endif - return turn_candidates; + BOOST_ASSERT(intersection[0].turn.angle < 0.001); + return intersection; } -std::vector TurnAnalysis::handleTwoWayTurn( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const +std::vector +TurnAnalysis::handleTwoWayTurn(const EdgeID via_edge, std::vector intersection) const { - BOOST_ASSERT(turn_candidates[0].angle < 0.001); - (void)from; - turn_candidates[1].instruction = - getInstructionForObvious(turn_candidates.size(), via_edge, turn_candidates[1]); - - if (turn_candidates[1].instruction.type == TurnType::Suppressed) - turn_candidates[1].instruction.type = TurnType::NoTurn; - -#if PRINT_DEBUG_CANDIDATES - std::cout << "Basic Two Turns Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << " name: " << node_based_graph.GetEdgeData(tc.eid).name_id << std::endl; -#endif - return turn_candidates; + BOOST_ASSERT(intersection[0].turn.angle < 0.001); + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[1]); + + if (intersection[1].turn.instruction.type == TurnType::Suppressed) + intersection[1].turn.instruction.type = TurnType::NoTurn; + + return intersection; } -std::vector TurnAnalysis::handleThreeWayTurn( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const +std::vector +TurnAnalysis::handleThreeWayTurn(const EdgeID via_edge, + std::vector intersection) const { - BOOST_ASSERT(turn_candidates[0].angle < 0.001); - (void)from; - const auto isObviousOfTwo = [](const TurnCandidate turn, const TurnCandidate other) - { - return (angularDeviation(turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && - angularDeviation(other.angle, STRAIGHT_ANGLE) > 85) || - (angularDeviation(other.angle, STRAIGHT_ANGLE) / - angularDeviation(turn.angle, STRAIGHT_ANGLE) > + BOOST_ASSERT(intersection[0].turn.angle < 0.001); + const auto isObviousOfTwo = [](const ConnectedRoad road, const ConnectedRoad other) + { + return (angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && + angularDeviation(other.turn.angle, STRAIGHT_ANGLE) > 85) || + (angularDeviation(road.turn.angle,STRAIGHT_ANGLE) < std::numeric_limits::epsilon()) || + (angularDeviation(other.turn.angle, STRAIGHT_ANGLE) / + angularDeviation(road.turn.angle, STRAIGHT_ANGLE) > 1.4); }; + /* Two nearly straight turns -> FORK OOOOOOO / @@ -878,21 +811,40 @@ std::vector TurnAnalysis::handleThreeWayTurn( \ OOOOOOO */ - if (angularDeviation(turn_candidates[1].angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && - angularDeviation(turn_candidates[2].angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) - { - if (turn_candidates[1].valid && turn_candidates[2].valid) - { - assignFork(via_edge, turn_candidates[2], turn_candidates[1]); + if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && + angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) + { + if (intersection[1].entry_allowed && intersection[2].entry_allowed) + { + const auto left_class = node_based_graph.GetEdgeData(intersection[2].turn.eid) + .road_classification.road_class; + const auto right_class = node_based_graph.GetEdgeData(intersection[1].turn.eid) + .road_classification.road_class; + if (canBeSeenAsFork(left_class, right_class)) + assignFork(via_edge, intersection[2], intersection[1]); + else if (getPriority(left_class) > getPriority(right_class)) + { + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[1]); + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + DirectionModifier::SlightLeft}; + } + else + { + intersection[2].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[2]); + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::SlightRight}; + } } else { - if (turn_candidates[1].valid) - turn_candidates[1].instruction = - getInstructionForObvious(turn_candidates.size(), via_edge, turn_candidates[1]); - if (turn_candidates[2].valid) - turn_candidates[2].instruction = - getInstructionForObvious(turn_candidates.size(), via_edge, turn_candidates[2]); + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[1]); + if (intersection[2].entry_allowed) + intersection[2].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[2]); } } /* T Intersection @@ -902,25 +854,25 @@ std::vector TurnAnalysis::handleThreeWayTurn( I I */ - else if (angularDeviation(turn_candidates[1].angle, 90) < NARROW_TURN_ANGLE && - angularDeviation(turn_candidates[2].angle, 270) < NARROW_TURN_ANGLE && - angularDeviation(turn_candidates[1].angle, turn_candidates[2].angle) > + else if (angularDeviation(intersection[1].turn.angle, 90) < NARROW_TURN_ANGLE && + angularDeviation(intersection[2].turn.angle, 270) < NARROW_TURN_ANGLE && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) > NARROW_TURN_ANGLE) { - if (turn_candidates[1].valid) + if (intersection[1].entry_allowed) { - if (TurnType::Ramp != findBasicTurnType(via_edge, turn_candidates[1])) - turn_candidates[1].instruction = {TurnType::EndOfRoad, DirectionModifier::Right}; + if (TurnType::Ramp != findBasicTurnType(via_edge, intersection[1])) + intersection[1].turn.instruction = {TurnType::EndOfRoad, DirectionModifier::Right}; else - turn_candidates[1].instruction = {TurnType::Ramp, DirectionModifier::Right}; + intersection[1].turn.instruction = {TurnType::Ramp, DirectionModifier::Right}; } - if (turn_candidates[2].valid) + if (intersection[2].entry_allowed) { - if (TurnType::Ramp != findBasicTurnType(via_edge, turn_candidates[2])) + if (TurnType::Ramp != findBasicTurnType(via_edge, intersection[2])) - turn_candidates[2].instruction = {TurnType::EndOfRoad, DirectionModifier::Left}; + intersection[2].turn.instruction = {TurnType::EndOfRoad, DirectionModifier::Left}; else - turn_candidates[2].instruction = {TurnType::Ramp, DirectionModifier::Left}; + intersection[2].turn.instruction = {TurnType::Ramp, DirectionModifier::Left}; } } /* T Intersection, Cross left @@ -929,23 +881,23 @@ std::vector TurnAnalysis::handleThreeWayTurn( O IIIIIIII - OOOOOOOOOO */ - else if (angularDeviation(turn_candidates[1].angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && - angularDeviation(turn_candidates[2].angle, 270) < NARROW_TURN_ANGLE && - angularDeviation(turn_candidates[1].angle, turn_candidates[2].angle) > + else if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && + angularDeviation(intersection[2].turn.angle, 270) < NARROW_TURN_ANGLE && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) > NARROW_TURN_ANGLE) { - if (turn_candidates[1].valid) + if (intersection[1].entry_allowed) { - if (TurnType::Ramp != findBasicTurnType(via_edge, turn_candidates[1])) - turn_candidates[1].instruction = - getInstructionForObvious(turn_candidates.size(), via_edge, turn_candidates[1]); + if (TurnType::Ramp != findBasicTurnType(via_edge, intersection[1])) + intersection[1].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[1]); else - turn_candidates[1].instruction = {TurnType::Ramp, DirectionModifier::Straight}; + intersection[1].turn.instruction = {TurnType::Ramp, DirectionModifier::Straight}; } - if (turn_candidates[2].valid) + if (intersection[2].entry_allowed) { - turn_candidates[2].instruction = {findBasicTurnType(via_edge, turn_candidates[2]), - DirectionModifier::Left}; + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + DirectionModifier::Left}; } } /* T Intersection, Cross right @@ -955,704 +907,367 @@ std::vector TurnAnalysis::handleThreeWayTurn( O O */ - else if (angularDeviation(turn_candidates[2].angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && - angularDeviation(turn_candidates[1].angle, 90) < NARROW_TURN_ANGLE && - angularDeviation(turn_candidates[1].angle, turn_candidates[2].angle) > + else if (angularDeviation(intersection[2].turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && + angularDeviation(intersection[1].turn.angle, 90) < NARROW_TURN_ANGLE && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) > NARROW_TURN_ANGLE) { - if (turn_candidates[2].valid) - turn_candidates[2].instruction = - getInstructionForObvious(turn_candidates.size(), via_edge, turn_candidates[2]); - if (turn_candidates[1].valid) - turn_candidates[1].instruction = {findBasicTurnType(via_edge, turn_candidates[1]), - DirectionModifier::Right}; + if (intersection[2].entry_allowed) + intersection[2].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[2]); + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::Right}; } // merge onto a through street - else if (INVALID_NAME_ID != node_based_graph.GetEdgeData(turn_candidates[1].eid).name_id && - node_based_graph.GetEdgeData(turn_candidates[1].eid).name_id == - node_based_graph.GetEdgeData(turn_candidates[2].eid).name_id) + else if (INVALID_NAME_ID != node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id && + node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id == + node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id) { - const auto findTurn = [isObviousOfTwo](const TurnCandidate turn, - const TurnCandidate other) -> TurnInstruction + const auto findTurn = [isObviousOfTwo](const ConnectedRoad turn, const ConnectedRoad other) + -> TurnInstruction { return {isObviousOfTwo(turn, other) ? TurnType::Merge : TurnType::Turn, - getTurnDirection(turn.angle)}; + getTurnDirection(turn.turn.angle)}; }; - turn_candidates[1].instruction = findTurn(turn_candidates[1], turn_candidates[2]); - turn_candidates[2].instruction = findTurn(turn_candidates[2], turn_candidates[1]); + intersection[1].turn.instruction = findTurn(intersection[1], intersection[2]); + intersection[2].turn.instruction = findTurn(intersection[2], intersection[1]); } // other street merges from the left else if (INVALID_NAME_ID != node_based_graph.GetEdgeData(via_edge).name_id && node_based_graph.GetEdgeData(via_edge).name_id == - node_based_graph.GetEdgeData(turn_candidates[1].eid).name_id) + node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id) { - if (isObviousOfTwo(turn_candidates[1], turn_candidates[2])) + if (isObviousOfTwo(intersection[1], intersection[2])) { - turn_candidates[1].instruction = + intersection[1].turn.instruction = TurnInstruction::SUPPRESSED(DirectionModifier::Straight); } else { - turn_candidates[1].instruction = {TurnType::Continue, - getTurnDirection(turn_candidates[1].angle)}; + intersection[1].turn.instruction = {TurnType::Continue, + getTurnDirection(intersection[1].turn.angle)}; } - turn_candidates[2].instruction = {TurnType::Turn, - getTurnDirection(turn_candidates[2].angle)}; + intersection[2].turn.instruction = {TurnType::Turn, + getTurnDirection(intersection[2].turn.angle)}; } // other street merges from the right else if (INVALID_NAME_ID != node_based_graph.GetEdgeData(via_edge).name_id && node_based_graph.GetEdgeData(via_edge).name_id == - node_based_graph.GetEdgeData(turn_candidates[2].eid).name_id) + node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id) { - if (isObviousOfTwo(turn_candidates[2], turn_candidates[1])) + if (isObviousOfTwo(intersection[2], intersection[1])) { - turn_candidates[2].instruction = + intersection[2].turn.instruction = TurnInstruction::SUPPRESSED(DirectionModifier::Straight); } else { - turn_candidates[2].instruction = {TurnType::Continue, - getTurnDirection(turn_candidates[2].angle)}; + intersection[2].turn.instruction = {TurnType::Continue, + getTurnDirection(intersection[2].turn.angle)}; } - turn_candidates[1].instruction = {TurnType::Turn, - getTurnDirection(turn_candidates[1].angle)}; + intersection[1].turn.instruction = {TurnType::Turn, + getTurnDirection(intersection[1].turn.angle)}; } else { const unsigned in_name_id = node_based_graph.GetEdgeData(via_edge).name_id; const unsigned out_names[2] = { - node_based_graph.GetEdgeData(turn_candidates[1].eid).name_id, - node_based_graph.GetEdgeData(turn_candidates[2].eid).name_id}; - if (isObviousOfTwo(turn_candidates[1], turn_candidates[2])) + node_based_graph.GetEdgeData(intersection[1].turn.eid).name_id, + node_based_graph.GetEdgeData(intersection[2].turn.eid).name_id}; + if (isObviousOfTwo(intersection[1], intersection[2])) { - turn_candidates[1].instruction = { + intersection[1].turn.instruction = { (in_name_id != INVALID_NAME_ID || out_names[0] != INVALID_NAME_ID) ? TurnType::NewName : TurnType::NoTurn, - getTurnDirection(turn_candidates[1].angle)}; + getTurnDirection(intersection[1].turn.angle)}; } else { - turn_candidates[1].instruction = {TurnType::Turn, - getTurnDirection(turn_candidates[1].angle)}; + intersection[1].turn.instruction = {TurnType::Turn, + getTurnDirection(intersection[1].turn.angle)}; } - if (isObviousOfTwo(turn_candidates[2], turn_candidates[1])) + if (isObviousOfTwo(intersection[2], intersection[1])) { - turn_candidates[2].instruction = { + intersection[2].turn.instruction = { (in_name_id != INVALID_NAME_ID || out_names[1] != INVALID_NAME_ID) ? TurnType::NewName : TurnType::NoTurn, - getTurnDirection(turn_candidates[2].angle)}; + getTurnDirection(intersection[2].turn.angle)}; } else { - turn_candidates[2].instruction = {TurnType::Turn, - getTurnDirection(turn_candidates[2].angle)}; + intersection[2].turn.instruction = {TurnType::Turn, + getTurnDirection(intersection[2].turn.angle)}; } } // unnamed intersections or basic three way turn // remain at basic turns // TODO handle obviousness, Handle Merges - -#if PRINT_DEBUG_CANDIDATES - std::cout << "Basic Three Turn Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << " name: " << node_based_graph.GetEdgeData(tc.eid).name_id << std::endl; -#endif - return turn_candidates; + return intersection; } -std::vector TurnAnalysis::handleFourWayTurn( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const +void TurnAnalysis::handleDistinctConflict(const EdgeID via_edge, + ConnectedRoad &left, + ConnectedRoad &right) const { - (void)from; - static int fallback_count = 0; - // basic turn, or slightly rotated basic turn, has straight ANGLE - if (angularDeviation(turn_candidates[2].angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE && - angularDeviation(turn_candidates[0].angle, turn_candidates[1].angle) > NARROW_TURN_ANGLE && - angularDeviation(turn_candidates[1].angle, turn_candidates[2].angle) > NARROW_TURN_ANGLE && - angularDeviation(turn_candidates[2].angle, turn_candidates[3].angle) > NARROW_TURN_ANGLE && - angularDeviation(turn_candidates[3].angle, turn_candidates[0].angle) > NARROW_TURN_ANGLE) - { - { // Right - const auto type = findBasicTurnType(via_edge, turn_candidates[1]); - turn_candidates[1].instruction = {type, DirectionModifier::Right}; - } - { // Straight - turn_candidates[2].instruction = - getInstructionForObvious(turn_candidates.size(), via_edge, turn_candidates[2]); - } - { // Left - const auto type = findBasicTurnType(via_edge, turn_candidates[3]); - turn_candidates[3].instruction = {type, DirectionModifier::Left}; - } - } - // well differentiated turns - else if (angularDeviation(turn_candidates[1].angle, turn_candidates[2].angle) > - WELL_DISTINCT_ANGLE && - angularDeviation(turn_candidates[2].angle, turn_candidates[3].angle) > - WELL_DISTINCT_ANGLE) - { - for (std::size_t i = 1; i < turn_candidates.size(); ++i) - { - const auto type = findBasicTurnType(via_edge, turn_candidates[i]); - turn_candidates[i].instruction = {type, getTurnDirection(turn_candidates[i].angle)}; - } - } - // * * - // * * - // * * - // * * - // * * - // * * - // Two roads at the right side of a street - else if (false && - angularDeviation(turn_candidates[3].angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE) - { - // currently unhandled - } - // * * - // * * - // * * - // * * - // * * - // * * - // Two roads at the left side of a street - else if (false && - angularDeviation(turn_candidates[1].angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE) - { - // currently unhandled + // single turn of both is valid (don't change the valid one) + // or multiple identical angles -> bad OSM intersection + if ((!left.entry_allowed || !right.entry_allowed) || (left.turn.angle == right.turn.angle)) + { + if (left.entry_allowed) + left.turn.instruction = {findBasicTurnType(via_edge, left), + getTurnDirection(left.turn.angle)}; + if (right.entry_allowed) + right.turn.instruction = {findBasicTurnType(via_edge, right), + getTurnDirection(right.turn.angle)}; + return; } - else + + if (getTurnDirection(left.turn.angle) == DirectionModifier::Straight || + getTurnDirection(left.turn.angle) == DirectionModifier::SlightLeft || + getTurnDirection(right.turn.angle) == DirectionModifier::SlightRight) { - if (fallback_count++ < 10) + const auto left_class = + node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class; + const auto right_class = + node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class; + if (canBeSeenAsFork(left_class, right_class)) + assignFork(via_edge, left, right); + else if (getPriority(left_class) > getPriority(right_class)) { - const auto coord = localizer(node_based_graph.GetTarget(via_edge)); - util::SimpleLogger().Write(logDEBUG) - << "Resolved to keep fallback on four way turn assignment at " - << std::setprecision(12) << toFloating(coord.lat) << " " << toFloating(coord.lon); - for (const auto &candidate : turn_candidates) - { - const auto &out_data = node_based_graph.GetEdgeData(candidate.eid); - util::SimpleLogger().Write(logDEBUG) - << "Candidate: " << candidate.toString() << " Name: " << out_data.name_id - << " Road Class: " << (int)out_data.road_classification.road_class - << " At: " << localizer(node_based_graph.GetTarget(candidate.eid)); - } + // FIXME this should possibly know about the actual roads? + right.turn.instruction = getInstructionForObvious(4, via_edge, right); + left.turn.instruction = {findBasicTurnType(via_edge, left), + DirectionModifier::SlightLeft}; + } + else + { + // FIXME this should possibly know about the actual roads? + left.turn.instruction = getInstructionForObvious(4, via_edge, left); + right.turn.instruction = {findBasicTurnType(via_edge, right), + DirectionModifier::SlightRight}; } } -#if PRINT_DEBUG_CANDIDATES - std::cout << "Basic Four Turn Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << " name: " << node_based_graph.GetEdgeData(tc.eid).name_id << std::endl; -#endif - return turn_candidates; -} - -std::vector TurnAnalysis::handleComplexTurn( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const -{ - (void)from; // FIXME unused - (void)via_edge; // FIXME unused - (void)node_based_graph; // FIXME unused -#if PRINT_DEBUG_CANDIDATES - std::cout << "Basic Complex Turn Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << " name: " << node_based_graph.GetEdgeData(tc.eid).name_id << std::endl; -#endif - return turn_candidates; -} -std::vector TurnAnalysis::setTurnTypes( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const -{ - NodeID turn_node = node_based_graph.GetTarget(via_edge); - - for (auto &candidate : turn_candidates) + const auto left_type = findBasicTurnType(via_edge, left); + const auto right_type = findBasicTurnType(via_edge, right); + // Two Right Turns + if (angularDeviation(left.turn.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) { - if (!candidate.valid) - continue; - const EdgeID onto_edge = candidate.eid; - const NodeID to_node = node_based_graph.GetTarget(onto_edge); - - auto turn = AnalyzeTurn(from, via_edge, turn_node, onto_edge, to_node, candidate.angle); - - auto confidence = getTurnConfidence(candidate.angle, turn); - candidate.instruction = turn; - candidate.confidence = confidence; + // Keep left perfect, shift right + left.turn.instruction = {left_type, DirectionModifier::Right}; + right.turn.instruction = {right_type, DirectionModifier::SharpRight}; + return; } - return turn_candidates; -} - -std::vector -TurnAnalysis::optimizeRamps(const EdgeID via_edge, std::vector turn_candidates) const -{ - EdgeID continue_eid = SPECIAL_EDGEID; - double continue_angle = 0; - const auto &in_edge_data = node_based_graph.GetEdgeData(via_edge); - for (auto &candidate : turn_candidates) + if (angularDeviation(right.turn.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) { - if (candidate.instruction.direction_modifier == DirectionModifier::UTurn) - continue; - - const auto &out_edge_data = node_based_graph.GetEdgeData(candidate.eid); - if (out_edge_data.name_id == in_edge_data.name_id) - { - continue_eid = candidate.eid; - continue_angle = candidate.angle; - if (angularDeviation(candidate.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && - isRampClass(in_edge_data.road_classification.road_class)) - candidate.instruction = TurnType::Suppressed; - break; - } + // Keep Right perfect, shift left + left.turn.instruction = {left_type, DirectionModifier::SlightRight}; + right.turn.instruction = {right_type, DirectionModifier::Right}; + return; } - - if (continue_eid != SPECIAL_EDGEID) + // Two Right Turns + if (angularDeviation(left.turn.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) { - bool to_the_right = true; - for (auto &candidate : turn_candidates) + // Keep left perfect, shift right + left.turn.instruction = {left_type, DirectionModifier::Left}; + right.turn.instruction = {right_type, DirectionModifier::SlightLeft}; + return; + } + if (angularDeviation(right.turn.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + { + // Keep Right perfect, shift left + left.turn.instruction = {left_type, DirectionModifier::SharpLeft}; + right.turn.instruction = {right_type, DirectionModifier::Left}; + return; + } + // Both turns? + if (TurnType::Ramp != left_type && TurnType::Ramp != right_type) + { + if (left.turn.angle < STRAIGHT_ANGLE) { - if (candidate.eid == continue_eid) - { - to_the_right = false; - continue; - } - - if (candidate.instruction.type != TurnType::Ramp) - continue; - - if (isSlightModifier(candidate.instruction.direction_modifier)) - candidate.instruction.direction_modifier = - (to_the_right) ? DirectionModifier::SlightRight : DirectionModifier::SlightLeft; + left.turn.instruction = {TurnType::FirstTurn, getTurnDirection(left.turn.angle)}; + right.turn.instruction = {TurnType::SecondTurn, getTurnDirection(right.turn.angle)}; } + else + { + left.turn.instruction = {TurnType::SecondTurn, getTurnDirection(left.turn.angle)}; + right.turn.instruction = {TurnType::FirstTurn, getTurnDirection(right.turn.angle)}; + } + return; } - return turn_candidates; -} - -// requires sorted candidates -std::vector -TurnAnalysis::optimizeCandidates(const EdgeID via_eid, - std::vector turn_candidates) const -{ - BOOST_ASSERT_MSG(std::is_sorted(turn_candidates.begin(), turn_candidates.end(), - [](const TurnCandidate &left, const TurnCandidate &right) - { - return left.angle < right.angle; - }), - "Turn Candidates not sorted by angle."); - if (turn_candidates.size() <= 1) - return turn_candidates; - - turn_candidates = optimizeRamps(via_eid, std::move(turn_candidates)); - - const auto getLeft = [&turn_candidates](std::size_t index) - { - return (index + 1) % turn_candidates.size(); - }; - const auto getRight = [&turn_candidates](std::size_t index) + // Shift the lesser penalty + if (getTurnDirection(left.turn.angle) == DirectionModifier::SharpLeft) { - return (index + turn_candidates.size() - 1) % turn_candidates.size(); - }; - - // handle availability of multiple u-turns (e.g. street with separated small parking roads) - if (isUturn(turn_candidates[0].instruction) && turn_candidates[0].angle == 0) + left.turn.instruction = {left_type, DirectionModifier::SharpLeft}; + right.turn.instruction = {right_type, DirectionModifier::Left}; + return; + } + if (getTurnDirection(right.turn.angle) == DirectionModifier::SharpRight) { - if (isUturn(turn_candidates[getLeft(0)].instruction)) - turn_candidates[getLeft(0)].instruction.direction_modifier = - DirectionModifier::SharpLeft; - if (isUturn(turn_candidates[getRight(0)].instruction)) - turn_candidates[getRight(0)].instruction.direction_modifier = - DirectionModifier::SharpRight; + left.turn.instruction = {left_type, DirectionModifier::Right}; + right.turn.instruction = {right_type, DirectionModifier::SharpRight}; + return; } - const auto keepStraight = [](double angle) + if (getTurnDirection(left.turn.angle) == DirectionModifier::Right) { - return std::abs(angle - 180) < 5; - }; - - for (std::size_t turn_index = 0; turn_index < turn_candidates.size(); ++turn_index) + if (angularDeviation(left.turn.angle, 90) > angularDeviation(right.turn.angle, 90)) + { + left.turn.instruction = {left_type, DirectionModifier::SlightRight}; + right.turn.instruction = {right_type, DirectionModifier::Right}; + } + else + { + left.turn.instruction = {left_type, DirectionModifier::Right}; + right.turn.instruction = {right_type, DirectionModifier::SharpRight}; + } + } + else { - auto &turn = turn_candidates[turn_index]; - if (!isBasic(turn.instruction.type) || isUturn(turn.instruction)) - continue; - auto &left = turn_candidates[getLeft(turn_index)]; - if (turn.angle == left.angle) + if (angularDeviation(left.turn.angle, 270) > angularDeviation(right.turn.angle, 270)) { - util::SimpleLogger().Write(logWARNING) - << "conflicting turn angles, identical road duplicated? " - << std::setprecision(12) << node_info_list[node_based_graph.GetTarget(via_eid)].lat - << " " << node_info_list[node_based_graph.GetTarget(via_eid)].lon << std::endl; - } - if (isConflict(turn.instruction, left.instruction)) - { - // begin of a conflicting region - std::size_t conflict_begin = turn_index; - std::size_t conflict_end = getLeft(turn_index); - std::size_t conflict_size = 2; - while ( - isConflict(turn_candidates[getLeft(conflict_end)].instruction, turn.instruction) && - conflict_size < turn_candidates.size()) - { - conflict_end = getLeft(conflict_end); - ++conflict_size; - } - - turn_index = (conflict_end < conflict_begin) ? turn_candidates.size() : conflict_end; - - if (conflict_size > 3) - { - // check if some turns are invalid to find out about good handling - } - - auto &instruction_left_of_end = turn_candidates[getLeft(conflict_end)].instruction; - auto &instruction_right_of_begin = - turn_candidates[getRight(conflict_begin)].instruction; - auto &candidate_at_end = turn_candidates[conflict_end]; - auto &candidate_at_begin = turn_candidates[conflict_begin]; - if (conflict_size == 2) - { - if (turn.instruction.direction_modifier == DirectionModifier::Straight) - { - if (instruction_left_of_end.direction_modifier != - DirectionModifier::SlightLeft && - instruction_right_of_begin.direction_modifier != - DirectionModifier::SlightRight) - { - std::int32_t resolved_count = 0; - // uses side-effects in resolve - if (!keepStraight(candidate_at_end.angle) && - !resolve(candidate_at_end.instruction, instruction_left_of_end, - RESOLVE_TO_LEFT)) - util::SimpleLogger().Write(logDEBUG) - << "[warning] failed to resolve conflict"; - else - ++resolved_count; - // uses side-effects in resolve - if (!keepStraight(candidate_at_begin.angle) && - !resolve(candidate_at_begin.instruction, instruction_right_of_begin, - RESOLVE_TO_RIGHT)) - util::SimpleLogger().Write(logDEBUG) - << "[warning] failed to resolve conflict"; - else - ++resolved_count; - if (resolved_count >= 1 && - (!keepStraight(candidate_at_begin.angle) || - !keepStraight(candidate_at_end.angle))) // should always be the - // case, theoretically - continue; - } - } - if (candidate_at_begin.confidence < candidate_at_end.confidence) - { // if right shift is cheaper, or only option - if (resolve(candidate_at_begin.instruction, instruction_right_of_begin, - RESOLVE_TO_RIGHT)) - continue; - else if (resolve(candidate_at_end.instruction, instruction_left_of_end, - RESOLVE_TO_LEFT)) - continue; - } - else - { - if (resolve(candidate_at_end.instruction, instruction_left_of_end, - RESOLVE_TO_LEFT)) - continue; - else if (resolve(candidate_at_begin.instruction, instruction_right_of_begin, - RESOLVE_TO_RIGHT)) - continue; - } - if (isSlightTurn(turn.instruction) || isSharpTurn(turn.instruction)) - { - auto resolve_direction = - (turn.instruction.direction_modifier == DirectionModifier::SlightRight || - turn.instruction.direction_modifier == DirectionModifier::SharpLeft) - ? RESOLVE_TO_RIGHT - : RESOLVE_TO_LEFT; - if (resolve_direction == RESOLVE_TO_RIGHT && - resolveTransitive( - candidate_at_begin.instruction, instruction_right_of_begin, - turn_candidates[getRight(getRight(conflict_begin))].instruction, - RESOLVE_TO_RIGHT)) - continue; - else if (resolve_direction == RESOLVE_TO_LEFT && - resolveTransitive( - candidate_at_end.instruction, instruction_left_of_end, - turn_candidates[getLeft(getLeft(conflict_end))].instruction, - RESOLVE_TO_LEFT)) - continue; - } - } - else if (conflict_size >= 3) - { - // a conflict of size larger than three cannot be handled with the current - // model. - // Handle it as best as possible and keep the rest of the conflicting turns - if (conflict_size > 3) - { - NodeID conflict_location = node_based_graph.GetTarget(via_eid); - util::SimpleLogger().Write(logDEBUG) - << "[warning] found conflict larget than size three at " - << node_info_list[conflict_location].lat << ", " - << node_info_list[conflict_location].lon; - } - - if (!resolve(candidate_at_begin.instruction, instruction_right_of_begin, - RESOLVE_TO_RIGHT)) - { - if (isSlightTurn(turn.instruction)) - resolveTransitive( - candidate_at_begin.instruction, instruction_right_of_begin, - turn_candidates[getRight(getRight(conflict_begin))].instruction, - RESOLVE_TO_RIGHT); - else if (isSharpTurn(turn.instruction)) - resolveTransitive( - candidate_at_end.instruction, instruction_left_of_end, - turn_candidates[getLeft(getLeft(conflict_end))].instruction, - RESOLVE_TO_LEFT); - } - if (!resolve(candidate_at_end.instruction, instruction_left_of_end, - RESOLVE_TO_LEFT)) - { - if (isSlightTurn(turn.instruction)) - resolveTransitive( - candidate_at_end.instruction, instruction_left_of_end, - turn_candidates[getLeft(getLeft(conflict_end))].instruction, - RESOLVE_TO_LEFT); - else if (isSharpTurn(turn.instruction)) - resolveTransitive( - candidate_at_begin.instruction, instruction_right_of_begin, - turn_candidates[getRight(getRight(conflict_begin))].instruction, - RESOLVE_TO_RIGHT); - } - } + left.turn.instruction = {left_type, DirectionModifier::SharpLeft}; + right.turn.instruction = {right_type, DirectionModifier::Left}; + } + else + { + left.turn.instruction = {left_type, DirectionModifier::Left}; + right.turn.instruction = {right_type, DirectionModifier::SlightLeft}; } } - return turn_candidates; } -bool TurnAnalysis::isObviousChoice(const EdgeID via_eid, - const std::size_t turn_index, - const std::vector &turn_candidates) const +std::vector +TurnAnalysis::handleComplexTurn(const EdgeID via_edge, + std::vector intersection) const { - const auto getLeft = [&turn_candidates](std::size_t index) - { - return (index + 1) % turn_candidates.size(); - }; - const auto getRight = [&turn_candidates](std::size_t index) - { - return (index + turn_candidates.size() - 1) % turn_candidates.size(); - }; - const auto &candidate = turn_candidates[turn_index]; - const EdgeData &in_data = node_based_graph.GetEdgeData(via_eid); - const EdgeData &out_data = node_based_graph.GetEdgeData(candidate.eid); - const auto &candidate_to_the_left = turn_candidates[getLeft(turn_index)]; - - const auto &candidate_to_the_right = turn_candidates[getRight(turn_index)]; - - const auto hasValidRatio = [&](const TurnCandidate &left, const TurnCandidate ¢er, - const TurnCandidate &right) - { - auto angle_left = (left.angle > 180) ? angularDeviation(left.angle, STRAIGHT_ANGLE) : 180; - auto angle_right = - (right.angle < 180) ? angularDeviation(right.angle, STRAIGHT_ANGLE) : 180; - auto self_angle = angularDeviation(center.angle, STRAIGHT_ANGLE); - return angularDeviation(center.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && - ((center.angle < STRAIGHT_ANGLE) - ? (angle_right > self_angle && angle_left / self_angle > DISTINCTION_RATIO) - : (angle_left > self_angle && angle_right / self_angle > DISTINCTION_RATIO)); - }; - // only valid turn - if (!isLowPriorityRoadClass( - node_based_graph.GetEdgeData(candidate.eid).road_classification.road_class)) + static int fallback_count = 0; + const std::size_t obvious_index = findObviousTurn(via_edge, intersection); + const auto fork_range = findFork(intersection); + std::size_t straightmost_turn = 0; + double straightmost_deviation = 180; + for (std::size_t i = 0; i < intersection.size(); ++i) { - bool is_only_normal_road = true; - // TODO find out why this can also be reached for non-u-turns - for (size_t i = 0; i < turn_candidates.size(); ++i) + const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE); + if (deviation < straightmost_deviation) { - if (i == turn_index || turn_candidates[i].angle == 0) // skip self and u-turn - continue; - if (!isLowPriorityRoadClass(node_based_graph.GetEdgeData(turn_candidates[i].eid) - .road_classification.road_class)) - { - is_only_normal_road = false; - break; - } + straightmost_deviation = deviation; + straightmost_turn = i; } - if (is_only_normal_road == true) - return true; } - return turn_candidates.size() == 1 || - // only non u-turn - (turn_candidates.size() == 2 && - isUturn(candidate_to_the_left.instruction)) || // nearly straight turn - angularDeviation(candidate.angle, STRAIGHT_ANGLE) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION || - hasValidRatio(candidate_to_the_left, candidate, candidate_to_the_right) || - (in_data.name_id != 0 && in_data.name_id == out_data.name_id && - angularDeviation(candidate.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE / 2); -} - -std::vector -TurnAnalysis::suppressTurns(const EdgeID via_eid, std::vector turn_candidates) const -{ - if (turn_candidates.size() == 3) + if (obvious_index != 0) { - BOOST_ASSERT(turn_candidates[0].instruction.direction_modifier == DirectionModifier::UTurn); - if (isLowPriorityRoadClass(node_based_graph.GetEdgeData(turn_candidates[1].eid) - .road_classification.road_class) && - !isLowPriorityRoadClass(node_based_graph.GetEdgeData(turn_candidates[2].eid) - .road_classification.road_class)) - { - if (angularDeviation(turn_candidates[2].angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) + intersection[obvious_index].turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, intersection[obvious_index]); + + // assign left/right turns + intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index + 1); + intersection = assignRightTurns(via_edge, std::move(intersection), obvious_index); + } + else if (fork_range.first != 0 && fork_range.second - fork_range.first <= 2) // found fork + { + if (fork_range.second - fork_range.first == 1) + { + auto &left = intersection[fork_range.second]; + auto &right = intersection[fork_range.first]; + const auto left_class = + node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class; + const auto right_class = + node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class; + if (canBeSeenAsFork(left_class, right_class)) + assignFork(via_edge, left, right); + else if (getPriority(left_class) > getPriority(right_class)) { - if (node_based_graph.GetEdgeData(turn_candidates[2].eid).name_id == - node_based_graph.GetEdgeData(via_eid).name_id) - { - turn_candidates[2].instruction = TurnInstruction::NO_TURN(); - } - else - { - turn_candidates[2].instruction.type = TurnType::NewName; - } - return turn_candidates; + right.turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, right); + left.turn.instruction = {findBasicTurnType(via_edge, left), + DirectionModifier::SlightLeft}; } - } - else if (isLowPriorityRoadClass(node_based_graph.GetEdgeData(turn_candidates[2].eid) - .road_classification.road_class) && - !isLowPriorityRoadClass(node_based_graph.GetEdgeData(turn_candidates[1].eid) - .road_classification.road_class)) - { - if (angularDeviation(turn_candidates[1].angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) + else { - if (node_based_graph.GetEdgeData(turn_candidates[1].eid).name_id == - node_based_graph.GetEdgeData(via_eid).name_id) - { - turn_candidates[1].instruction = TurnInstruction::NO_TURN(); - } - else - { - turn_candidates[1].instruction.type = TurnType::NewName; - } - return turn_candidates; + left.turn.instruction = + getInstructionForObvious(intersection.size(), via_edge, left); + right.turn.instruction = {findBasicTurnType(via_edge, right), + DirectionModifier::SlightRight}; } } + else if (fork_range.second - fork_range.second == 2) + { + assignFork(via_edge, intersection[fork_range.second], + intersection[fork_range.first + 1], intersection[fork_range.first]); + } + // assign left/right turns + intersection = assignLeftTurns(via_edge, std::move(intersection), fork_range.second + 1); + intersection = assignRightTurns(via_edge, std::move(intersection), fork_range.first); } - - BOOST_ASSERT_MSG(std::is_sorted(turn_candidates.begin(), turn_candidates.end(), - [](const TurnCandidate &left, const TurnCandidate &right) - { - return left.angle < right.angle; - }), - "Turn Candidates not sorted by angle."); - - const auto getLeft = [&turn_candidates](std::size_t index) + else if (straightmost_deviation < FUZZY_ANGLE_DIFFERENCE && + !intersection[straightmost_turn].entry_allowed) { - return (index + 1) % turn_candidates.size(); - }; - const auto getRight = [&turn_candidates](std::size_t index) + // invalid straight turn + intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1); + intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn); + } + // no straight turn + else if (intersection[straightmost_turn].turn.angle > 180) { - return (index + turn_candidates.size() - 1) % turn_candidates.size(); - }; - - const EdgeData &in_data = node_based_graph.GetEdgeData(via_eid); - - bool has_obvious_with_same_name = false; - double obvious_with_same_name_angle = 0; - for (std::size_t turn_index = 0; turn_index < turn_candidates.size(); ++turn_index) + // at most three turns on either side + intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn); + intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn); + } + else if (intersection[straightmost_turn].turn.angle < 180) { - if (node_based_graph.GetEdgeData(turn_candidates[turn_index].eid).name_id == - in_data.name_id && - isObviousChoice(via_eid, turn_index, turn_candidates)) + intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1); + intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn + 1); + } + else + { + if (fallback_count++ < 10) { - has_obvious_with_same_name = true; - obvious_with_same_name_angle = turn_candidates[turn_index].angle; - break; + const auto coord = localizer(node_based_graph.GetTarget(via_edge)); + util::SimpleLogger().Write(logWARNING) + << "Resolved to keep fallback on complex turn assignment at " + << std::setprecision(12) << toFloating(coord.lat) << " " << toFloating(coord.lon) + << "Straightmost: " << straightmost_turn; + ; + for (const auto &road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + util::SimpleLogger().Write(logWARNING) + << "road: " << road.toString() << " Name: " << out_data.name_id + << " Road Class: " << (int)out_data.road_classification.road_class + << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid)); + } } } + return intersection; +} - for (std::size_t turn_index = 0; turn_index < turn_candidates.size(); ++turn_index) +// Sets basic turn types as fallback for otherwise unhandled turns +std::vector TurnAnalysis::setTurnTypes(const NodeID from, + const EdgeID via_edge, + std::vector intersection) const +{ + for (auto &road : intersection) { - auto &candidate = turn_candidates[turn_index]; - if (!isBasic(candidate.instruction.type)) + if (!road.entry_allowed) continue; - const EdgeData &out_data = node_based_graph.GetEdgeData(candidate.eid); - if (out_data.name_id == in_data.name_id && in_data.name_id != 0 && - candidate.instruction.direction_modifier != DirectionModifier::UTurn && - !has_obvious_with_same_name) - { - candidate.instruction.type = TurnType::Continue; - } - if (candidate.valid && !isUturn(candidate.instruction)) - { - // TODO road category would be useful to indicate obviousness of turn - // check if turn can be omitted or at least changed - const auto &left = turn_candidates[getLeft(turn_index)]; - const auto &right = turn_candidates[getRight(turn_index)]; - - // make very slight instructions straight, if they are the only valid choice going - // with - // at most a slight turn - if ((!isSlightModifier(getTurnDirection(left.angle)) || !left.valid) && - (!isSlightModifier(getTurnDirection(right.angle)) || !right.valid) && - angularDeviation(candidate.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE) - candidate.instruction.direction_modifier = DirectionModifier::Straight; - - // TODO this smaller comparison for turns is DANGEROUS, has to be revised if turn - // instructions change - if (in_data.travel_mode == - out_data.travel_mode) // make sure to always announce mode changes - { - if (isObviousChoice(via_eid, turn_index, turn_candidates)) - { - - if (in_data.name_id == out_data.name_id) // same road - { - candidate.instruction.type = TurnType::Suppressed; - } + const EdgeID onto_edge = road.turn.eid; + const NodeID to_node = node_based_graph.GetTarget(onto_edge); - else if (!has_obvious_with_same_name) - { - // TODO discuss, we might want to keep the current name of the turn. But - // this would mean emitting a turn when you just keep on a road - if (isRampClass(in_data.road_classification.road_class) && - !isRampClass(out_data.road_classification.road_class)) - { - candidate.instruction.type = TurnType::Merge; - candidate.instruction.direction_modifier = - mirrorDirectionModifier(candidate.instruction.direction_modifier); - } - else - { - if (canBeSuppressed(candidate.instruction.type)) - candidate.instruction.type = TurnType::NewName; - } - } - else if (candidate.angle < obvious_with_same_name_angle) - candidate.instruction.direction_modifier = DirectionModifier::SlightRight; - else - candidate.instruction.direction_modifier = DirectionModifier::SlightLeft; - } - else if (candidate.instruction.direction_modifier == DirectionModifier::Straight && - has_obvious_with_same_name) - { - if (candidate.angle < obvious_with_same_name_angle) - candidate.instruction.direction_modifier = DirectionModifier::SlightRight; - else - candidate.instruction.direction_modifier = DirectionModifier::SlightLeft; - } - } - } + road.turn.instruction = (from == to_node) + ? TurnInstruction{TurnType::Turn, DirectionModifier::UTurn} + : TurnInstruction{findBasicTurnType(via_edge, road), + getTurnDirection(road.turn.angle)}; } - return turn_candidates; + return intersection; } // a @@ -1668,18 +1283,19 @@ TurnAnalysis::suppressTurns(const EdgeID via_eid, std::vector tur // That means we not only get (from_node, turn_node, c) in the above example // but also (from_node, turn_node, a), (from_node, turn_node, b). These turns are // marked as invalid and only needed for intersection classification. -std::vector TurnAnalysis::getTurnCandidates(const NodeID from_node, +std::vector TurnAnalysis::getConnectedRoads(const NodeID from_node, const EdgeID via_eid) const { - std::vector turn_candidates; + std::vector intersection; const NodeID turn_node = node_based_graph.GetTarget(via_eid); const NodeID only_restriction_to_node = restriction_map.CheckForEmanatingIsOnlyTurn(from_node, turn_node); const bool is_barrier_node = barrier_nodes.find(turn_node) != barrier_nodes.end(); + bool has_uturn_edge = false; for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(turn_node)) { - BOOST_ASSERT( onto_edge != SPECIAL_EDGEID ); + BOOST_ASSERT(onto_edge != SPECIAL_EDGEID); const NodeID to_node = node_based_graph.GetTarget(onto_edge); bool turn_is_valid = @@ -1717,6 +1333,7 @@ std::vector TurnAnalysis::getTurnCandidates(const NodeID from_nod turn_is_valid = number_of_emmiting_bidirectional_edges <= 1; } } + has_uturn_edge = true; BOOST_ASSERT(angle >= 0. && angle < std::numeric_limits::epsilon()); } else @@ -1728,312 +1345,925 @@ std::vector TurnAnalysis::getTurnCandidates(const NodeID from_nod turn_node, to_node, onto_edge, !INVERT, compressed_edge_container, node_info_list); angle = util::coordinate_calculation::computeAngle( first_coordinate, node_info_list[turn_node], third_coordinate); + if (angle < std::numeric_limits::epsilon()) + has_uturn_edge = true; } - turn_candidates.push_back( - {onto_edge, turn_is_valid, angle, {TurnType::Invalid, DirectionModifier::UTurn}, 0}); + intersection.push_back(ConnectedRoad( + TurnOperation{onto_edge, angle, {TurnType::Invalid, DirectionModifier::UTurn}}, + turn_is_valid)); + } + + // We hit the case of a street leading into nothing-ness. Since the code here assumes that this + // will + // never happen we add an artificial invalid uturn in this case. + if (!has_uturn_edge) + { + intersection.push_back( + {TurnOperation{via_eid, 0., {TurnType::Invalid, DirectionModifier::UTurn}}, false}); } - const auto ByAngle = [](const TurnCandidate &first, const TurnCandidate second) + const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) { - return first.angle < second.angle; + return first.turn.angle < second.turn.angle; }; - std::sort(std::begin(turn_candidates), std::end(turn_candidates), ByAngle); + std::sort(std::begin(intersection), std::end(intersection), ByAngle); - BOOST_ASSERT(turn_candidates[0].angle >= 0. && turn_candidates[0].angle < std::numeric_limits::epsilon()); + BOOST_ASSERT(intersection[0].turn.angle >= 0. && + intersection[0].turn.angle < std::numeric_limits::epsilon()); - return mergeSegregatedRoads(from_node, via_eid, std::move(turn_candidates)); + return mergeSegregatedRoads(std::move(intersection)); } -std::vector TurnAnalysis::mergeSegregatedRoads( - const NodeID from_node, const EdgeID via_eid, std::vector turn_candidates) const +/* + * Segregated Roads often merge onto a single intersection. + * While technically representing different roads, they are + * often looked at as a single road. + * Due to the merging, turn Angles seem off, wenn we compute them from the + * initial positions. + * + * bb>b>b(2)>b>b>b + * + * Would be seen as a slight turn going fro a to (2). A Sharp turn going from + * (1) to (2). + * + * In cases like these, we megre this segregated roads into a single road to + * end up with a case like: + * + * aaaaa-bbbbbb + * + * for the turn representation. + * Anything containing the first u-turn in a merge affects all other angles + * and is handled separately from all others. + */ +std::vector +TurnAnalysis::mergeSegregatedRoads(std::vector intersection) const { - (void)from_node; // FIXME - (void)via_eid; // FIXME -#define PRINT_SEGREGATION_INFO 0 - -#if PRINT_SEGREGATION_INFO - std::cout << "Input:\n"; - for (const auto &candidate : turn_candidates) - std::cout << "\t" << candidate.toString() << std::endl; -#endif - const auto getLeft = [&](std::size_t index) - { - return (index + 1) % turn_candidates.size(); - }; - (void)getLeft; // FIXME - const auto getRight = [&](std::size_t index) { - return (index + turn_candidates.size() - 1) % turn_candidates.size(); + return (index + intersection.size() - 1) % intersection.size(); }; const auto mergable = [&](std::size_t first, std::size_t second) -> bool { - const auto &first_data = node_based_graph.GetEdgeData(turn_candidates[first].eid); - const auto &second_data = node_based_graph.GetEdgeData(turn_candidates[second].eid); -#if PRINT_SEGREGATION_INFO - std::cout << "First: " << first_data.name_id << " " << first_data.travel_mode << " " - << first_data.road_classification.road_class << " " - << turn_candidates[first].angle << " " << first_data.reversed << "\n"; - std::cout << "Second: " << second_data.name_id << " " << second_data.travel_mode << " " - << second_data.road_classification.road_class << " " - << turn_candidates[second].angle << " " << second_data.reversed << std::endl; - std::cout << "Deviation: " - << angularDeviation(turn_candidates[first].angle, turn_candidates[second].angle) - << std::endl; -#endif + const auto &first_data = node_based_graph.GetEdgeData(intersection[first].turn.eid); + const auto &second_data = node_based_graph.GetEdgeData(intersection[second].turn.eid); return first_data.name_id != INVALID_NAME_ID && first_data.name_id == second_data.name_id && !first_data.roundabout && !second_data.roundabout && first_data.travel_mode == second_data.travel_mode && first_data.road_classification == second_data.road_classification && // compatible threshold - angularDeviation(turn_candidates[first].angle, turn_candidates[second].angle) < 60 && + angularDeviation(intersection[first].turn.angle, intersection[second].turn.angle) < + 60 && first_data.reversed != second_data.reversed; }; - const auto merge = [](const TurnCandidate &first, const TurnCandidate &second) -> TurnCandidate + const auto merge = [](const ConnectedRoad &first, const ConnectedRoad &second) -> ConnectedRoad { - if (!first.valid) + if (!first.entry_allowed) { - TurnCandidate result = second; - result.angle = (first.angle + second.angle) / 2; - if (first.angle - second.angle > 180) - result.angle += 180; - if (result.angle > 360) - result.angle -= 360; + ConnectedRoad result = second; + result.turn.angle = (first.turn.angle + second.turn.angle) / 2; + if (first.turn.angle - second.turn.angle > 180) + result.turn.angle += 180; + if (result.turn.angle > 360) + result.turn.angle -= 360; -#if PRINT_SEGREGATION_INFO - std::cout << "Merged: " << first.angle << " and " << second.angle << " to " - << result.angle << std::endl; -#endif return result; } else { - BOOST_ASSERT(!second.valid); - TurnCandidate result = first; - result.angle = (first.angle + second.angle) / 2; + BOOST_ASSERT(!second.entry_allowed); + ConnectedRoad result = first; + result.turn.angle = (first.turn.angle + second.turn.angle) / 2; - if (first.angle - second.angle > 180) - result.angle += 180; - if (result.angle > 360) - result.angle -= 360; + if (first.turn.angle - second.turn.angle > 180) + result.turn.angle += 180; + if (result.turn.angle > 360) + result.turn.angle -= 360; -#if PRINT_SEGREGATION_INFO - std::cout << "Merged: " << first.angle << " and " << second.angle << " to " - << result.angle << std::endl; -#endif return result; } }; - if (turn_candidates.size() == 1) - return turn_candidates; + if (intersection.size() == 1) + return intersection; - if (mergable(0, turn_candidates.size() - 1)) + // check for merges including the basic u-turn + // these result in an adjustment of all other angles + if (mergable(0, intersection.size() - 1)) { // std::cout << "First merge" << std::endl; const double correction_factor = - (360 - turn_candidates[turn_candidates.size() - 1].angle) / 2; - for (std::size_t i = 1; i + 1 < turn_candidates.size(); ++i) - turn_candidates[i].angle += correction_factor; - turn_candidates[0] = merge(turn_candidates.front(),turn_candidates.back()); - turn_candidates[0].angle = 0; - turn_candidates.pop_back(); + (360 - intersection[intersection.size() - 1].turn.angle) / 2; + for (std::size_t i = 1; i + 1 < intersection.size(); ++i) + intersection[i].turn.angle += correction_factor; + intersection[0] = merge(intersection.front(), intersection.back()); + intersection[0].turn.angle = 0; + intersection.pop_back(); } else if (mergable(0, 1)) { // std::cout << "First merge" << std::endl; - const double correction_factor = (turn_candidates[1].angle) / 2; - for (std::size_t i = 2; i < turn_candidates.size(); ++i) - turn_candidates[i].angle += correction_factor; - turn_candidates[0] = merge(turn_candidates[0],turn_candidates[1]); - turn_candidates[0].angle = 0; - turn_candidates.erase(turn_candidates.begin() + 1); + const double correction_factor = (intersection[1].turn.angle) / 2; + for (std::size_t i = 2; i < intersection.size(); ++i) + intersection[i].turn.angle += correction_factor; + intersection[0] = merge(intersection[0], intersection[1]); + intersection[0].turn.angle = 0; + intersection.erase(intersection.begin() + 1); } - for (std::size_t index = 2; index < turn_candidates.size(); ++index) + // a merge including the first u-turn requres an adjustment of the turn angles + // therefore these are handled prior to this step + for (std::size_t index = 2; index < intersection.size(); ++index) { if (mergable(index, getRight(index))) { - turn_candidates[getRight(index)] = - merge(turn_candidates[getRight(index)], turn_candidates[index]); - turn_candidates.erase(turn_candidates.begin() + index); + intersection[getRight(index)] = + merge(intersection[getRight(index)], intersection[index]); + intersection.erase(intersection.begin() + index); --index; } } - const auto ByAngle = [](const TurnCandidate &first, const TurnCandidate second) - { - return first.angle < second.angle; - }; - std::sort(std::begin(turn_candidates), std::end(turn_candidates), ByAngle); -#if PRINT_SEGREGATION_INFO - std::cout << "Result:\n"; - for (const auto &candidate : turn_candidates) - std::cout << "\t" << candidate.toString() << std::endl; -#endif - return turn_candidates; -} - -// node_u -- (edge_1) --> node_v -- (edge_2) --> node_w -TurnInstruction TurnAnalysis::AnalyzeTurn(const NodeID node_u, - const EdgeID edge1, - const NodeID node_v, - const EdgeID edge2, - const NodeID node_w, - const double angle) const -{ - (void)node_v; - const EdgeData &data1 = node_based_graph.GetEdgeData(edge1); - const EdgeData &data2 = node_based_graph.GetEdgeData(edge2); - bool from_ramp = isRampClass(data1.road_classification.road_class); - bool to_ramp = isRampClass(data2.road_classification.road_class); - if (node_u == node_w) - { - return {TurnType::Turn, DirectionModifier::UTurn}; - } - - if (!from_ramp && to_ramp) - { - return {TurnType::Ramp, getTurnDirection(angle)}; - } - - // assign a designated turn angle instruction purely based on the angle - return {TurnType::Turn, getTurnDirection(angle)}; -} - -std::vector TurnAnalysis::handleConflicts( - const NodeID from, const EdgeID via_edge, std::vector turn_candidates) const -{ - (void)from; // FIXME - (void)via_edge; // FIXME - const auto isConflict = [](const TurnCandidate &left, const TurnCandidate &right) + const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) { - // most obvious, same instructions conflict - if (left.instruction == right.instruction) - return true; - - return left.instruction.direction_modifier != DirectionModifier::UTurn && - left.instruction.direction_modifier == right.instruction.direction_modifier; + return first.turn.angle < second.turn.angle; }; - (void)isConflict; // FIXME -#if PRINT_DEBUG_CANDIDATES - std::cout << "Post Conflict Resolution Candidates:\n"; - for (auto tc : turn_candidates) - std::cout << "\t" << tc.toString() << " " - << (int)node_based_graph.GetEdgeData(tc.eid).road_classification.road_class - << " name: " << node_based_graph.GetEdgeData(tc.eid).name_id << std::endl; -#endif - return turn_candidates; + std::sort(std::begin(intersection), std::end(intersection), ByAngle); + return intersection; } void TurnAnalysis::assignFork(const EdgeID via_edge, - TurnCandidate &left, - TurnCandidate &right) const + ConnectedRoad &left, + ConnectedRoad &right) const { const auto &in_data = node_based_graph.GetEdgeData(via_edge); const bool low_priority_left = isLowPriorityRoadClass( - node_based_graph.GetEdgeData(left.eid).road_classification.road_class); + node_based_graph.GetEdgeData(left.turn.eid).road_classification.road_class); const bool low_priority_right = isLowPriorityRoadClass( - node_based_graph.GetEdgeData(right.eid).road_classification.road_class); + node_based_graph.GetEdgeData(right.turn.eid).road_classification.road_class); { // left fork - const auto &out_data = node_based_graph.GetEdgeData(left.eid); - if ((angularDeviation(left.angle, STRAIGHT_ANGLE) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION && - angularDeviation(right.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE)) + const auto &out_data = node_based_graph.GetEdgeData(left.turn.eid); + if ((angularDeviation(left.turn.angle, STRAIGHT_ANGLE) < + MAXIMAL_ALLOWED_NO_TURN_DEVIATION && + angularDeviation(right.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE)) { if (requiresAnnouncement(in_data, out_data)) { if (low_priority_right && !low_priority_left) - left.instruction = getInstructionForObvious(3, via_edge, left); + left.turn.instruction = getInstructionForObvious(3, via_edge, left); else { if (low_priority_left && !low_priority_right) - left.instruction = {TurnType::Turn, DirectionModifier::SlightLeft}; + left.turn.instruction = {TurnType::Turn, DirectionModifier::SlightLeft}; else - left.instruction = {TurnType::Fork, DirectionModifier::SlightLeft}; + left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft}; } } else { - left.instruction = {TurnType::Suppressed, DirectionModifier::Straight}; + left.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight}; } } else { if (low_priority_right && !low_priority_left) - left.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft}; + left.turn.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft}; else { if (low_priority_left && !low_priority_right) - left.instruction = {TurnType::Turn, DirectionModifier::SlightLeft}; + left.turn.instruction = {TurnType::Turn, DirectionModifier::SlightLeft}; else - left.instruction = {TurnType::Fork, DirectionModifier::SlightLeft}; + left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft}; } } } { // right fork - const auto &out_data = node_based_graph.GetEdgeData(right.eid); - if (angularDeviation(right.angle, STRAIGHT_ANGLE) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION && - angularDeviation(left.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE) + const auto &out_data = node_based_graph.GetEdgeData(right.turn.eid); + if (angularDeviation(right.turn.angle, STRAIGHT_ANGLE) < + MAXIMAL_ALLOWED_NO_TURN_DEVIATION && + angularDeviation(left.turn.angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE) { if (requiresAnnouncement(in_data, out_data)) { if (low_priority_left && !low_priority_right) - right.instruction = getInstructionForObvious(3, via_edge, right); + right.turn.instruction = getInstructionForObvious(3, via_edge, right); else { if (low_priority_right && !low_priority_left) - right.instruction = {TurnType::Turn, DirectionModifier::SlightRight}; + right.turn.instruction = {TurnType::Turn, DirectionModifier::SlightRight}; else - right.instruction = {TurnType::Fork, DirectionModifier::SlightRight}; + right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight}; } } else { - right.instruction = {TurnType::Suppressed, DirectionModifier::Straight}; + right.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight}; } } else { if (low_priority_left && !low_priority_right) - right.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft}; + right.turn.instruction = {TurnType::Suppressed, DirectionModifier::SlightLeft}; else { if (low_priority_right && !low_priority_left) - right.instruction = {TurnType::Turn, DirectionModifier::SlightRight}; + right.turn.instruction = {TurnType::Turn, DirectionModifier::SlightRight}; else - right.instruction = {TurnType::Fork, DirectionModifier::SlightRight}; + right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight}; } } } } void TurnAnalysis::assignFork(const EdgeID via_edge, - TurnCandidate &left, - TurnCandidate ¢er, - TurnCandidate &right) const + ConnectedRoad &left, + ConnectedRoad ¢er, + ConnectedRoad &right) const { - left.instruction = {TurnType::Fork, DirectionModifier::SlightLeft}; - if (angularDeviation(center.angle, 180) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + // TODO handle low priority road classes in a reasonable way + if (left.entry_allowed && center.entry_allowed && right.entry_allowed) { - const auto &in_data = node_based_graph.GetEdgeData(via_edge); - const auto &out_data = node_based_graph.GetEdgeData(center.eid); - if (requiresAnnouncement(in_data, out_data)) + left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft}; + if (angularDeviation(center.turn.angle, 180) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + { + const auto &in_data = node_based_graph.GetEdgeData(via_edge); + const auto &out_data = node_based_graph.GetEdgeData(center.turn.eid); + if (requiresAnnouncement(in_data, out_data)) + { + center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight}; + } + else + { + center.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight}; + } + } + else + { + center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight}; + } + right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight}; + } + else if (left.entry_allowed) + { + if (right.entry_allowed) + assignFork(via_edge, left, right); + else if (center.entry_allowed) + assignFork(via_edge, left, center); + else + left.turn.instruction = {findBasicTurnType(via_edge, left), + getTurnDirection(left.turn.angle)}; + } + else if (right.entry_allowed) + { + if (center.entry_allowed) + assignFork(via_edge, center, right); + else + right.turn.instruction = {findBasicTurnType(via_edge, right), + getTurnDirection(right.turn.angle)}; + } + else + { + if (center.entry_allowed) + center.turn.instruction = {findBasicTurnType(via_edge, center), + getTurnDirection(center.turn.angle)}; + } +} + +std::size_t TurnAnalysis::findObviousTurn(const EdgeID via_edge, + const std::vector &intersection) const +{ + // no obvious road + if (intersection.size() == 1) + return 0; + + // a single non u-turn is obvious + if (intersection.size() == 2) + return 1; + + // at least three roads + std::size_t best = 0; + double best_deviation = 180; + + std::size_t best_continue = 0; + double best_continue_deviation = 180; + + const EdgeData &in_data = node_based_graph.GetEdgeData(via_edge); + for (std::size_t i = 1; i < intersection.size(); ++i) + { + const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE); + if (intersection[i].entry_allowed && deviation < best_deviation) + { + best_deviation = deviation; + best = i; + } + + const auto out_data = node_based_graph.GetEdgeData(intersection[i].turn.eid); + if (intersection[i].entry_allowed && out_data.name_id == in_data.name_id && + deviation < best_continue_deviation) + { + best_continue_deviation = deviation; + best_continue = i; + } + } + + if (best == 0) + return 0; + + if (best_deviation >= 2 * NARROW_TURN_ANGLE) + return 0; + + // TODO incorporate road class in decision + if (best != 0 && best_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) + { + return best; + } + + // has no obvious continued road + if (best_continue == 0 || true) + { + // Find left/right deviation + const double left_deviation = angularDeviation( + intersection[(best + 1) % intersection.size()].turn.angle, STRAIGHT_ANGLE); + const double right_deviation = + angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE); + + if (best_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION && + std::min(left_deviation, right_deviation) > FUZZY_ANGLE_DIFFERENCE) + return best; + + // other narrow turns? + if (angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE) <= + FUZZY_ANGLE_DIFFERENCE) + return 0; + if (angularDeviation(intersection[(best + 1) % intersection.size()].turn.angle, + STRAIGHT_ANGLE) <= FUZZY_ANGLE_DIFFERENCE) + return 0; + + // Well distinct turn that is nearly straight + if (left_deviation / best_deviation >= DISTINCTION_RATIO && + right_deviation / best_deviation >= DISTINCTION_RATIO) + { + return best; + } + } + + return 0; // no obvious turn +} + +std::pair +TurnAnalysis::findFork(const std::vector &intersection) const +{ + + std::size_t best = 0; + double best_deviation = 180; + + // TODO handle road classes + for (std::size_t i = 1; i < intersection.size(); ++i) + { + const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE); + if (intersection[i].entry_allowed && deviation < best_deviation) + { + best_deviation = deviation; + best = i; + } + } + + if (best_deviation <= NARROW_TURN_ANGLE) + { + std::size_t left = best, right = best; + while (left + 1 < intersection.size() && + angularDeviation(intersection[left].turn.angle, intersection[left + 1].turn.angle) < + NARROW_TURN_ANGLE) + ++left; + while (right > 1 && + angularDeviation(intersection[right].turn.angle, + intersection[right - 1].turn.angle) < NARROW_TURN_ANGLE) + --right; + + // TODO check whether 2*NARROW_TURN is too large + if (right < left && + angularDeviation(intersection[left].turn.angle, + intersection[(left + 1) % intersection.size()].turn.angle) >= + 2 * NARROW_TURN_ANGLE && + angularDeviation(intersection[right].turn.angle, intersection[right - 1].turn.angle) >= + 2 * NARROW_TURN_ANGLE) + return std::make_pair(right, left); + } + return std::make_pair(0llu, 0llu); +} + +// Can only assign three turns +std::vector TurnAnalysis::assignLeftTurns(const EdgeID via_edge, + std::vector intersection, + const std::size_t starting_at) const +{ + const auto count_valid = [&intersection, starting_at]() + { + std::size_t count = 0; + for (std::size_t i = starting_at; i < intersection.size(); ++i) + if (intersection[i].entry_allowed) + ++count; + return count; + }; + if (starting_at == intersection.size() || count_valid() == 0) + return intersection; + // handle single turn + if (intersection.size() - starting_at == 1) + { + if (!intersection[starting_at].entry_allowed) + return intersection; + + if (angularDeviation(intersection[starting_at].turn.angle, STRAIGHT_ANGLE) > + NARROW_TURN_ANGLE && + angularDeviation(intersection[starting_at].turn.angle, 0) > NARROW_TURN_ANGLE) + { + // assign left turn + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), DirectionModifier::Left}; + } + else if (angularDeviation(intersection[starting_at].turn.angle, STRAIGHT_ANGLE) <= + NARROW_TURN_ANGLE) + { + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), + DirectionModifier::SlightLeft}; + } + else + { + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), + DirectionModifier::SharpLeft}; + } + } + // two turns on at the side + else if (intersection.size() - starting_at == 2) + { + const auto first_direction = getTurnDirection(intersection[starting_at].turn.angle); + const auto second_direction = getTurnDirection(intersection[starting_at + 1].turn.angle); + if (first_direction == second_direction) + { + // conflict + handleDistinctConflict(via_edge, intersection[starting_at + 1], + intersection[starting_at]); + } + else + { + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), first_direction}; + intersection[starting_at + 1].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 1]), second_direction}; + } + } + else if (intersection.size() - starting_at == 3) + { + const auto first_direction = getTurnDirection(intersection[starting_at].turn.angle); + const auto second_direction = getTurnDirection(intersection[starting_at + 1].turn.angle); + const auto third_direction = getTurnDirection(intersection[starting_at + 2].turn.angle); + if (first_direction != second_direction && second_direction != third_direction) + { + // implies first != third, based on the angles and clockwise order + if (intersection[starting_at].entry_allowed) + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), first_direction}; + if (intersection[starting_at + 1].entry_allowed) + intersection[starting_at + 1].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 1]), second_direction}; + if (intersection[starting_at + 2].entry_allowed) + intersection[starting_at + 2].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 2]), second_direction}; + } + else if (2 >= (intersection[starting_at].entry_allowed + + intersection[starting_at + 1].entry_allowed + + intersection[starting_at + 2].entry_allowed)) + { + // at least one invalid turn + if (!intersection[starting_at].entry_allowed) + { + handleDistinctConflict(via_edge, intersection[starting_at + 2], + intersection[starting_at + 1]); + } + else if (!intersection[starting_at + 1].entry_allowed) + { + handleDistinctConflict(via_edge, intersection[starting_at + 2], + intersection[starting_at]); + } + else + { + handleDistinctConflict(via_edge, intersection[starting_at + 1], + intersection[starting_at]); + } + } + else if (intersection[starting_at].entry_allowed && + intersection[starting_at + 1].entry_allowed && + intersection[starting_at + 2].entry_allowed && + angularDeviation(intersection[starting_at].turn.angle, + intersection[starting_at + 1].turn.angle) >= NARROW_TURN_ANGLE && + angularDeviation(intersection[starting_at + 1].turn.angle, + intersection[starting_at + 2].turn.angle) >= NARROW_TURN_ANGLE) + { + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), + DirectionModifier::SlightLeft}; + intersection[starting_at + 1].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 1]), + DirectionModifier::Left}; + intersection[starting_at + 2].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 2]), + DirectionModifier::SharpLeft}; + } + else if (intersection[starting_at].entry_allowed && + intersection[starting_at + 1].entry_allowed && + intersection[starting_at + 2].entry_allowed && + ((first_direction == second_direction && second_direction == third_direction) || + (third_direction == second_direction && + angularDeviation(intersection[starting_at].turn.angle, + intersection[starting_at + 1].turn.angle) < GROUP_ANGLE) || + (second_direction == first_direction && + angularDeviation(intersection[starting_at + 1].turn.angle, + intersection[starting_at + 2].turn.angle) < GROUP_ANGLE))) + { + intersection[starting_at].turn.instruction = { + detail::isRampClass(intersection[starting_at].turn.eid, node_based_graph) + ? FirstRamp + : FirstTurn, + second_direction}; + intersection[starting_at + 1].turn.instruction = { + detail::isRampClass(intersection[starting_at + 1].turn.eid, node_based_graph) + ? SecondRamp + : SecondTurn, + second_direction}; + intersection[starting_at + 2].turn.instruction = { + detail::isRampClass(intersection[starting_at + 2].turn.eid, node_based_graph) + ? ThirdRamp + : ThirdTurn, + second_direction}; + } + else if (intersection[starting_at].entry_allowed && + intersection[starting_at + 1].entry_allowed && + intersection[starting_at + 2].entry_allowed && + ((third_direction == second_direction && + angularDeviation(intersection[starting_at].turn.angle, + intersection[starting_at + 1].turn.angle) >= GROUP_ANGLE) || + (second_direction == first_direction && + angularDeviation(intersection[starting_at + 1].turn.angle, + intersection[starting_at + 2].turn.angle) >= GROUP_ANGLE))) + { + // conflict one side with an additional very sharp turn + if (angularDeviation(intersection[starting_at + 1].turn.angle, + intersection[starting_at + 2].turn.angle) >= GROUP_ANGLE) + { + handleDistinctConflict(via_edge, intersection[starting_at + 1], + intersection[starting_at]); + intersection[starting_at + 2].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 2]), third_direction}; + } + else + { + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), first_direction}; + handleDistinctConflict(via_edge, intersection[starting_at + 2], + intersection[starting_at + 1]); + } + } + + else if ((first_direction == second_direction && + intersection[starting_at].entry_allowed != + intersection[starting_at + 1].entry_allowed) || + (second_direction == third_direction && + intersection[starting_at + 1].entry_allowed != + intersection[starting_at + 2].entry_allowed)) + { + // no conflict, due to conflict being restricted to valid/invalid + if (intersection[starting_at].entry_allowed) + intersection[starting_at].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at]), first_direction}; + if (intersection[starting_at + 1].entry_allowed) + intersection[starting_at + 1].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 1]), second_direction}; + if (intersection[starting_at + 2].entry_allowed) + intersection[starting_at + 2].turn.instruction = { + findBasicTurnType(via_edge, intersection[starting_at + 2]), third_direction}; + } + else + { + auto coord = localizer(node_based_graph.GetTarget(via_edge)); + util::SimpleLogger().Write(logWARNING) + << "Reached fallback for left turns, size 3: " << std::setprecision(12) + << toFloating(coord.lat) << " " << toFloating(coord.lon); + for (const auto road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + util::SimpleLogger().Write(logWARNING) + << "\troad: " << road.toString() << " Name: " << out_data.name_id + << " Road Class: " << (int)out_data.road_classification.road_class + << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid)); + } + + for (std::size_t i = starting_at; i < intersection.size(); ++i) + if (intersection[i].entry_allowed) + intersection[i].turn.instruction = { + findBasicTurnType(via_edge, intersection[i]), + getTurnDirection(intersection[i].turn.angle)}; + } + } + else if (intersection.size() - starting_at == 4) + { + if (intersection[starting_at].entry_allowed) + intersection[starting_at].turn.instruction = { + detail::isRampClass(intersection[starting_at].turn.eid, node_based_graph) + ? FirstRamp + : FirstTurn, + DirectionModifier::Left}; + if (intersection[starting_at + 1].entry_allowed) + intersection[starting_at + 1].turn.instruction = { + detail::isRampClass(intersection[starting_at + 1].turn.eid, node_based_graph) + ? SecondRamp + : SecondTurn, + DirectionModifier::Left}; + if (intersection[starting_at + 2].entry_allowed) + intersection[starting_at + 2].turn.instruction = { + detail::isRampClass(intersection[starting_at + 2].turn.eid, node_based_graph) + ? ThirdRamp + : ThirdTurn, + DirectionModifier::Left}; + if (intersection[starting_at + 3].entry_allowed) + intersection[starting_at + 3].turn.instruction = { + detail::isRampClass(intersection[starting_at + 3].turn.eid, node_based_graph) + ? FourthRamp + : FourthTurn, + DirectionModifier::Left}; + } + else + { + for (auto &road : intersection) + { + if (!road.entry_allowed) + continue; + road.turn.instruction = {detail::isRampClass(road.turn.eid, node_based_graph) ? Ramp + : Turn, + getTurnDirection(road.turn.angle)}; + } + /* + auto coord = localizer(node_based_graph.GetTarget(via_edge)); + util::SimpleLogger().Write(logWARNING) + << "Reached fallback for left turns (" << starting_at << ") " << std::setprecision(12) + << toFloating(coord.lat) << " " << toFloating(coord.lon); + for (const auto road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + util::SimpleLogger().Write(logWARNING) + << "\troad: " << road.toString() << " Name: " << out_data.name_id + << " Road Class: " << (int)out_data.road_classification.road_class + << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid)); + } + */ + } + return intersection; +} + +// can only assign three turns +std::vector TurnAnalysis::assignRightTurns(const EdgeID via_edge, + std::vector intersection, + const std::size_t up_to) const +{ + BOOST_ASSERT(up_to <= intersection.size()); + const auto count_valid = [&intersection, up_to]() + { + std::size_t count = 0; + for (std::size_t i = 1; i < up_to; ++i) + if (intersection[i].entry_allowed) + ++count; + return count; + }; + if (up_to <= 1 || count_valid() == 0) + return intersection; + // handle single turn + if (up_to == 2) + { + if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) > NARROW_TURN_ANGLE && + angularDeviation(intersection[1].turn.angle, 0) > NARROW_TURN_ANGLE) + { + // assign left turn + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::Right}; + } + else if (angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) <= NARROW_TURN_ANGLE) + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::SlightRight}; + } + else { - center.instruction = {TurnType::Fork, DirectionModifier::Straight}; + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::SharpRight}; + } + } + else if (up_to == 3) + { + const auto first_direction = getTurnDirection(intersection[1].turn.angle); + const auto second_direction = getTurnDirection(intersection[2].turn.angle); + if (first_direction == second_direction) + { + // conflict + handleDistinctConflict(via_edge, intersection[2], intersection[1]); + } + else + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + first_direction}; + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + second_direction}; + } + } + else if (up_to == 4) + { + const auto first_direction = getTurnDirection(intersection[1].turn.angle); + const auto second_direction = getTurnDirection(intersection[2].turn.angle); + const auto third_direction = getTurnDirection(intersection[3].turn.angle); + if (first_direction != second_direction && second_direction != third_direction) + { + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + first_direction}; + if (intersection[2].entry_allowed) + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + second_direction}; + if (intersection[3].entry_allowed) + intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]), + third_direction}; + } + else if (2 >= (intersection[1].entry_allowed + intersection[2].entry_allowed + + intersection[3].entry_allowed)) + { + // at least a single invalid + if (!intersection[3].entry_allowed) + { + handleDistinctConflict(via_edge, intersection[2], intersection[1]); + } + else if (!intersection[1].entry_allowed) + { + handleDistinctConflict(via_edge, intersection[3], intersection[2]); + } + else // handles one-valid as well as two valid (1,3) + { + handleDistinctConflict(via_edge, intersection[3], intersection[1]); + } + } + + else if (intersection[1].entry_allowed && intersection[2].entry_allowed && + intersection[3].entry_allowed && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >= + NARROW_TURN_ANGLE && + angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >= + NARROW_TURN_ANGLE) + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + DirectionModifier::SharpRight}; + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + DirectionModifier::Right}; + intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]), + DirectionModifier::SlightRight}; + } + else if (intersection[1].entry_allowed && intersection[2].entry_allowed && + intersection[3].entry_allowed && + ((first_direction == second_direction && second_direction == third_direction) || + (first_direction == second_direction && + angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) < + GROUP_ANGLE) || + (second_direction == third_direction && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) < + GROUP_ANGLE))) + { + intersection[1].turn.instruction = { + detail::isRampClass(intersection[1].turn.eid, node_based_graph) ? ThirdRamp + : ThirdTurn, + second_direction}; + intersection[2].turn.instruction = { + detail::isRampClass(intersection[2].turn.eid, node_based_graph) ? SecondRamp + : SecondTurn, + second_direction}; + intersection[3].turn.instruction = { + detail::isRampClass(intersection[3].turn.eid, node_based_graph) ? FirstRamp + : FirstTurn, + second_direction}; + } + else if (intersection[1].entry_allowed && intersection[2].entry_allowed && + intersection[3].entry_allowed && + ((first_direction == second_direction && + angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >= + GROUP_ANGLE) || + (second_direction == third_direction && + angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >= + GROUP_ANGLE))) + { + if (angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >= + GROUP_ANGLE) + { + handleDistinctConflict(via_edge, intersection[2], intersection[1]); + intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]), + third_direction}; + } + else + { + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + first_direction}; + handleDistinctConflict(via_edge, intersection[3], intersection[2]); + } + } + else if ((first_direction == second_direction && + intersection[1].entry_allowed != intersection[2].entry_allowed) || + (second_direction == third_direction && + intersection[2].entry_allowed != intersection[3].entry_allowed)) + { + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]), + first_direction}; + if (intersection[2].entry_allowed) + intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]), + second_direction}; + if (intersection[3].entry_allowed) + intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]), + third_direction}; } else { - center.instruction = {TurnType::Suppressed, DirectionModifier::Straight}; + auto coord = localizer(node_based_graph.GetTarget(via_edge)); + util::SimpleLogger().Write(logWARNING) + << "Reached fallback for right turns, size 3: " << std::setprecision(12) + << toFloating(coord.lat) << " " << toFloating(coord.lon) + << " Valids: " << (intersection[1].entry_allowed + intersection[2].entry_allowed + + intersection[3].entry_allowed); + for (const auto road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + util::SimpleLogger().Write(logWARNING) + << "\troad: " << road.toString() << " Name: " << out_data.name_id + << " Road Class: " << (int)out_data.road_classification.road_class + << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid)); + } + + for (std::size_t i = 1; i < up_to; ++i) + if (intersection[i].entry_allowed) + intersection[i].turn.instruction = { + findBasicTurnType(via_edge, intersection[i]), + getTurnDirection(intersection[i].turn.angle)}; } } + else if (up_to == 5) + { + if (intersection[4].entry_allowed) + intersection[4].turn.instruction = { + detail::isRampClass(intersection[4].turn.eid, node_based_graph) ? FirstRamp + : FirstTurn, + DirectionModifier::Right}; + if (intersection[3].entry_allowed) + intersection[3].turn.instruction = { + detail::isRampClass(intersection[3].turn.eid, node_based_graph) ? SecondRamp + : SecondTurn, + DirectionModifier::Right}; + if (intersection[2].entry_allowed) + intersection[2].turn.instruction = { + detail::isRampClass(intersection[2].turn.eid, node_based_graph) ? ThirdRamp + : ThirdTurn, + DirectionModifier::Right}; + if (intersection[1].entry_allowed) + intersection[1].turn.instruction = { + detail::isRampClass(intersection[1].turn.eid, node_based_graph) ? FourthRamp + : FourthTurn, + DirectionModifier::Right}; + } else { - center.instruction = {TurnType::Fork, DirectionModifier::Straight}; + for (std::size_t i = 1; i < up_to; ++i) + { + auto &road = intersection[i]; + if (!road.entry_allowed) + continue; + road.turn.instruction = {detail::isRampClass(road.turn.eid, node_based_graph) ? Ramp + : Turn, + getTurnDirection(road.turn.angle)}; + } + + /* + auto coord = localizer(node_based_graph.GetTarget(via_edge)); + util::SimpleLogger().Write(logWARNING) + << "Reached fallback for right turns (" << up_to << ") " << std::setprecision(12) + << toFloating(coord.lat) << " " << toFloating(coord.lon); + for (const auto road : intersection) + { + const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); + util::SimpleLogger().Write(logWARNING) + << "\troad: " << road.toString() << " Name: " << out_data.name_id + << " Road Class: " << (int)out_data.road_classification.road_class + << " At: " << localizer(node_based_graph.GetTarget(road.turn.eid)); + } + */ } - right.instruction = {TurnType::Fork, DirectionModifier::SlightRight}; + return intersection; } } // namespace guidance diff --git a/src/util/name_table.cpp b/src/util/name_table.cpp new file mode 100644 index 00000000000..e3d5cef3511 --- /dev/null +++ b/src/util/name_table.cpp @@ -0,0 +1,62 @@ +#include "util/name_table.hpp" +#include "util/simple_logger.hpp" +#include "util/exception.hpp" + +#include +#include +#include + +#include + +namespace osrm +{ +namespace util +{ + +NameTable::NameTable(const std::string &filename) +{ + boost::filesystem::ifstream name_stream(filename, std::ios::binary); + + if( !name_stream ) + throw exception("Failed to open " + filename + " for reading."); + + name_stream >> m_name_table; + + unsigned number_of_chars = 0; + name_stream.read(reinterpret_cast(&number_of_chars), sizeof(number_of_chars)); + if( !name_stream ) + throw exception("Encountered invalid file, failed to read number of contained 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(&m_names_char_list[0]), + number_of_chars * sizeof(m_names_char_list[0])); + if( !name_stream ) + throw exception("Failed to read " + std::to_string(number_of_chars) + " characters from file."); + + if (0 == m_names_char_list.size()) + { + util::SimpleLogger().Write(logWARNING) << "list of street names is empty"; + } +} + +std::string NameTable::GetNameForID(const unsigned name_id) const +{ + if (std::numeric_limits::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