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
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/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 <boost/optional.hpp>
@ -68,10 +68,8 @@ std::vector<RouteStep> 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<short>({bearings.second}),
std::vector<bool>({true}), Intersection::NO_INDEX, 0};
Intersection intersection{source_node.location, std::vector<short>({bearings.second}),
std::vector<bool>({true}), Intersection::NO_INDEX, 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 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<RouteStep> 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<std::size_t>(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<RouteStep> 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<RouteStep> 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<short>({static_cast<short>(util::bearing::reverseBearing(bearings.first))}),
std::vector<bool>({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);

View File

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

View File

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

View File

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

View File

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

View File

@ -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<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 <limits>
#include <cstddef>
#include <cstdint>
#include <limits>
// 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");

View File

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

View File

@ -41,8 +41,8 @@ void print(const std::vector<RouteStep> &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<int>(step.maneuver.instruction.type)
<< " " << static_cast<int>(step.maneuver.instruction.direction_modifier) << " "
<< static_cast<int>(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<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)
{
// 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<RouteStep> &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<RouteStep> collapseTurns(std::vector<RouteStep> 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<RouteStep> collapseTurns(std::vector<RouteStep> 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]);

View File

@ -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<QueryNode> &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};
}

View File

@ -11,8 +11,8 @@
#include <limits>
#include <map>
#include <set>
#include <unordered_set>
#include <unordered_map>
#include <unordered_set>
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<TurnOperation> TurnAnalysis::getTurns(const NodeID from_nid, const E
}
}
// Handle sliproads
intersection = handleSliproads(via_eid, std::move(intersection));
std::vector<TurnOperation> 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<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 extractor
} // namespace osrm