Distinguish between offramps and sliproads.

This commit is contained in:
Daniel Patterson 2016-05-25 15:24:11 +02:00 committed by Patrick Niklaus
parent 089e60fa1e
commit 3d03797e53
No known key found for this signature in database
GPG Key ID: E426891B5F978B1B
13 changed files with 428 additions and 114 deletions

View File

@ -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 # 5.2.0 RC1
Changes from 5.1.0 Changes from 5.1.0

View File

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

View File

@ -12,8 +12,8 @@
#include "util/bearing.hpp" #include "util/bearing.hpp"
#include "util/coordinate.hpp" #include "util/coordinate.hpp"
#include "util/coordinate_calculation.hpp" #include "util/coordinate_calculation.hpp"
#include "util/guidance/toolkit.hpp"
#include "util/guidance/entry_class.hpp" #include "util/guidance/entry_class.hpp"
#include "util/guidance/toolkit.hpp"
#include "util/typedefs.hpp" #include "util/typedefs.hpp"
#include <boost/optional.hpp> #include <boost/optional.hpp>
@ -68,10 +68,8 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
StepManeuver maneuver{source_node.location, bearings.first, StepManeuver maneuver{source_node.location, bearings.first,
bearings.second, extractor::guidance::TurnInstruction::NO_TURN(), bearings.second, extractor::guidance::TurnInstruction::NO_TURN(),
WaypointType::Depart, 0}; WaypointType::Depart, 0};
Intersection intersection{ Intersection intersection{source_node.location, std::vector<short>({bearings.second}),
source_node.location, std::vector<bool>({true}), Intersection::NO_INDEX, 0};
std::vector<short>({bearings.second}),
std::vector<bool>({true}), Intersection::NO_INDEX, 0};
if (leg_data.size() > 0) if (leg_data.size() > 0)
{ {
@ -96,6 +94,17 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
const auto name = facade.GetNameForID(step_name_id); const auto name = facade.GetNameForID(step_name_id);
const auto distance = leg_geometry.segment_distances[segment_index]; 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()) if (leg_data_index + 1 < leg_data.size())
{ {
step_name_id = leg_data[leg_data_index + 1].name_id; step_name_id = leg_data[leg_data_index + 1].name_id;
@ -104,26 +113,26 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
{ {
step_name_id = target_node.name_id; 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); bearings = detail::getIntermediateBearings(leg_geometry, segment_index);
const auto entry_class = facade.GetEntryClass(path_point.entry_classid); const auto entry_class = facade.GetEntryClass(path_point.entry_classid);
const auto bearing_class = facade.GetBearingClass(facade.GetBearingClassID(path_point.turn_via_node)); const auto bearing_class =
intersection.in = bearing_class.findMatchingBearing(util::bearing::reverseBearing(bearings.first)); 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.out = bearing_class.findMatchingBearing(bearings.second);
intersection.location = facade.GetCoordinateOfNode(path_point.turn_via_node); intersection.location = facade.GetCoordinateOfNode(path_point.turn_via_node);
intersection.bearings.clear(); 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)); std::back_inserter(intersection.bearings));
intersection.entry.clear(); intersection.entry.clear();
for (auto idx : util::irange<std::size_t>(0, intersection.bearings.size())) for (auto idx : util::irange<std::size_t>(0, intersection.bearings.size()))
{ {
intersection.entry.push_back(entry_class.allowsEntry(idx)); 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_index++;
segment_duration = 0; segment_duration = 0;
} }
@ -131,10 +140,16 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
const auto distance = leg_geometry.segment_distances[segment_index]; const auto distance = leg_geometry.segment_distances[segment_index];
const int duration = segment_duration + target_duration; const int duration = segment_duration + target_duration;
BOOST_ASSERT(duration >= 0); BOOST_ASSERT(duration >= 0);
steps.push_back(RouteStep{ steps.push_back(RouteStep{step_name_id,
step_name_id, facade.GetNameForID(step_name_id), NO_ROTARY_NAME, duration / 10., facade.GetNameForID(step_name_id),
distance, target_mode, maneuver, leg_geometry.FrontIndex(segment_index), NO_ROTARY_NAME,
leg_geometry.BackIndex(segment_index) + 1, {intersection}}); 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 // In this case the source + target are on the same edge segment
else else
@ -148,30 +163,41 @@ std::vector<RouteStep> assembleSteps(const DataFacadeT &facade,
int duration = target_duration - source_duration; int duration = target_duration - source_duration;
BOOST_ASSERT(duration >= 0); BOOST_ASSERT(duration >= 0);
steps.push_back(RouteStep{ steps.push_back(RouteStep{source_node.name_id,
source_node.name_id, facade.GetNameForID(source_node.name_id), NO_ROTARY_NAME, facade.GetNameForID(source_node.name_id),
duration / 10., leg_geometry.segment_distances[segment_index], source_mode, NO_ROTARY_NAME,
std::move(maneuver), leg_geometry.FrontIndex(segment_index), duration / 10.,
leg_geometry.BackIndex(segment_index) + 1, {intersection}}); 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); BOOST_ASSERT(segment_index == number_of_segments - 1);
bearings = detail::getArriveBearings(leg_geometry); bearings = detail::getArriveBearings(leg_geometry);
// This step has length zero, the only reason we need it is the target location // 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 = { intersection = {
target_node.location, target_node.location,
std::vector<short>({static_cast<short>(util::bearing::reverseBearing(bearings.first))}), std::vector<short>({static_cast<short>(util::bearing::reverseBearing(bearings.first))}),
std::vector<bool>({true}), 0, Intersection::NO_INDEX}; std::vector<bool>({true}), 0, Intersection::NO_INDEX};
BOOST_ASSERT(!leg_geometry.locations.empty()); BOOST_ASSERT(!leg_geometry.locations.empty());
steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id), steps.push_back(RouteStep{target_node.name_id,
NO_ROTARY_NAME, ZERO_DURATION, ZERO_DISTANCE, target_mode, facade.GetNameForID(target_node.name_id),
std::move(maneuver), leg_geometry.locations.size() - 1, NO_ROTARY_NAME,
ZERO_DURATION,
ZERO_DISTANCE,
target_mode,
std::move(maneuver),
leg_geometry.locations.size() - 1,
leg_geometry.locations.size(), leg_geometry.locations.size(),
{intersection}}); {intersection}});
BOOST_ASSERT(steps.front().intersections.size() == 1); BOOST_ASSERT(steps.front().intersections.size() == 1);
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1); BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1); BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);

