From 3d03797e53dcece975865ca5bd82efeb28184892 Mon Sep 17 00:00:00 2001 From: Daniel Patterson Date: Wed, 25 May 2016 15:24:11 +0200 Subject: [PATCH] Distinguish between offramps and sliproads. --- CHANGELOG.md | 7 ++ .../guidance/dedicated-turn-roads.feature | 117 ++++++++++++++++++ include/engine/guidance/assemble_steps.hpp | 80 ++++++++---- .../guidance/classification_data.hpp | 11 ++ include/extractor/guidance/constants.hpp | 4 +- .../extractor/guidance/motorway_handler.hpp | 8 +- include/extractor/guidance/turn_analysis.hpp | 3 + .../extractor/guidance/turn_instruction.hpp | 63 +++++----- include/util/typedefs.hpp | 7 +- src/engine/api/json_factory.cpp | 12 +- src/engine/guidance/post_processing.cpp | 113 +++++++++++------ src/extractor/guidance/motorway_handler.cpp | 16 +-- src/extractor/guidance/turn_analysis.cpp | 101 ++++++++++++++- 13 files changed, 428 insertions(+), 114 deletions(-) create mode 100644 features/guidance/dedicated-turn-roads.feature diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bb0ab056..145d8cde9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +# 5.2 + Changes from 5.2.0 RC2 + + - Guidance: + - improved handling of sliproads (emit turns instead of 'take the ramp') + BREAKING: modifies the file format with new internal identifiers + # 5.2.0 RC1 Changes from 5.1.0 diff --git a/features/guidance/dedicated-turn-roads.feature b/features/guidance/dedicated-turn-roads.feature new file mode 100644 index 000000000..64dd7cdab --- /dev/null +++ b/features/guidance/dedicated-turn-roads.feature @@ -0,0 +1,117 @@ +@routing @guidance +Feature: Slipways and Dedicated Turn Lanes + + Background: + Given the profile "car" + Given a grid size of 5 meters + + Scenario: Turn Instead of Ramp + Given the node map + | | | | | e | | + | a | b | | | c | d | + | | | | h | | | + | | | | | | | + | | | | 1 | | | + | | | | | | | + | | | | | f | | + | | | | | | | + | | | | | g | | + + And the ways + | nodes | highway | name | + | abcd | trunk | first | + | bhf | trunk_link | | + | ecfg | primary | second | + + When I route I should get + | waypoints | route | turns | + | a,g | first,second,second | depart,turn right,arrive | + | a,1 | first,, | depart,turn slight right,arrive | + + Scenario: Turn Instead of Ramp + Given the node map + | | | | | e | | + | a | b | | | c | d | + | | | | h | | | + | | | | | | | + | | | | | | | + | | | | | | | + | | | | | f | | + | | | | | | | + | | | | | | | + | | | | | g | | + + And the ways + | nodes | highway | name | + | abcd | motorway | first | + | bhf | motorway_link | | + | efg | primary | second | + + When I route I should get + | waypoints | route | turns | + | a,g | first,,second,second | depart,off ramp slight right,merge slight left,arrive | + + Scenario: Inner city expressway with on road + Given the node map + | a | b | | | | c | + | | | | | f | | + | | | | | | | + | | | | | | | + | | | | | | | + | | | | | | d | + | | | | | | | + | | | | | | | + | | | | | | | + | | | | | | e | + + And the ways + | nodes | highway | name | + | abc | primary | road | + | bfd | trunk_link | | + | cde | trunk | trunk | + + When I route I should get + | waypoints | route | turns | + | a,e | road,trunk,trunk | depart,turn right,arrive | + + + Scenario: Slipway Round U-Turn + Given the node map + | a | | f | + | | | | + | b | | e | + | | | | + | | | | + | | g | | + | | | | + | c | | d | + + And the ways + | nodes | highway | name | oneway | + | abc | primary | road | yes | + | bge | primary_link | | yes | + | def | primary | road | yes | + + When I route I should get + | waypoints | route | turns | + | a,f | road,road,road | depart,continue uturn,arrive | + + Scenario: Slipway Steep U-Turn + Given the node map + | a | | f | + | | | | + | b | | e | + | | g | | + | | | | + | | | | + | c | | d | + + And the ways + | nodes | highway | name | oneway | + | abc | primary | road | yes | + | bge | primary_link | | yes | + | def | primary | road | yes | + + When I route I should get + | waypoints | route | turns | + | a,f | road,road,road | depart,continue uturn,arrive | diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp index b359857e0..4bbcbb4e9 100644 --- a/include/engine/guidance/assemble_steps.hpp +++ b/include/engine/guidance/assemble_steps.hpp @@ -12,8 +12,8 @@ #include "util/bearing.hpp" #include "util/coordinate.hpp" #include "util/coordinate_calculation.hpp" -#include "util/guidance/toolkit.hpp" #include "util/guidance/entry_class.hpp" +#include "util/guidance/toolkit.hpp" #include "util/typedefs.hpp" #include @@ -68,10 +68,8 @@ std::vector assembleSteps(const DataFacadeT &facade, StepManeuver maneuver{source_node.location, bearings.first, bearings.second, extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, 0}; - Intersection intersection{ - source_node.location, - std::vector({bearings.second}), - std::vector({true}), Intersection::NO_INDEX, 0}; + Intersection intersection{source_node.location, std::vector({bearings.second}), + std::vector({true}), Intersection::NO_INDEX, 0}; if (leg_data.size() > 0) { @@ -96,6 +94,17 @@ std::vector assembleSteps(const DataFacadeT &facade, const auto name = facade.GetNameForID(step_name_id); const auto distance = leg_geometry.segment_distances[segment_index]; + steps.push_back(RouteStep{step_name_id, + name, + NO_ROTARY_NAME, + segment_duration / 10.0, + distance, + path_point.travel_mode, + maneuver, + leg_geometry.FrontIndex(segment_index), + leg_geometry.BackIndex(segment_index) + 1, + {intersection}}); + if (leg_data_index + 1 < leg_data.size()) { step_name_id = leg_data[leg_data_index + 1].name_id; @@ -104,26 +113,26 @@ std::vector assembleSteps(const DataFacadeT &facade, { step_name_id = target_node.name_id; } - steps.push_back(RouteStep{ - step_name_id, name, NO_ROTARY_NAME, segment_duration / 10.0, distance, - path_point.travel_mode, maneuver, leg_geometry.FrontIndex(segment_index), - leg_geometry.BackIndex(segment_index) + 1, {intersection}}); bearings = detail::getIntermediateBearings(leg_geometry, segment_index); const auto entry_class = facade.GetEntryClass(path_point.entry_classid); - const auto bearing_class = facade.GetBearingClass(facade.GetBearingClassID(path_point.turn_via_node)); - intersection.in = bearing_class.findMatchingBearing(util::bearing::reverseBearing(bearings.first)); + const auto bearing_class = + facade.GetBearingClass(facade.GetBearingClassID(path_point.turn_via_node)); + intersection.in = bearing_class.findMatchingBearing( + util::bearing::reverseBearing(bearings.first)); intersection.out = bearing_class.findMatchingBearing(bearings.second); intersection.location = facade.GetCoordinateOfNode(path_point.turn_via_node); intersection.bearings.clear(); - std::copy(bearing_class.getAvailableBearings().begin(), bearing_class.getAvailableBearings().end(), + std::copy(bearing_class.getAvailableBearings().begin(), + bearing_class.getAvailableBearings().end(), std::back_inserter(intersection.bearings)); intersection.entry.clear(); for (auto idx : util::irange(0, intersection.bearings.size())) { intersection.entry.push_back(entry_class.allowsEntry(idx)); } - maneuver = {intersection.location, bearings.first, bearings.second, path_point.turn_instruction, WaypointType::None, 0}; + maneuver = {intersection.location, bearings.first, bearings.second, + path_point.turn_instruction, WaypointType::None, 0}; segment_index++; segment_duration = 0; } @@ -131,10 +140,16 @@ std::vector assembleSteps(const DataFacadeT &facade, const auto distance = leg_geometry.segment_distances[segment_index]; const int duration = segment_duration + target_duration; BOOST_ASSERT(duration >= 0); - steps.push_back(RouteStep{ - step_name_id, facade.GetNameForID(step_name_id), NO_ROTARY_NAME, duration / 10., - distance, target_mode, maneuver, leg_geometry.FrontIndex(segment_index), - leg_geometry.BackIndex(segment_index) + 1, {intersection}}); + steps.push_back(RouteStep{step_name_id, + facade.GetNameForID(step_name_id), + NO_ROTARY_NAME, + duration / 10., + distance, + target_mode, + maneuver, + leg_geometry.FrontIndex(segment_index), + leg_geometry.BackIndex(segment_index) + 1, + {intersection}}); } // In this case the source + target are on the same edge segment else @@ -148,30 +163,41 @@ std::vector assembleSteps(const DataFacadeT &facade, int duration = target_duration - source_duration; BOOST_ASSERT(duration >= 0); - steps.push_back(RouteStep{ - source_node.name_id, facade.GetNameForID(source_node.name_id), NO_ROTARY_NAME, - duration / 10., leg_geometry.segment_distances[segment_index], source_mode, - std::move(maneuver), leg_geometry.FrontIndex(segment_index), - leg_geometry.BackIndex(segment_index) + 1, {intersection}}); + steps.push_back(RouteStep{source_node.name_id, + facade.GetNameForID(source_node.name_id), + NO_ROTARY_NAME, + duration / 10., + leg_geometry.segment_distances[segment_index], + source_mode, + std::move(maneuver), + leg_geometry.FrontIndex(segment_index), + leg_geometry.BackIndex(segment_index) + 1, + {intersection}}); } BOOST_ASSERT(segment_index == number_of_segments - 1); bearings = detail::getArriveBearings(leg_geometry); // This step has length zero, the only reason we need it is the target location - maneuver = {intersection.location, bearings.first, bearings.second, extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, 0}; + maneuver = {intersection.location, bearings.first, + bearings.second, extractor::guidance::TurnInstruction::NO_TURN(), + WaypointType::Arrive, 0}; intersection = { target_node.location, std::vector({static_cast(util::bearing::reverseBearing(bearings.first))}), std::vector({true}), 0, Intersection::NO_INDEX}; BOOST_ASSERT(!leg_geometry.locations.empty()); - steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id), - NO_ROTARY_NAME, ZERO_DURATION, ZERO_DISTANCE, target_mode, - std::move(maneuver), leg_geometry.locations.size() - 1, + steps.push_back(RouteStep{target_node.name_id, + facade.GetNameForID(target_node.name_id), + NO_ROTARY_NAME, + ZERO_DURATION, + ZERO_DISTANCE, + target_mode, + std::move(maneuver), + leg_geometry.locations.size() - 1, leg_geometry.locations.size(), {intersection}}); - BOOST_ASSERT(steps.front().intersections.size() == 1); BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1); BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1); diff --git a/include/extractor/guidance/classification_data.hpp b/include/extractor/guidance/classification_data.hpp index 53f54cf46..fd63e4f06 100644 --- a/include/extractor/guidance/classification_data.hpp +++ b/include/extractor/guidance/classification_data.hpp @@ -47,6 +47,17 @@ inline bool isRampClass(const FunctionalRoadClass road_class) road_class == FunctionalRoadClass::TRUNK_LINK; } +// Links are usually smaller than ramps, but are sometimes tagged +// as MOTORWAY_LINK if they exit/enter a motorway/trunk road. +inline bool isLinkClass(const FunctionalRoadClass road_class) +{ + return road_class == FunctionalRoadClass::MOTORWAY_LINK || + road_class == FunctionalRoadClass::TRUNK_LINK || + road_class == FunctionalRoadClass::PRIMARY_LINK || + road_class == FunctionalRoadClass::SECONDARY_LINK || + road_class == FunctionalRoadClass::TERTIARY_LINK; +} + // TODO augment this with all data required for guidance generation struct RoadClassificationData { diff --git a/include/extractor/guidance/constants.hpp b/include/extractor/guidance/constants.hpp index 3e4ae6ded..c9ddad7c2 100644 --- a/include/extractor/guidance/constants.hpp +++ b/include/extractor/guidance/constants.hpp @@ -22,10 +22,12 @@ const double constexpr FUZZY_ANGLE_DIFFERENCE = 20.; const double constexpr DISTINCTION_RATIO = 2; const unsigned constexpr INVALID_NAME_ID = 0; -const double constexpr MAX_ROUNDABOUT_INTERSECTION_RADIUS = 5; +const double constexpr MAX_ROUNDABOUT_INTERSECTION_RADIUS = 5; const double constexpr MAX_ROUNDABOUT_RADIUS = 15; // 30 m diameter as final distinction const double constexpr INCREASES_BY_FOURTY_PERCENT = 1.4; +const unsigned constexpr MAX_SLIPROAD_THRESHOLD = 250; + } // namespace guidance } // namespace extractor } // namespace osrm diff --git a/include/extractor/guidance/motorway_handler.hpp b/include/extractor/guidance/motorway_handler.hpp index 42805c5cb..75abe88a8 100644 --- a/include/extractor/guidance/motorway_handler.hpp +++ b/include/extractor/guidance/motorway_handler.hpp @@ -2,6 +2,7 @@ #define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_ #include "extractor/guidance/intersection.hpp" +#include "extractor/guidance/intersection_generator.hpp" #include "extractor/guidance/intersection_handler.hpp" #include "extractor/query_node.hpp" @@ -25,7 +26,8 @@ class MotorwayHandler : public IntersectionHandler MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph, const std::vector &node_info_list, const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table); + const SuffixTable &street_name_suffix_table, + const IntersectionGenerator &intersection_generator); ~MotorwayHandler() override final; // check whether the handler can actually handle the intersection @@ -39,10 +41,14 @@ class MotorwayHandler : public IntersectionHandler Intersection intersection) const override final; private: + Intersection handleSliproads(const NodeID intersection_node_id, + Intersection intersection) const; Intersection fromMotorway(const EdgeID via_edge, Intersection intersection) const; Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const; Intersection fallback(Intersection intersection) const; + + const IntersectionGenerator &intersection_generator; }; } // namespace guidance diff --git a/include/extractor/guidance/turn_analysis.hpp b/include/extractor/guidance/turn_analysis.hpp index 7d700a3ed..cade181ad 100644 --- a/include/extractor/guidance/turn_analysis.hpp +++ b/include/extractor/guidance/turn_analysis.hpp @@ -59,6 +59,9 @@ class TurnAnalysis // Utility function, setting basic turn types. Prepares for normal turn handling. Intersection setTurnTypes(const NodeID from, const EdgeID via_edge, Intersection intersection) const; + + Intersection handleSliproads(const NodeID intersection_node_id, + Intersection intersection) const; }; // class TurnAnalysis } // namespace guidance diff --git a/include/extractor/guidance/turn_instruction.hpp b/include/extractor/guidance/turn_instruction.hpp index 29557340e..08e82aa9f 100644 --- a/include/extractor/guidance/turn_instruction.hpp +++ b/include/extractor/guidance/turn_instruction.hpp @@ -20,7 +20,6 @@ namespace detail const constexpr uint8_t num_direction_modifiers = 8; } // detail - // direction modifiers based on angle namespace DirectionModifier { @@ -38,31 +37,36 @@ const constexpr Enum SharpLeft = 7; namespace TurnType { typedef std::uint8_t Enum; -const constexpr Enum Invalid = 0; // no valid turn instruction -const constexpr Enum NewName = 1; // no turn, but name changes -const constexpr Enum Continue = 2; // remain on a street -const constexpr Enum Turn = 3; // basic turn -const constexpr Enum Merge = 4; // merge onto a street -const constexpr Enum OnRamp = 5; // special turn (highway ramp on-ramps) -const constexpr Enum OffRamp = 6; // special turn, highway exit -const constexpr Enum Fork = 7; // fork road splitting up -const constexpr Enum EndOfRoad = 8; // T intersection -const constexpr Enum Notification = 9; // Travel Mode Changes, Restrictions apply... -const constexpr Enum EnterRoundabout = 10; // Entering a small Roundabout -const constexpr Enum EnterAndExitRoundabout = 11; // Touching a roundabout -const constexpr Enum EnterRotary = 12; // Enter a rotary -const constexpr Enum EnterAndExitRotary = 13; // Touching a rotary -const constexpr Enum EnterRoundaboutIntersection = 14; // Entering a small Roundabout +const constexpr Enum Invalid = 0; // no valid turn instruction +const constexpr Enum NewName = 1; // no turn, but name changes +const constexpr Enum Continue = 2; // remain on a street +const constexpr Enum Turn = 3; // basic turn +const constexpr Enum Merge = 4; // merge onto a street +const constexpr Enum OnRamp = 5; // special turn (highway ramp on-ramps) +const constexpr Enum OffRamp = 6; // special turn, highway exit +const constexpr Enum Fork = 7; // fork road splitting up +const constexpr Enum EndOfRoad = 8; // T intersection +const constexpr Enum Notification = 9; // Travel Mode Changes, Restrictions apply... +const constexpr Enum EnterRoundabout = 10; // Entering a small Roundabout +const constexpr Enum EnterAndExitRoundabout = 11; // Touching a roundabout +const constexpr Enum EnterRotary = 12; // Enter a rotary +const constexpr Enum EnterAndExitRotary = 13; // Touching a rotary +const constexpr Enum EnterRoundaboutIntersection = 14; // Entering a small Roundabout const constexpr Enum EnterAndExitRoundaboutIntersection = 15; // Touching a roundabout -const constexpr Enum NoTurn = 16; // end of segment without turn/middle of a segment -const constexpr Enum Suppressed = 17; // location that suppresses a turn -const constexpr Enum EnterRoundaboutAtExit = 18; // Entering a small Roundabout at a countable exit -const constexpr Enum ExitRoundabout = 19; // Exiting a small Roundabout -const constexpr Enum EnterRotaryAtExit = 20; // Enter A Rotary at a countable exit -const constexpr Enum ExitRotary = 21; // Exit a rotary -const constexpr Enum EnterRoundaboutIntersectionAtExit = 22; // Entering a small Roundabout at a countable exit -const constexpr Enum ExitRoundaboutIntersection = 23; // Exiting a small Roundabout -const constexpr Enum StayOnRoundabout = 24; // Continue on Either a small or a large Roundabout + +// Values below here are silent instructions +const constexpr Enum NoTurn = 16; // end of segment without turn/middle of a segment +const constexpr Enum Suppressed = 17; // location that suppresses a turn +const constexpr Enum EnterRoundaboutAtExit = 18; // Entering a small Roundabout at a countable exit +const constexpr Enum ExitRoundabout = 19; // Exiting a small Roundabout +const constexpr Enum EnterRotaryAtExit = 20; // Enter A Rotary at a countable exit +const constexpr Enum ExitRotary = 21; // Exit a rotary +const constexpr Enum EnterRoundaboutIntersectionAtExit = + 22; // Entering a small Roundabout at a countable exit +const constexpr Enum ExitRoundaboutIntersection = 23; // Exiting a small Roundabout +const constexpr Enum StayOnRoundabout = 24; // Continue on Either a small or a large Roundabout +const constexpr Enum Sliproad = + 25; // Something that looks like a ramp, but is actually just a small sliproad } // turn angle in 1.40625 degree -> 128 == 180 degree @@ -87,7 +91,8 @@ struct TurnInstruction return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn); } - static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType, const DirectionModifier::Enum modifier) + static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType, + const DirectionModifier::Enum modifier) { return TurnInstruction(TurnType::StayOnRoundabout, modifier); } @@ -104,9 +109,9 @@ struct TurnInstruction static TurnInstruction EXIT_ROUNDABOUT(const RoundaboutType roundabout_type, const DirectionModifier::Enum modifier) { - const constexpr TurnType::Enum exit_instruction[] = {TurnType::Invalid, TurnType::ExitRoundabout, - TurnType::ExitRotary, - TurnType::ExitRoundaboutIntersection}; + const constexpr TurnType::Enum exit_instruction[] = { + TurnType::Invalid, TurnType::ExitRoundabout, TurnType::ExitRotary, + TurnType::ExitRoundaboutIntersection}; return {exit_instruction[static_cast(roundabout_type)], modifier}; } diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp index 631bdb31a..bf1e9de6a 100644 --- a/include/util/typedefs.hpp +++ b/include/util/typedefs.hpp @@ -32,9 +32,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include -#include #include #include +#include // OpenStreetMap node ids are higher than 2^32 OSRM_STRONG_TYPEDEF(std::uint64_t, OSMNodeID) @@ -56,6 +56,7 @@ using OSMEdgeID_weak = std::uint64_t; using NodeID = unsigned int; using EdgeID = unsigned int; +using NameID = std::uint32_t; using EdgeWeight = int; using BearingClassID = std::uint32_t; @@ -80,8 +81,8 @@ struct SegmentID BOOST_ASSERT(!enabled || id != SPECIAL_SEGMENTID); } - NodeID id : 31; - std::uint32_t enabled : 1; + NodeID id : 31; + std::uint32_t enabled : 1; }; static_assert(sizeof(SegmentID) == 4, "SegmentID needs to be 4 bytes big"); diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp index 9122d6bd0..6514d3b9f 100644 --- a/src/engine/api/json_factory.cpp +++ b/src/engine/api/json_factory.cpp @@ -42,7 +42,8 @@ const constexpr char *turn_type_names[] = { "on ramp", "off ramp", "fork", "end of road", "notification", "roundabout", "roundabout", "rotary", "rotary", "roundabout turn", "roundabout turn", "invalid", "invalid", "invalid", "invalid", - "invalid", "invalid", "invalid", "invalid", "invalid"}; + "invalid", "invalid", "invalid", "invalid", "invalid", + "invalid"}; const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"}; @@ -150,20 +151,19 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver) return step_maneuver; } -util::json::Object -makeIntersection(const guidance::Intersection &intersection) +util::json::Object makeIntersection(const guidance::Intersection &intersection) { util::json::Object result; util::json::Array bearings; util::json::Array entry; bearings.values.reserve(intersection.bearings.size()); - std::copy(intersection.bearings.begin(), intersection.bearings.end(), std::back_inserter(bearings.values)); + std::copy(intersection.bearings.begin(), intersection.bearings.end(), + std::back_inserter(bearings.values)); entry.values.reserve(intersection.entry.size()); std::transform(intersection.entry.begin(), intersection.entry.end(), - std::back_inserter(entry.values), [](const bool has_entry) -> util::json::Value - { + std::back_inserter(entry.values), [](const bool has_entry) -> util::json::Value { if (has_entry) return util::json::True(); else diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp index cc7f83654..3cf978d4d 100644 --- a/src/engine/guidance/post_processing.cpp +++ b/src/engine/guidance/post_processing.cpp @@ -41,8 +41,8 @@ void print(const std::vector &steps) int segment = 0; for (const auto &step : steps) { - std::cout << "\t[" << ++segment << "]: " << step.maneuver.instruction.type << " " - << step.maneuver.instruction.direction_modifier << " " + std::cout << "\t[" << ++segment << "]: " << static_cast(step.maneuver.instruction.type) + << " " << static_cast(step.maneuver.instruction.direction_modifier) << " " << static_cast(step.maneuver.waypoint_type) << " Duration: " << step.duration << " Distance: " << step.distance << " Geometry: " << step.geometry_begin << " " << step.geometry_end << " exit: " << step.maneuver.exit @@ -63,6 +63,37 @@ void print(const std::vector &steps) } } +// Compute the angle between two bearings on a normal turn circle +// +// Bearings Angles +// +// 0 180 +// 315 45 225 135 +// +// 270 x 90 270 x 90 +// +// 225 135 315 45 +// 180 0 +// +// A turn from north to north-east offerst bearing 0 and 45 has to be translated +// into a turn of 135 degrees. The same holdes for 90 - 135 (east to south +// east). +// For north, the transformation works by angle = 540 (360 + 180) - exit_bearing +// % 360; +// All other cases are handled by first rotating both bearings to an +// entry_bearing of 0. +double turn_angle(const double entry_bearing, const double exit_bearing) +{ + const double offset = 360 - entry_bearing; + const double rotated_exit = [](double bearing, const double offset) { + bearing += offset; + return bearing > 360 ? bearing - 360 : bearing; + }(exit_bearing, offset); + + const auto angle = 540 - rotated_exit; + return angle > 360 ? angle - 360 : angle; +} + RouteStep forwardInto(RouteStep destination, const RouteStep &source) { // Merge a turn into a silent turn @@ -232,39 +263,11 @@ void closeOffRoundabout(const bool on_roundabout, propagation_step.maneuver.instruction.type == TurnType::EnterRoundaboutIntersectionAtExit) { - // Compute the angle between two bearings on a normal turn circle - // - // Bearings Angles - // - // 0 180 - // 315 45 225 135 - // - // 270 x 90 270 x 90 - // - // 225 135 315 45 - // 180 0 - // - // A turn from north to north-east offerst bearing 0 and 45 has to be translated - // into a turn of 135 degrees. The same holdes for 90 - 135 (east to south - // east). - // For north, the transformation works by angle = 540 (360 + 180) - exit_bearing - // % 360; - // All other cases are handled by first rotating both bearings to an - // entry_bearing of 0. BOOST_ASSERT(!propagation_step.intersections.empty()); const double angle = - [](const double entry_bearing, const double exit_bearing) { - const double offset = 360 - entry_bearing; - const double rotated_exit = [](double bearing, const double offset) { - bearing += offset; - return bearing > 360 ? bearing - 360 : bearing; - }(exit_bearing, offset); - - const auto angle = 540 - rotated_exit; - return angle > 360 ? angle - 360 : angle; - }(util::bearing::reverseBearing( - entry_intersection.bearings[entry_intersection.in]), - exit_bearing); + turn_angle(util::bearing::reverseBearing( + entry_intersection.bearings[entry_intersection.in]), + exit_bearing); propagation_step.maneuver.instruction.direction_modifier = ::osrm::util::guidance::getTurnDirection(angle); @@ -417,6 +420,7 @@ void collapseTurnAt(std::vector &steps, } steps[one_back_index].name = steps[two_back_index].name; + steps[one_back_index].name_id = steps[two_back_index].name_id; steps[one_back_index].maneuver.instruction.type = TurnType::Continue; steps[one_back_index].maneuver.instruction.direction_modifier = DirectionModifier::UTurn; @@ -580,6 +584,15 @@ std::vector collapseTurns(std::vector steps) instruction.direction_modifier == DirectionModifier::Straight); }; + // Special case handling: if the phantomnode landed on a sliproad, we + // change this into a 'turn' instruction. Sliproads are small ramps + // between roads, not ramps. + if (steps.size() >= 3 && + steps[steps.size() - 2].maneuver.instruction.type == TurnType::Sliproad) + { + steps[steps.size() - 2].maneuver.instruction.type = TurnType::Turn; + } + // first and last instructions are waypoints that cannot be collapsed for (std::size_t step_index = 2; step_index < steps.size(); ++step_index) { @@ -595,10 +608,40 @@ std::vector collapseTurns(std::vector steps) const auto two_back_index = getPreviousIndex(one_back_index); BOOST_ASSERT(two_back_index < steps.size()); + // Handle sliproads from motorways in urban areas + if (one_back_step.maneuver.instruction.type == TurnType::Sliproad) + { + + // Handle possible u-turns between highways that look like slip-roads + if (steps[two_back_index].name_id == steps[step_index].name_id && + steps[step_index].name_id != INVALID_NAMEID) + { + steps[one_back_index].maneuver.instruction.type = TurnType::Continue; + } + else + { + steps[one_back_index].maneuver.instruction.type = TurnType::Turn; + } + steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]); + steps[one_back_index].name_id = steps[step_index].name_id; + steps[one_back_index].name = steps[step_index].name; + + const auto exit_intersection = steps[step_index].intersections.front(); + const auto exit_bearing = exit_intersection.bearings[exit_intersection.out]; + + const auto entry_intersection = steps[one_back_index].intersections.front(); + const auto entry_bearing = entry_intersection.bearings[entry_intersection.in]; + + const double angle = + turn_angle(util::bearing::reverseBearing(entry_bearing), exit_bearing); + steps[one_back_index].maneuver.instruction.direction_modifier = + ::osrm::util::guidance::getTurnDirection(angle); + invalidateStep(steps[step_index]); + } // Due to empty segments, we can get name-changes from A->A // These have to be handled in post-processing - if (isCollapsableInstruction(current_step.maneuver.instruction) && - current_step.name == steps[one_back_index].name) + else if (isCollapsableInstruction(current_step.maneuver.instruction) && + current_step.name == steps[one_back_index].name) { steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]); invalidateStep(steps[step_index]); diff --git a/src/extractor/guidance/motorway_handler.cpp b/src/extractor/guidance/motorway_handler.cpp index dd4f1e9d7..c6b88a37a 100644 --- a/src/extractor/guidance/motorway_handler.cpp +++ b/src/extractor/guidance/motorway_handler.cpp @@ -45,8 +45,10 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph, const std::vector &node_info_list, const util::NameTable &name_table, - const SuffixTable &street_name_suffix_table) - : IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table) + const SuffixTable &street_name_suffix_table, + const IntersectionGenerator &intersection_generator) + : IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table), + intersection_generator(intersection_generator) { } @@ -251,18 +253,16 @@ Intersection MotorwayHandler::fromMotorway(const EdgeID via_eid, Intersection in else if (road.turn.angle < continue_angle) { road.turn.instruction = { - detail::isRampClass(road.turn.eid, node_based_graph) - ? TurnType::OffRamp - : TurnType::Turn, + detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::OffRamp + : TurnType::Turn, (road.turn.angle < 145) ? DirectionModifier::Right : DirectionModifier::SlightRight}; } else if (road.turn.angle > continue_angle) { road.turn.instruction = { - detail::isRampClass(road.turn.eid, node_based_graph) - ? TurnType::OffRamp - : TurnType::Turn, + detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::OffRamp + : TurnType::Turn, (road.turn.angle > 215) ? DirectionModifier::Left : DirectionModifier::SlightLeft}; } diff --git a/src/extractor/guidance/turn_analysis.cpp b/src/extractor/guidance/turn_analysis.cpp index 38359cdb4..403c0e214 100644 --- a/src/extractor/guidance/turn_analysis.cpp +++ b/src/extractor/guidance/turn_analysis.cpp @@ -11,8 +11,8 @@ #include #include #include -#include #include +#include using osrm::util::guidance::getTurnDirection; @@ -42,9 +42,17 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph, barrier_nodes, node_info_list, compressed_edge_container), - roundabout_handler(node_based_graph, node_info_list, compressed_edge_container, name_table, street_name_suffix_table), - motorway_handler(node_based_graph, node_info_list, name_table,street_name_suffix_table), - turn_handler(node_based_graph, node_info_list, name_table,street_name_suffix_table) + roundabout_handler(node_based_graph, + node_info_list, + compressed_edge_container, + name_table, + street_name_suffix_table), + motorway_handler(node_based_graph, + node_info_list, + name_table, + street_name_suffix_table, + intersection_generator), + turn_handler(node_based_graph, node_info_list, name_table, street_name_suffix_table) { } @@ -73,6 +81,9 @@ std::vector TurnAnalysis::getTurns(const NodeID from_nid, const E } } + // Handle sliproads + intersection = handleSliproads(via_eid, std::move(intersection)); + std::vector turns; for (auto road : intersection) if (road.entry_allowed) @@ -105,6 +116,88 @@ TurnAnalysis::setTurnTypes(const NodeID from_nid, const EdgeID, Intersection int return intersection; } +// "Sliproads" occur when we've got a link between two roads (MOTORWAY_LINK, etc), but +// the two roads are *also* directly connected shortly afterwards. +// In these cases, we tag the turn-type as "sliproad", and then in post-processing +// we emit a "turn", instead of "take the ramp"+"merge" +Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id, + Intersection intersection) const +{ + + auto intersection_node_id = node_based_graph.GetTarget(source_edge_id); + + const auto linkTest = [this](const ConnectedRoad &road) { + return isLinkClass( + node_based_graph.GetEdgeData(road.turn.eid).road_classification.road_class) && + road.entry_allowed && + angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE; + }; + + bool hasRamp = + std::find_if(intersection.begin(), intersection.end(), linkTest) != intersection.end(); + if (!hasRamp) + return intersection; + + const auto source_edge_data = node_based_graph.GetEdgeData(source_edge_id); + + // Find the continuation of the intersection we're on + auto next_road = std::find_if( + intersection.begin(), intersection.end(), + [this, source_edge_data](const ConnectedRoad &road) { + const auto road_edge_data = node_based_graph.GetEdgeData(road.turn.eid); + // Test to see if the source edge and the one we're looking at are the same road + return road_edge_data.road_classification.road_class == + source_edge_data.road_classification.road_class && + road_edge_data.name_id != INVALID_NAME_ID && + road_edge_data.name_id == source_edge_data.name_id && road.entry_allowed && + angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE; + }); + + const bool hasNext = next_road != intersection.end(); + + if (!hasNext) + { + return intersection; + } + + // Threshold check, if the intersection is too far away, don't bother continuing + const auto &next_road_data = node_based_graph.GetEdgeData(next_road->turn.eid); + if (next_road_data.distance > MAX_SLIPROAD_THRESHOLD) + { + return intersection; + } + + const auto next_road_next_intersection = + intersection_generator(intersection_node_id, next_road->turn.eid); + + std::unordered_set target_road_names; + + for (const auto &road : next_road_next_intersection) + { + const auto &target_data = node_based_graph.GetEdgeData(road.turn.eid); + target_road_names.insert(target_data.name_id); + } + + for (auto &road : intersection) + { + if (linkTest(road)) + { + auto target_intersection = intersection_generator(intersection_node_id, road.turn.eid); + for (const auto &candidate_road : target_intersection) + { + const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.turn.eid); + if (target_road_names.count(candidate_data.name_id) > 0) + { + road.turn.instruction.type = TurnType::Sliproad; + break; + } + } + } + } + + return intersection; +} + } // namespace guidance } // namespace extractor } // namespace osrm