View File

@ -47,6 +47,17 @@ inline bool isRampClass(const FunctionalRoadClass road_class)
road_class == FunctionalRoadClass::TRUNK_LINK; 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 // TODO augment this with all data required for guidance generation
struct RoadClassificationData struct RoadClassificationData
{ {

View File

@ -22,10 +22,12 @@ const double constexpr FUZZY_ANGLE_DIFFERENCE = 20.;
const double constexpr DISTINCTION_RATIO = 2; const double constexpr DISTINCTION_RATIO = 2;
const unsigned constexpr INVALID_NAME_ID = 0; 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 MAX_ROUNDABOUT_RADIUS = 15; // 30 m diameter as final distinction
const double constexpr INCREASES_BY_FOURTY_PERCENT = 1.4; const double constexpr INCREASES_BY_FOURTY_PERCENT = 1.4;
const unsigned constexpr MAX_SLIPROAD_THRESHOLD = 250;
} // namespace guidance } // namespace guidance
} // namespace extractor } // namespace extractor
} // namespace osrm } // namespace osrm

View File

@ -2,6 +2,7 @@
#define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_ #define OSRM_EXTRACTOR_GUIDANCE_MOTORWAY_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp" #include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/intersection_handler.hpp" #include "extractor/guidance/intersection_handler.hpp"
#include "extractor/query_node.hpp" #include "extractor/query_node.hpp"
@ -25,7 +26,8 @@ class MotorwayHandler : public IntersectionHandler
MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph, MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const std::vector<QueryNode> &node_info_list, const std::vector<QueryNode> &node_info_list,
const util::NameTable &name_table, const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table); const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator);
~MotorwayHandler() override final; ~MotorwayHandler() override final;
// check whether the handler can actually handle the intersection // check whether the handler can actually handle the intersection
@ -39,10 +41,14 @@ class MotorwayHandler : public IntersectionHandler
Intersection intersection) const override final; Intersection intersection) const override final;
private: private:
Intersection handleSliproads(const NodeID intersection_node_id,
Intersection intersection) const;
Intersection fromMotorway(const EdgeID via_edge, Intersection intersection) const; Intersection fromMotorway(const EdgeID via_edge, Intersection intersection) const;
Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const; Intersection fromRamp(const EdgeID via_edge, Intersection intersection) const;
Intersection fallback(Intersection intersection) const; Intersection fallback(Intersection intersection) const;
const IntersectionGenerator &intersection_generator;
}; };
} // namespace guidance } // namespace guidance

View File

@ -59,6 +59,9 @@ class TurnAnalysis
// Utility function, setting basic turn types. Prepares for normal turn handling. // Utility function, setting basic turn types. Prepares for normal turn handling.
Intersection Intersection
setTurnTypes(const NodeID from, const EdgeID via_edge, Intersection intersection) const; setTurnTypes(const NodeID from, const EdgeID via_edge, Intersection intersection) const;
Intersection handleSliproads(const NodeID intersection_node_id,
Intersection intersection) const;
}; // class TurnAnalysis }; // class TurnAnalysis
} // namespace guidance } // namespace guidance

View File

@ -20,7 +20,6 @@ namespace detail
const constexpr uint8_t num_direction_modifiers = 8; const constexpr uint8_t num_direction_modifiers = 8;
} // detail } // detail
// direction modifiers based on angle // direction modifiers based on angle
namespace DirectionModifier namespace DirectionModifier
{ {
@ -38,31 +37,36 @@ const constexpr Enum SharpLeft = 7;
namespace TurnType namespace TurnType
{ {
typedef std::uint8_t Enum; typedef std::uint8_t Enum;
const constexpr Enum Invalid = 0; // no valid turn instruction const constexpr Enum Invalid = 0; // no valid turn instruction
const constexpr Enum NewName = 1; // no turn, but name changes const constexpr Enum NewName = 1; // no turn, but name changes
const constexpr Enum Continue = 2; // remain on a street const constexpr Enum Continue = 2; // remain on a street
const constexpr Enum Turn = 3; // basic turn const constexpr Enum Turn = 3; // basic turn
const constexpr Enum Merge = 4; // merge onto a street const constexpr Enum Merge = 4; // merge onto a street
const constexpr Enum OnRamp = 5; // special turn (highway ramp on-ramps) const constexpr Enum OnRamp = 5; // special turn (highway ramp on-ramps)
const constexpr Enum OffRamp = 6; // special turn, highway exit const constexpr Enum OffRamp = 6; // special turn, highway exit
const constexpr Enum Fork = 7; // fork road splitting up const constexpr Enum Fork = 7; // fork road splitting up
const constexpr Enum EndOfRoad = 8; // T intersection const constexpr Enum EndOfRoad = 8; // T intersection
const constexpr Enum Notification = 9; // Travel Mode Changes, Restrictions apply... const constexpr Enum Notification = 9; // Travel Mode Changes, Restrictions apply...
const constexpr Enum EnterRoundabout = 10; // Entering a small Roundabout const constexpr Enum EnterRoundabout = 10; // Entering a small Roundabout
const constexpr Enum EnterAndExitRoundabout = 11; // Touching a roundabout const constexpr Enum EnterAndExitRoundabout = 11; // Touching a roundabout
const constexpr Enum EnterRotary = 12; // Enter a rotary const constexpr Enum EnterRotary = 12; // Enter a rotary
const constexpr Enum EnterAndExitRotary = 13; // Touching a rotary const constexpr Enum EnterAndExitRotary = 13; // Touching a rotary
const constexpr Enum EnterRoundaboutIntersection = 14; // Entering a small Roundabout const constexpr Enum EnterRoundaboutIntersection = 14; // Entering a small Roundabout
const constexpr Enum EnterAndExitRoundaboutIntersection = 15; // Touching a 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 // Values below here are silent instructions
const constexpr Enum EnterRoundaboutAtExit = 18; // Entering a small Roundabout at a countable exit const constexpr Enum NoTurn = 16; // end of segment without turn/middle of a segment
const constexpr Enum ExitRoundabout = 19; // Exiting a small Roundabout const constexpr Enum Suppressed = 17; // location that suppresses a turn
const constexpr Enum EnterRotaryAtExit = 20; // Enter A Rotary at a countable exit const constexpr Enum EnterRoundaboutAtExit = 18; // Entering a small Roundabout at a countable exit
const constexpr Enum ExitRotary = 21; // Exit a rotary const constexpr Enum ExitRoundabout = 19; // Exiting a small Roundabout
const constexpr Enum EnterRoundaboutIntersectionAtExit = 22; // Entering a small Roundabout at a countable exit const constexpr Enum EnterRotaryAtExit = 20; // Enter A Rotary at a countable exit
const constexpr Enum ExitRoundaboutIntersection = 23; // Exiting a small Roundabout const constexpr Enum ExitRotary = 21; // Exit a rotary
const constexpr Enum StayOnRoundabout = 24; // Continue on Either a small or a large Roundabout 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 // turn angle in 1.40625 degree -> 128 == 180 degree
@ -87,7 +91,8 @@ struct TurnInstruction
return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn); 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); return TurnInstruction(TurnType::StayOnRoundabout, modifier);
} }
@ -104,9 +109,9 @@ struct TurnInstruction
static TurnInstruction EXIT_ROUNDABOUT(const RoundaboutType roundabout_type, static TurnInstruction EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
const DirectionModifier::Enum modifier) const DirectionModifier::Enum modifier)
{ {
const constexpr TurnType::Enum exit_instruction[] = {TurnType::Invalid, TurnType::ExitRoundabout, const constexpr TurnType::Enum exit_instruction[] = {
TurnType::ExitRotary, TurnType::Invalid, TurnType::ExitRoundabout, TurnType::ExitRotary,
TurnType::ExitRoundaboutIntersection}; TurnType::ExitRoundaboutIntersection};
return {exit_instruction[static_cast<int>(roundabout_type)], modifier}; return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
} }

View File

@ -32,9 +32,9 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <limits>
#include <cstddef> #include <cstddef>
#include <cstdint> #include <cstdint>
#include <limits>
// OpenStreetMap node ids are higher than 2^32 // OpenStreetMap node ids are higher than 2^32
OSRM_STRONG_TYPEDEF(std::uint64_t, OSMNodeID) OSRM_STRONG_TYPEDEF(std::uint64_t, OSMNodeID)
@ -56,6 +56,7 @@ using OSMEdgeID_weak = std::uint64_t;
using NodeID = unsigned int; using NodeID = unsigned int;
using EdgeID = unsigned int; using EdgeID = unsigned int;
using NameID = std::uint32_t;
using EdgeWeight = int; using EdgeWeight = int;
using BearingClassID = std::uint32_t; using BearingClassID = std::uint32_t;
@ -80,8 +81,8 @@ struct SegmentID
BOOST_ASSERT(!enabled || id != SPECIAL_SEGMENTID); BOOST_ASSERT(!enabled || id != SPECIAL_SEGMENTID);
} }
NodeID id : 31; NodeID id : 31;
std::uint32_t enabled : 1; std::uint32_t enabled : 1;
}; };
static_assert(sizeof(SegmentID) == 4, "SegmentID needs to be 4 bytes big"); static_assert(sizeof(SegmentID) == 4, "SegmentID needs to be 4 bytes big");

View File

@ -42,7 +42,8 @@ const constexpr char *turn_type_names[] = {
"on ramp", "off ramp", "fork", "end of road", "notification", "on ramp", "off ramp", "fork", "end of road", "notification",
"roundabout", "roundabout", "rotary", "rotary", "roundabout turn", "roundabout", "roundabout", "rotary", "rotary", "roundabout turn",
"roundabout turn", "invalid", "invalid", "invalid", "invalid", "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"}; const constexpr char *waypoint_type_names[] = {"invalid", "arrive", "depart"};
@ -150,20 +151,19 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver)
return step_maneuver; return step_maneuver;
} }
util::json::Object util::json::Object makeIntersection(const guidance::Intersection &intersection)
makeIntersection(const guidance::Intersection &intersection)
{ {
util::json::Object result; util::json::Object result;
util::json::Array bearings; util::json::Array bearings;
util::json::Array entry; util::json::Array entry;
bearings.values.reserve(intersection.bearings.size()); 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()); entry.values.reserve(intersection.entry.size());
std::transform(intersection.entry.begin(), intersection.entry.end(), 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) if (has_entry)
return util::json::True(); return util::json::True();
else else

View File

@ -41,8 +41,8 @@ void print(const std::vector<RouteStep> &steps)
int segment = 0; int segment = 0;
for (const auto &step : steps) for (const auto &step : steps)
{ {
std::cout << "\t[" << ++segment << "]: " << step.maneuver.instruction.type << " " std::cout << "\t[" << ++segment << "]: " << static_cast<int>(step.maneuver.instruction.type)
<< step.maneuver.instruction.direction_modifier << " " << " " << static_cast<int>(step.maneuver.instruction.direction_modifier) << " "
<< static_cast<int>(step.maneuver.waypoint_type) << " Duration: " << step.duration << static_cast<int>(step.maneuver.waypoint_type) << " Duration: " << step.duration
<< " Distance: " << step.distance << " Geometry: " << step.geometry_begin << " " << " Distance: " << step.distance << " Geometry: " << step.geometry_begin << " "
<< step.geometry_end << " exit: " << step.maneuver.exit << step.geometry_end << " exit: " << step.maneuver.exit
@ -63,6 +63,37 @@ void print(const std::vector<RouteStep> &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) RouteStep forwardInto(RouteStep destination, const RouteStep &source)
{ {
// Merge a turn into a silent turn // Merge a turn into a silent turn
@ -232,39 +263,11 @@ void closeOffRoundabout(const bool on_roundabout,
propagation_step.maneuver.instruction.type == propagation_step.maneuver.instruction.type ==
TurnType::EnterRoundaboutIntersectionAtExit) 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()); BOOST_ASSERT(!propagation_step.intersections.empty());
const double angle = const double angle =
[](const double entry_bearing, const double exit_bearing) { turn_angle(util::bearing::reverseBearing(
const double offset = 360 - entry_bearing; entry_intersection.bearings[entry_intersection.in]),
const double rotated_exit = [](double bearing, const double offset) { exit_bearing);
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);
propagation_step.maneuver.instruction.direction_modifier = propagation_step.maneuver.instruction.direction_modifier =
::osrm::util::guidance::getTurnDirection(angle); ::osrm::util::guidance::getTurnDirection(angle);
@ -417,6 +420,7 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
} }
steps[one_back_index].name = steps[two_back_index].name; 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.type = TurnType::Continue;
steps[one_back_index].maneuver.instruction.direction_modifier = steps[one_back_index].maneuver.instruction.direction_modifier =
DirectionModifier::UTurn; DirectionModifier::UTurn;
@ -580,6 +584,15 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
instruction.direction_modifier == DirectionModifier::Straight); 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 // first and last instructions are waypoints that cannot be collapsed
for (std::size_t step_index = 2; step_index < steps.size(); ++step_index) for (std::size_t step_index = 2; step_index < steps.size(); ++step_index)
{ {
@ -595,10 +608,40 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
const auto two_back_index = getPreviousIndex(one_back_index); const auto two_back_index = getPreviousIndex(one_back_index);
BOOST_ASSERT(two_back_index < steps.size()); 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 // Due to empty segments, we can get name-changes from A->A
// These have to be handled in post-processing // These have to be handled in post-processing
if (isCollapsableInstruction(current_step.maneuver.instruction) && else if (isCollapsableInstruction(current_step.maneuver.instruction) &&
current_step.name == steps[one_back_index].name) current_step.name == steps[one_back_index].name)
{ {
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]); steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
invalidateStep(steps[step_index]); invalidateStep(steps[step_index]);

View File

@ -45,8 +45,10 @@ inline bool isRampClass(EdgeID eid, const util::NodeBasedDynamicGraph &node_base
MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph, MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_graph,
const std::vector<QueryNode> &node_info_list, const std::vector<QueryNode> &node_info_list,
const util::NameTable &name_table, const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table) const SuffixTable &street_name_suffix_table,
: IntersectionHandler(node_based_graph, node_info_list, name_table, 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) else if (road.turn.angle < continue_angle)
{ {
road.turn.instruction = { road.turn.instruction = {
detail::isRampClass(road.turn.eid, node_based_graph) detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::OffRamp
? TurnType::OffRamp : TurnType::Turn,
: TurnType::Turn,
(road.turn.angle < 145) ? DirectionModifier::Right (road.turn.angle < 145) ? DirectionModifier::Right
: DirectionModifier::SlightRight}; : DirectionModifier::SlightRight};
} }
else if (road.turn.angle > continue_angle) else if (road.turn.angle > continue_angle)
{ {
road.turn.instruction = { road.turn.instruction = {
detail::isRampClass(road.turn.eid, node_based_graph) detail::isRampClass(road.turn.eid, node_based_graph) ? TurnType::OffRamp
? TurnType::OffRamp : TurnType::Turn,
: TurnType::Turn,
(road.turn.angle > 215) ? DirectionModifier::Left (road.turn.angle > 215) ? DirectionModifier::Left
: DirectionModifier::SlightLeft}; : DirectionModifier::SlightLeft};
} }

View File

@ -11,8 +11,8 @@
#include <limits> #include <limits>
#include <map> #include <map>
#include <set> #include <set>
#include <unordered_set>
#include <unordered_map> #include <unordered_map>
#include <unordered_set>
using osrm::util::guidance::getTurnDirection; using osrm::util::guidance::getTurnDirection;
@ -42,9 +42,17 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
barrier_nodes, barrier_nodes,
node_info_list, node_info_list,
compressed_edge_container), compressed_edge_container),
roundabout_handler(node_based_graph, node_info_list, compressed_edge_container, name_table, street_name_suffix_table), roundabout_handler(node_based_graph,
motorway_handler(node_based_graph, node_info_list, name_table,street_name_suffix_table), node_info_list,
turn_handler(node_based_graph, node_info_list, name_table,street_name_suffix_table) 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<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const E
} }
} }
// Handle sliproads
intersection = handleSliproads(via_eid, std::move(intersection));
std::vector<TurnOperation> turns; std::vector<TurnOperation> turns;
for (auto road : intersection) for (auto road : intersection)
if (road.entry_allowed) if (road.entry_allowed)
@ -105,6 +116,88 @@ TurnAnalysis::setTurnTypes(const NodeID from_nid, const EdgeID, Intersection int
return intersection; 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<NameID> 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 guidance
} // namespace extractor } // namespace extractor
} // namespace osrm } // namespace osrm