introduce roundabout-turns into instruction set

This commit is contained in:
Moritz Kobitzsch 2016-04-18 13:41:19 +02:00
parent 8d68d4c050
commit 1544a08ea2
19 changed files with 791 additions and 141 deletions

View File

@ -1,3 +1,11 @@
# 5.1.0
- API:
- added roundabout-turn instruction. The instruction indicates a small roundabout that is treated as an intersection
(turn right at the roundabout for first exit, go straight at the roundabout...)
- Infrastructure
- BREAKING: reordered internal instruction types. This breaks the data format
# 5.0.0 # 5.0.0
Changes with regard 5.0.0 RC2: Changes with regard 5.0.0 RC2:
- API: - API:

View File

@ -0,0 +1,369 @@
@routing @guidance
Feature: Basic Roundabout
Background:
Given the profile "car"
Given a grid size of 3 meters
Scenario: Enter and Exit
Given the node map
| | | a | | |
| | | b | | |
| h | g | | c | d |
| | | e | | |
| | | f | | |
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bgecb | roundabout |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,roundabout_turn left exit-3,arrive |
| a,f | ab,ef,ef | depart,roundabout_turn straight exit-2,arrive |
| a,h | ab,gh,gh | depart,roundabout_turn right exit-1,arrive |
| d,f | cd,ef,ef | depart,roundabout_turn left exit-3,arrive |
| d,h | cd,gh,gh | depart,roundabout_turn straight exit-2,arrive |
| d,a | cd,ab,ab | depart,roundabout_turn right exit-1,arrive |
| f,h | ef,gh,gh | depart,roundabout_turn left exit-3,arrive |
| f,a | ef,ab,ab | depart,roundabout_turn straight exit-2,arrive |
| f,d | ef,cd,cd | depart,roundabout_turn right exit-1,arrive |
| h,a | gh,ab,ab | depart,roundabout_turn left exit-3,arrive |
| h,d | gh,cd,cd | depart,roundabout_turn straight exit-2,arrive |
| h,f | gh,ef,ef | depart,roundabout_turn right exit-1,arrive |
Scenario: Enter and Exit - Rotated
Given the node map
| a | | | d |
| | b | c | |
| | g | e | |
| h | | | f |
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bgecb | roundabout |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,roundabout_turn left exit-3,arrive |
| a,f | ab,ef,ef | depart,roundabout_turn straight exit-2,arrive |
| a,h | ab,gh,gh | depart,roundabout_turn right exit-1,arrive |
| d,f | cd,ef,ef | depart,roundabout_turn left exit-3,arrive |
| d,h | cd,gh,gh | depart,roundabout_turn straight exit-2,arrive |
| d,a | cd,ab,ab | depart,roundabout_turn right exit-1,arrive |
| f,h | ef,gh,gh | depart,roundabout_turn left exit-3,arrive |
| f,a | ef,ab,ab | depart,roundabout_turn straight exit-2,arrive |
| f,d | ef,cd,cd | depart,roundabout_turn right exit-1,arrive |
| h,a | gh,ab,ab | depart,roundabout_turn left exit-3,arrive |
| h,d | gh,cd,cd | depart,roundabout_turn straight exit-2,arrive |
| h,f | gh,ef,ef | depart,roundabout_turn right exit-1,arrive |
Scenario: Only Enter
Given the node map
| | | a | | |
| | | b | | |
| d | c | | g | h |
| | | e | | |
| | | f | | |
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| a,c | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| a,e | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| a,g | ab,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| d,e | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| d,g | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| d,b | cd,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| f,g | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| f,b | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| f,c | ef,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| h,b | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| h,c | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
| h,e | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive |
Scenario: Only Exit
Given the node map
| | | a | | |
| | | b | | |
| d | c | | g | h |
| | | e | | |
| | | f | | |
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| b,d | bcegb,cd,cd | depart,roundabout-exit-1,arrive |
| b,f | bcegb,ef,ef | depart,roundabout-exit-2,arrive |
| b,h | bcegb,gh,gh | depart,roundabout-exit-3,arrive |
| c,f | bcegb,ef,ef | depart,roundabout-exit-1,arrive |
| c,h | bcegb,gh,gh | depart,roundabout-exit-2,arrive |
| c,a | bcegb,ab,ab | depart,roundabout-exit-3,arrive |
| e,h | bcegb,gh,gh | depart,roundabout-exit-1,arrive |
| e,a | bcegb,ab,ab | depart,roundabout-exit-2,arrive |
| e,d | bcegb,cd,cd | depart,roundabout-exit-3,arrive |
| g,a | bcegb,ab,ab | depart,roundabout-exit-1,arrive |
| g,d | bcegb,cd,cd | depart,roundabout-exit-2,arrive |
| g,f | bcegb,ef,ef | depart,roundabout-exit-3,arrive |
#phantom node snapping can result in a full round-trip here, therefore we cannot test b->a and the other direct exits
Scenario: Drive Around
Given the node map
| | | a | | |
| | | b | | |
| d | c | | g | h |
| | | e | | |
| | | f | | |
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bcegb | roundabout |
When I route I should get
| waypoints | route | turns |
| b,c | bcegb,bcegb | depart,arrive |
| b,e | bcegb,bcegb | depart,arrive |
| b,g | bcegb,bcegb | depart,arrive |
| c,e | bcegb,bcegb | depart,arrive |
| c,g | bcegb,bcegb | depart,arrive |
| c,b | bcegb,bcegb | depart,arrive |
| e,g | bcegb,bcegb | depart,arrive |
| e,b | bcegb,bcegb | depart,arrive |
| e,c | bcegb,bcegb | depart,arrive |
| g,b | bcegb,bcegb | depart,arrive |
| g,c | bcegb,bcegb | depart,arrive |
| g,e | bcegb,bcegb | depart,arrive |
Scenario: Mixed Entry and Exit - Not an Intersection
Given the node map
| | c | | a | |
| j | | b | | f |
| | k | | e | |
| l | | h | | d |
| | g | | i | |
And the ways
| nodes | junction | oneway |
| abc | | yes |
| def | | yes |
| ghi | | yes |
| jkl | | yes |
| bkheb | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| a,c | abc,abc,abc | depart,roundabout-exit-1,arrive |
| a,l | abc,jkl,jkl | depart,roundabout-exit-2,arrive |
| a,i | abc,ghi,ghi | depart,roundabout-exit-3,arrive |
| a,f | abc,def,def | depart,roundabout-exit-4,arrive |
| d,f | def,def,def | depart,roundabout-exit-1,arrive |
| d,c | def,abc,abc | depart,roundabout-exit-2,arrive |
| d,l | def,jkl,jkl | depart,roundabout-exit-3,arrive |
| d,i | def,ghi,ghi | depart,roundabout-exit-4,arrive |
| g,i | ghi,ghi,ghi | depart,roundabout-exit-1,arrive |
| g,f | ghi,def,def | depart,roundabout-exit-2,arrive |
| g,c | ghi,abc,abc | depart,roundabout-exit-3,arrive |
| g,l | ghi,jkl,jkl | depart,roundabout-exit-4,arrive |
| j,l | jkl,jkl,jkl | depart,roundabout-exit-1,arrive |
| j,i | jkl,ghi,ghi | depart,roundabout-exit-2,arrive |
| j,f | jkl,def,def | depart,roundabout-exit-3,arrive |
| j,c | jkl,abc,abc | depart,roundabout-exit-4,arrive |
Scenario: Segregated roads - Not an intersection
Given the node map
| | a | | c | |
| l | | b | | d |
| | k | | e | |
| j | | h | | f |
| | i | | g | |
And the ways
| nodes | junction | oneway |
| abc | | yes |
| def | | yes |
| ghi | | yes |
| jkl | | yes |
| bkheb | roundabout | yes |
When I route I should get
| waypoints | route | turns |
| a,c | abc,abc,abc | depart,roundabout-exit-4,arrive |
| a,l | abc,jkl,jkl | depart,roundabout-exit-1,arrive |
| a,i | abc,ghi,ghi | depart,roundabout-exit-2,arrive |
| a,f | abc,def,def | depart,roundabout-exit-3,arrive |
| d,f | def,def,def | depart,roundabout-exit-4,arrive |
| d,c | def,abc,abc | depart,roundabout-exit-1,arrive |
| d,l | def,jkl,jkl | depart,roundabout-exit-2,arrive |
| d,i | def,ghi,ghi | depart,roundabout-exit-3,arrive |
| g,i | ghi,ghi,ghi | depart,roundabout-exit-4,arrive |
| g,f | ghi,def,def | depart,roundabout-exit-1,arrive |
| g,c | ghi,abc,abc | depart,roundabout-exit-2,arrive |
| g,l | ghi,jkl,jkl | depart,roundabout-exit-3,arrive |
| j,l | jkl,jkl,jkl | depart,roundabout-exit-4,arrive |
| j,i | jkl,ghi,ghi | depart,roundabout-exit-1,arrive |
| j,f | jkl,def,def | depart,roundabout-exit-2,arrive |
| j,c | jkl,abc,abc | depart,roundabout-exit-3,arrive |
Scenario: Collinear in X
Given the node map
| a | b | c | d | f |
| | | e | | |
And the ways
| nodes | junction |
| ab | |
| bcdb | roundabout |
| ce | |
| df | |
When I route I should get
| waypoints | route | turns |
| a,e | ab,ce,ce | depart,roundabout-exit-1,arrive |
| a,f | ab,df,df | depart,roundabout-exit-2,arrive |
Scenario: Collinear in X,Y
Given the node map
| a | | |
| b | | |
| c | d | f |
| e | | |
And the ways
| nodes | junction |
| ab | |
| bcdb | roundabout |
| ce | |
| df | |
When I route I should get
| waypoints | route | turns |
| a,e | ab,ce,ce | depart,roundabout_turn straight exit-1,arrive |
| a,f | ab,df,df | depart,roundabout_turn left exit-2,arrive |
Scenario: Collinear in X,Y
Given the node map
| a | | |
| d | | |
| b | c | f |
| e | | |
And the ways
| nodes | junction |
| ad | |
| bcdb | roundabout |
| be | |
| cf | |
When I route I should get
| waypoints | route | turns |
| a,e | ad,be,be | depart,roundabout_turn straight exit-1,arrive |
| a,f | ad,cf,cf | depart,roundabout_turn left exit-2,arrive |
Scenario: Collinear in X,Y
Given the node map
| a | | |
| c | | |
| d | b | f |
| e | | |
And the ways
| nodes | junction |
| ac | |
| bcdb | roundabout |
| de | |
| bf | |
When I route I should get
| waypoints | route | turns |
| a,e | ac,de,de | depart,roundabout_turn straight exit-1,arrive |
| a,f | ac,bf,bf | depart,roundabout_turn left exit-2,arrive |
Scenario: Enter and Exit -- too complex
Given the node map
| j | | a | | |
| | i | b | | |
| | g | | c | d |
| h | | e | | |
| | | f | | |
And the ways
| nodes | junction |
| ab | |
| ij | |
| cd | |
| ef | |
| gh | |
| bigecb | roundabout |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,roundabout-exit-4,arrive |
| a,f | ab,ef,ef | depart,roundabout-exit-3,arrive |
| a,h | ab,gh,gh | depart,roundabout-exit-2,arrive |
| d,f | cd,ef,ef | depart,roundabout-exit-4,arrive |
| d,h | cd,gh,gh | depart,roundabout-exit-3,arrive |
| d,a | cd,ab,ab | depart,roundabout-exit-1,arrive |
| f,h | ef,gh,gh | depart,roundabout-exit-4,arrive |
| f,a | ef,ab,ab | depart,roundabout-exit-2,arrive |
| f,d | ef,cd,cd | depart,roundabout-exit-1,arrive |
| h,a | gh,ab,ab | depart,roundabout-exit-3,arrive |
| h,d | gh,cd,cd | depart,roundabout-exit-2,arrive |
| h,f | gh,ef,ef | depart,roundabout-exit-1,arrive |
Scenario: Enter and Exit -- Non-Distinct
Given the node map
| | | a | | |
| | | b | | |
| | g | | c | d |
| | | e | | |
| h | | f | | |
And the ways
| nodes | junction |
| ab | |
| cd | |
| ef | |
| gh | |
| bgecb | roundabout |
When I route I should get
| waypoints | route | turns |
| a,d | ab,cd,cd | depart,roundabout-exit-3,arrive |
| a,f | ab,ef,ef | depart,roundabout-exit-2,arrive |
| a,h | ab,gh,gh | depart,roundabout-exit-1,arrive |
| d,f | cd,ef,ef | depart,roundabout-exit-3,arrive |
| d,h | cd,gh,gh | depart,roundabout-exit-2,arrive |
| d,a | cd,ab,ab | depart,roundabout-exit-1,arrive |
| f,h | ef,gh,gh | depart,roundabout-exit-3,arrive |
| f,a | ef,ab,ab | depart,roundabout-exit-2,arrive |
| f,d | ef,cd,cd | depart,roundabout-exit-1,arrive |
| h,a | gh,ab,ab | depart,roundabout-exit-3,arrive |
| h,d | gh,cd,cd | depart,roundabout-exit-2,arrive |
| h,f | gh,ef,ef | depart,roundabout-exit-1,arrive |

View File

@ -149,6 +149,8 @@ module.exports = function () {
return v.rotary_name + '-exit-' + v.maneuver.exit; return v.rotary_name + '-exit-' + v.maneuver.exit;
else else
return 'rotary-exit-' + v.maneuver.exit; return 'rotary-exit-' + v.maneuver.exit;
case 'roundabout_turn':
return v.maneuver.type + ' ' + v.maneuver.modifier + ' exit-' + v.maneuver.exit;
// FIXME this is a little bit over-simplistic for merge/fork instructions // FIXME this is a little bit over-simplistic for merge/fork instructions
default: default:
return v.maneuver.type + ' ' + v.maneuver.modifier; return v.maneuver.type + ' ' + v.maneuver.modifier;

View File

@ -1,5 +1,5 @@
#ifndef OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ #ifndef OSRM_ENGINE_GUIDANCE_TOOLKIT_HPP_
#define OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ #define OSRM_ENGINE_GUIDANCE_TOOLKIT_HPP_
#include "extractor/guidance/turn_instruction.hpp" #include "extractor/guidance/turn_instruction.hpp"
#include "util/bearing.hpp" #include "util/bearing.hpp"
@ -25,9 +25,12 @@ inline bool entersRoundabout(const extractor::guidance::TurnInstruction instruct
{ {
return (instruction.type == extractor::guidance::TurnType::EnterRoundabout || return (instruction.type == extractor::guidance::TurnType::EnterRoundabout ||
instruction.type == extractor::guidance::TurnType::EnterRotary || instruction.type == extractor::guidance::TurnType::EnterRotary ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersection ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutAtExit || instruction.type == extractor::guidance::TurnType::EnterRoundaboutAtExit ||
instruction.type == extractor::guidance::TurnType::EnterRotaryAtExit || instruction.type == extractor::guidance::TurnType::EnterRotaryAtExit ||
instruction.type == extractor::guidance::TurnType::EnterRoundaboutIntersectionAtExit ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout || instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary); instruction.type == extractor::guidance::TurnType::EnterAndExitRotary);
} }
@ -35,8 +38,10 @@ inline bool leavesRoundabout(const extractor::guidance::TurnInstruction instruct
{ {
return (instruction.type == extractor::guidance::TurnType::ExitRoundabout || return (instruction.type == extractor::guidance::TurnType::ExitRoundabout ||
instruction.type == extractor::guidance::TurnType::ExitRotary || instruction.type == extractor::guidance::TurnType::ExitRotary ||
instruction.type == extractor::guidance::TurnType::ExitRoundaboutIntersection ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout || instruction.type == extractor::guidance::TurnType::EnterAndExitRoundabout ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRotary); instruction.type == extractor::guidance::TurnType::EnterAndExitRotary ||
instruction.type == extractor::guidance::TurnType::EnterAndExitRoundaboutIntersection);
} }
inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruction) inline bool staysOnRoundabout(const extractor::guidance::TurnInstruction instruction)
@ -68,4 +73,4 @@ inline double angularDeviation(const double angle, const double from)
} // namespace engine } // namespace engine
} // namespace osrm } // namespace osrm
#endif /* OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ */ #endif /* OSRM_ENGINE_GUIDANCE_TOOLKIT_HPP_ */

View File

@ -22,6 +22,7 @@ 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_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;

View File

@ -1,14 +1,17 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_ #ifndef OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_ #define OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_HANDLER_HPP_
#include "extractor/compressed_edge_container.hpp"
#include "extractor/guidance/intersection.hpp" #include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_handler.hpp" #include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/roundabout_type.hpp"
#include "extractor/query_node.hpp" #include "extractor/query_node.hpp"
#include "util/name_table.hpp" #include "util/name_table.hpp"
#include "util/node_based_graph.hpp" #include "util/node_based_graph.hpp"
#include "util/typedefs.hpp" #include "util/typedefs.hpp"
#include <set>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -37,7 +40,8 @@ class RoundaboutHandler : public IntersectionHandler
public: public:
RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph, RoundaboutHandler(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 CompressedEdgeContainer &compressed_edge_container);
~RoundaboutHandler() override final; ~RoundaboutHandler() override final;
@ -61,17 +65,21 @@ class RoundaboutHandler : public IntersectionHandler
Intersection &intersection) const; Intersection &intersection) const;
// decide whether we lookk at a roundabout or a rotary // decide whether we lookk at a roundabout or a rotary
bool isRotary(const NodeID nid) const; RoundaboutType getRoundaboutType(const NodeID nid) const;
// TODO handle bike/walk cases that allow crossing a roundabout! // TODO handle bike/walk cases that allow crossing a roundabout!
// Processing of roundabouts // Processing of roundabouts
// Produces instructions to enter/exit a roundabout or to stay on it. // Produces instructions to enter/exit a roundabout or to stay on it.
// Performs the distinction between roundabout and rotaries. // Performs the distinction between roundabout and rotaries.
Intersection handleRoundabouts(const bool is_rotary, Intersection handleRoundabouts(const RoundaboutType roundabout_type,
const EdgeID via_edge, const EdgeID via_edge,
const bool on_roundabout, const bool on_roundabout,
const bool can_exit_roundabout, const bool can_exit_roundabout,
Intersection intersection) const; Intersection intersection) const;
bool qualifiesAsRoundaboutIntersection(const std::set<NodeID> &roundabout_nodes) const;
const CompressedEdgeContainer &compressed_edge_container;
}; };
} // namespace guidance } // namespace guidance

View File

@ -0,0 +1,21 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_TYPES_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_TYPES_HPP_
namespace osrm
{
namespace extractor
{
namespace guidance
{
enum class RoundaboutType
{
None, // not a roundabout
Roundabout, // standard roundabout
Rotary, // traffic circle (large roundabout) with dedicated name
RoundaboutIntersection // small roundabout with distinct turns, handled as intersection
};
} /* namespace guidance */
} /* namespace extractor */
} /* namespace osrm */
#endif /* OSRM_EXTRACTOR_GUIDANCE_ROUNDABOUT_TYPES_HPP_ */

View File

@ -4,6 +4,7 @@
#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 "extractor/compressed_edge_container.hpp" #include "extractor/compressed_edge_container.hpp"
#include "extractor/query_node.hpp" #include "extractor/query_node.hpp"
@ -26,6 +27,8 @@ namespace extractor
namespace guidance namespace guidance
{ {
using util::guidance::angularDeviation;
namespace detail namespace detail
{ {
const constexpr double DESIRED_SEGMENT_LENGTH = 10.0; const constexpr double DESIRED_SEGMENT_LENGTH = 10.0;
@ -246,12 +249,6 @@ inline double angleFromDiscreteAngle(const DiscreteAngle angle)
return static_cast<double>(angle) * detail::discrete_angle_step_size; return static_cast<double>(angle) * detail::discrete_angle_step_size;
} }
inline double angularDeviation(const double angle, const double from)
{
const double deviation = std::abs(angle - from);
return std::min(360 - deviation, deviation);
}
inline double getAngularPenalty(const double angle, DirectionModifier modifier) inline double getAngularPenalty(const double angle, DirectionModifier modifier)
{ {
// these are not aligned with getTurnDirection but represent an ideal center // these are not aligned with getTurnDirection but represent an ideal center
@ -272,30 +269,6 @@ inline double getTurnConfidence(const double angle, TurnInstruction instruction)
return 1.0 - (difference / max_deviation) * (difference / max_deviation); return 1.0 - (difference / max_deviation) * (difference / max_deviation);
} }
// Translates between angles and their human-friendly directional representation
inline DirectionModifier getTurnDirection(const double angle)
{
// An angle of zero is a u-turn
// 180 goes perfectly straight
// 0-180 are right turns
// 180-360 are left turns
if (angle > 0 && angle < 60)
return DirectionModifier::SharpRight;
if (angle >= 60 && angle < 140)
return DirectionModifier::Right;
if (angle >= 140 && angle < 170)
return DirectionModifier::SlightRight;
if (angle >= 165 && angle <= 195)
return DirectionModifier::Straight;
if (angle > 190 && angle <= 220)
return DirectionModifier::SlightLeft;
if (angle > 220 && angle <= 300)
return DirectionModifier::Left;
if (angle > 300 && angle < 360)
return DirectionModifier::SharpLeft;
return DirectionModifier::UTurn;
}
// swaps left <-> right modifier types // swaps left <-> right modifier types
inline DirectionModifier mirrorDirectionModifier(const DirectionModifier modifier) inline DirectionModifier mirrorDirectionModifier(const DirectionModifier modifier)
{ {

View File

@ -5,6 +5,8 @@
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include "extractor/guidance/roundabout_type.hpp"
namespace osrm namespace osrm
{ {
namespace extractor namespace extractor
@ -37,8 +39,6 @@ enum DirectionModifier
enum TurnType // at the moment we can support 32 turn types, without increasing memory consumption enum TurnType // at the moment we can support 32 turn types, without increasing memory consumption
{ {
Invalid, // no valid turn instruction Invalid, // no valid turn instruction
NoTurn, // end of segment without turn/middle of a segment
Suppressed, // location that suppresses a turn
NewName, // no turn, but name changes NewName, // no turn, but name changes
Continue, // remain on a street Continue, // remain on a street
Turn, // basic turn Turn, // basic turn
@ -46,16 +46,22 @@ enum TurnType // at the moment we can support 32 turn types, without increasing
Ramp, // special turn (highway ramp exits) Ramp, // special turn (highway ramp exits)
Fork, // fork road splitting up Fork, // fork road splitting up
EndOfRoad, // T intersection EndOfRoad, // T intersection
Notification, // Travel Mode Changes, Restrictions apply...
EnterRoundabout, // Entering a small Roundabout EnterRoundabout, // Entering a small Roundabout
EnterRoundaboutAtExit, // Entering a small Roundabout at a countable exit
EnterAndExitRoundabout, // Touching a roundabout EnterAndExitRoundabout, // Touching a roundabout
ExitRoundabout, // Exiting a small Roundabout
EnterRotary, // Enter a rotary EnterRotary, // Enter a rotary
EnterRotaryAtExit, // Enter A Rotary at a countable exit
EnterAndExitRotary, // Touching a rotary EnterAndExitRotary, // Touching a rotary
EnterRoundaboutIntersection, // Entering a small Roundabout
EnterAndExitRoundaboutIntersection, // Touching a roundabout
NoTurn, // end of segment without turn/middle of a segment
Suppressed, // location that suppresses a turn
EnterRoundaboutAtExit, // Entering a small Roundabout at a countable exit
ExitRoundabout, // Exiting a small Roundabout
EnterRotaryAtExit, // Enter A Rotary at a countable exit
ExitRotary, // Exit a rotary ExitRotary, // Exit a rotary
StayOnRoundabout, // Continue on Either a small or a large Roundabout EnterRoundaboutIntersectionAtExit, // Entering a small Roundabout at a countable exit
Notification // Travel Mode Changes` ExitRoundaboutIntersection, // Exiting a small Roundabout
StayOnRoundabout // Continue on Either a small or a large Roundabout
}; };
// turn angle in 1.40625 degree -> 128 == 180 degree // turn angle in 1.40625 degree -> 128 == 180 degree
@ -80,27 +86,45 @@ struct TurnInstruction
return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn); return TurnInstruction(TurnType::NoTurn, DirectionModifier::UTurn);
} }
static TurnInstruction REMAIN_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier) static TurnInstruction REMAIN_ROUNDABOUT(const RoundaboutType, const DirectionModifier modifier)
{ {
(void)is_rotary; // staying does not require a different instruction at the moment
return TurnInstruction(TurnType::StayOnRoundabout, modifier); return TurnInstruction(TurnType::StayOnRoundabout, modifier);
} }
static TurnInstruction ENTER_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier) static TurnInstruction ENTER_ROUNDABOUT(const RoundaboutType roundabout_type,
{
return {is_rotary ? TurnType::EnterRotary : TurnType::EnterRoundabout, modifier};
}
static TurnInstruction EXIT_ROUNDABOUT(bool is_rotary, const DirectionModifier modifier)
{
return {is_rotary ? TurnType::ExitRotary : TurnType::ExitRoundabout, modifier};
}
static TurnInstruction ENTER_AND_EXIT_ROUNDABOUT(bool is_rotary,
const DirectionModifier modifier) const DirectionModifier modifier)
{ {
return {is_rotary ? TurnType::EnterAndExitRotary : TurnType::EnterAndExitRoundabout, const constexpr TurnType enter_instruction[] = {
modifier}; TurnType::Invalid, TurnType::EnterRoundabout, TurnType::EnterRotary,
TurnType::EnterRoundaboutIntersection};
return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
}
static TurnInstruction EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
const DirectionModifier modifier)
{
const constexpr TurnType exit_instruction[] = {TurnType::Invalid, TurnType::ExitRoundabout,
TurnType::ExitRotary,
TurnType::ExitRoundaboutIntersection};
return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
}
static TurnInstruction ENTER_AND_EXIT_ROUNDABOUT(const RoundaboutType roundabout_type,
const DirectionModifier modifier)
{
const constexpr TurnType exit_instruction[] = {
TurnType::Invalid, TurnType::EnterAndExitRoundabout, TurnType::EnterAndExitRotary,
TurnType::EnterAndExitRoundaboutIntersection};
return {exit_instruction[static_cast<int>(roundabout_type)], modifier};
}
static TurnInstruction ENTER_ROUNDABOUT_AT_EXIT(const RoundaboutType roundabout_type,
const DirectionModifier modifier)
{
const constexpr TurnType enter_instruction[] = {
TurnType::Invalid, TurnType::EnterRoundaboutAtExit, TurnType::EnterRotaryAtExit,
TurnType::EnterRoundaboutIntersectionAtExit};
return {enter_instruction[static_cast<int>(roundabout_type)], modifier};
} }
static TurnInstruction SUPPRESSED(const DirectionModifier modifier) static TurnInstruction SUPPRESSED(const DirectionModifier modifier)

View File

@ -0,0 +1,48 @@
#ifndef OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_
#define OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_
/* A set of tools required for guidance in both pre and post-processing */
#include "extractor/guidance/turn_instruction.hpp"
namespace osrm
{
namespace util
{
namespace guidance
{
inline double angularDeviation(const double angle, const double from)
{
const double deviation = std::abs(angle - from);
return std::min(360 - deviation, deviation);
}
inline extractor::guidance::DirectionModifier getTurnDirection(const double angle)
{
// An angle of zero is a u-turn
// 180 goes perfectly straight
// 0-180 are right turns
// 180-360 are left turns
if (angle > 0 && angle < 60)
return extractor::guidance::DirectionModifier::SharpRight;
if (angle >= 60 && angle < 140)
return extractor::guidance::DirectionModifier::Right;
if (angle >= 140 && angle < 170)
return extractor::guidance::DirectionModifier::SlightRight;
if (angle >= 165 && angle <= 195)
return extractor::guidance::DirectionModifier::Straight;
if (angle > 190 && angle <= 220)
return extractor::guidance::DirectionModifier::SlightLeft;
if (angle > 220 && angle <= 300)
return extractor::guidance::DirectionModifier::Left;
if (angle > 300 && angle < 360)
return extractor::guidance::DirectionModifier::SharpLeft;
return extractor::guidance::DirectionModifier::UTurn;
}
} /* namespace guidance */
} /* namespace util */
} /* namespace osrm */
#endif /* OSRM_UTIL_GUIDANCE_TOOLKIT_HPP_ */

View File

@ -34,9 +34,11 @@ const constexpr char *modifier_names[] = {"uturn", "sharp right", "right", "s
// translations of TurnTypes. Not all types are exposed to the outside world. // translations of TurnTypes. Not all types are exposed to the outside world.
// invalid types should never be returned as part of the API // invalid types should never be returned as part of the API
const constexpr char *turn_type_names[] = { const constexpr char *turn_type_names[] = {
"invalid", "no turn", "invalid", "new name", "continue", "turn", "merge", "invalid", "new name", "continue", "turn", "merge", "ramp",
"ramp", "fork", "end of road", "roundabout", "invalid", "roundabout", "invalid", "fork", "end of road", "notification", "roundabout", "roundabout", "rotary",
"rotary", "invalid", "rotary", "invalid", "invalid", "notification"}; "rotary", "roundabout_turn", "roundabout_turn", "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"};
// Check whether to include a modifier in the result of the API // Check whether to include a modifier in the result of the API

View File

@ -4,6 +4,8 @@
#include "engine/guidance/assemble_steps.hpp" #include "engine/guidance/assemble_steps.hpp"
#include "engine/guidance/toolkit.hpp" #include "engine/guidance/toolkit.hpp"
#include "util/guidance/toolkit.hpp"
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <boost/range/algorithm_ext/erase.hpp> #include <boost/range/algorithm_ext/erase.hpp>
@ -17,6 +19,8 @@
using TurnInstruction = osrm::extractor::guidance::TurnInstruction; using TurnInstruction = osrm::extractor::guidance::TurnInstruction;
using TurnType = osrm::extractor::guidance::TurnType; using TurnType = osrm::extractor::guidance::TurnType;
using DirectionModifier = osrm::extractor::guidance::DirectionModifier; using DirectionModifier = osrm::extractor::guidance::DirectionModifier;
using osrm::util::guidance::angularDeviation;
using osrm::util::guidance::getTurnDirection;
namespace osrm namespace osrm
{ {
@ -43,7 +47,9 @@ void print(const std::vector<RouteStep> &steps)
for (const auto &intersection : step.maneuver.intersections) for (const auto &intersection : step.maneuver.intersections)
std::cout << "(" << intersection.duration << " " << intersection.distance << ")"; std::cout << "(" << intersection.duration << " " << intersection.distance << ")";
std::cout << "] name[" << step.name_id << "]: " << step.name << std::endl; std::cout << "] name[" << step.name_id << "]: " << step.name
<< " Bearings: " << step.maneuver.bearing_before << " "
<< step.maneuver.bearing_after << std::endl;
} }
} }
@ -79,17 +85,25 @@ void fixFinalRoundabout(std::vector<RouteStep> &steps)
--propagation_index) --propagation_index)
{ {
auto &propagation_step = steps[propagation_index]; auto &propagation_step = steps[propagation_index];
if (propagation_index == 0 || entersRoundabout(propagation_step.maneuver.instruction)) if (entersRoundabout(propagation_step.maneuver.instruction))
{ {
propagation_step.maneuver.exit = 0; propagation_step.maneuver.exit = 0;
propagation_step.geometry_end = steps.back().geometry_begin; propagation_step.geometry_end = steps.back().geometry_begin;
// remember the current name as rotary name in tha case we end in a rotary
if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary || if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary ||
propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit) propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit)
propagation_step.rotary_name = propagation_step.name; propagation_step.rotary_name = propagation_step.name;
break; else if (propagation_step.maneuver.instruction.type ==
TurnType::EnterRoundaboutIntersection ||
propagation_step.maneuver.instruction.type ==
TurnType::EnterRoundaboutIntersectionAtExit)
propagation_step.maneuver.instruction.type = TurnType::EnterRoundabout;
return;
} }
// accumulate turn data into the enter instructions
else if (propagation_step.maneuver.instruction.type == TurnType::StayOnRoundabout) else if (propagation_step.maneuver.instruction.type == TurnType::StayOnRoundabout)
{ {
// TODO this operates on the data that is in the instructions. // TODO this operates on the data that is in the instructions.
@ -109,14 +123,17 @@ bool setUpRoundabout(RouteStep &step)
// Special case handling, if an entry is directly tied to an exit // Special case handling, if an entry is directly tied to an exit
const auto instruction = step.maneuver.instruction; const auto instruction = step.maneuver.instruction;
if (instruction.type == TurnType::EnterRotaryAtExit || if (instruction.type == TurnType::EnterRotaryAtExit ||
instruction.type == TurnType::EnterRoundaboutAtExit) instruction.type == TurnType::EnterRoundaboutAtExit ||
instruction.type == TurnType::EnterRoundaboutIntersectionAtExit)
{ {
step.maneuver.exit = 1; step.maneuver.exit = 1;
// prevent futher special case handling of these two. // prevent futher special case handling of these two.
if (instruction.type == TurnType::EnterRotaryAtExit) if (instruction.type == TurnType::EnterRotaryAtExit)
step.maneuver.instruction.type = TurnType::EnterRotary; step.maneuver.instruction.type = TurnType::EnterRotary;
else else if (instruction.type == TurnType::EnterRoundaboutAtExit)
step.maneuver.instruction.type = TurnType::EnterRoundabout; step.maneuver.instruction.type = TurnType::EnterRoundabout;
else
step.maneuver.instruction.type = TurnType::EnterRoundaboutIntersection;
} }
if (leavesRoundabout(instruction)) if (leavesRoundabout(instruction))
@ -126,8 +143,10 @@ bool setUpRoundabout(RouteStep &step)
// prevent futher special case handling of these two. // prevent futher special case handling of these two.
if (instruction.type == TurnType::EnterAndExitRotary) if (instruction.type == TurnType::EnterAndExitRotary)
step.maneuver.instruction.type = TurnType::EnterRotary; step.maneuver.instruction.type = TurnType::EnterRotary;
else else if (instruction.type == TurnType::EnterAndExitRoundabout)
step.maneuver.instruction.type = TurnType::EnterRoundabout; step.maneuver.instruction.type = TurnType::EnterRoundabout;
else
step.maneuver.instruction.type = TurnType::EnterRoundaboutIntersection;
return false; return false;
} }
else else
@ -145,28 +164,35 @@ void closeOffRoundabout(const bool on_roundabout,
if (!on_roundabout) if (!on_roundabout)
{ {
// We reached a special case that requires the addition of a special route step in // We reached a special case that requires the addition of a special route step in the
// the beginning. // beginning. We started in a roundabout, so to announce the exit, we move use the exit
// We started in a roundabout, so to announce the exit, we move use the exit // instruction and move it right to the beginning to make sure to immediately announce the
// instruction and // exit.
// move it right to the beginning to make sure to immediately announce the exit.
BOOST_ASSERT(leavesRoundabout(steps[1].maneuver.instruction) || BOOST_ASSERT(leavesRoundabout(steps[1].maneuver.instruction) ||
steps[1].maneuver.instruction.type == TurnType::StayOnRoundabout); steps[1].maneuver.instruction.type == TurnType::StayOnRoundabout);
steps[0].geometry_end = 1; steps[0].geometry_end = 1;
steps[1] = detail::forwardInto(steps[1], steps[0]); steps[1] = detail::forwardInto(steps[1], steps[0]);
steps[0].duration = 0; steps[0].duration = 0;
steps[0].distance = 0; steps[0].distance = 0;
steps[1].maneuver.instruction.type = step.maneuver.instruction.type == TurnType::ExitRotary const auto exitToEnter = [](const TurnType type) {
? TurnType::EnterRotary if (TurnType::ExitRotary == type)
: TurnType::EnterRoundabout; return TurnType::EnterRotary;
// if we do not enter the roundabout Intersection, we cannot treat the full traversal as
// a turn. So we switch it up to the roundabout type
else if (type == TurnType::ExitRoundaboutIntersection)
return TurnType::EnterRoundabout;
else
return TurnType::EnterRoundabout;
};
steps[1].maneuver.instruction.type = exitToEnter(step.maneuver.instruction.type);
if (steps[1].maneuver.instruction.type == TurnType::EnterRotary) if (steps[1].maneuver.instruction.type == TurnType::EnterRotary)
steps[1].rotary_name = steps[0].name; steps[1].rotary_name = steps[0].name;
} }
// Normal exit from the roundabout, or exit from a previously fixed roundabout. // Normal exit from the roundabout, or exit from a previously fixed roundabout. Propagate the
// Propagate the index back to the entering // index back to the entering location and prepare the current silent set of instructions for
// location and // removal.
// prepare the current silent set of instructions for removal. const auto exit_bearing = steps[step_index].maneuver.bearing_after;
if (step_index > 1) if (step_index > 1)
{ {
// The very first route-step is head, so we cannot iterate past that one // The very first route-step is head, so we cannot iterate past that one
@ -177,15 +203,55 @@ void closeOffRoundabout(const bool on_roundabout,
propagation_step = detail::forwardInto(propagation_step, steps[propagation_index + 1]); propagation_step = detail::forwardInto(propagation_step, steps[propagation_index + 1]);
if (entersRoundabout(propagation_step.maneuver.instruction)) if (entersRoundabout(propagation_step.maneuver.instruction))
{ {
// TODO at this point, we can remember the additional name for a rotary
// This requires some initial thought on the data format, though
propagation_step.maneuver.exit = step.maneuver.exit; propagation_step.maneuver.exit = step.maneuver.exit;
propagation_step.geometry_end = step.geometry_end; propagation_step.geometry_end = step.geometry_end;
// remember rotary name // remember rotary name
if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary || if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary ||
propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit) propagation_step.maneuver.instruction.type == TurnType::EnterRotaryAtExit)
{
propagation_step.rotary_name = propagation_step.name; propagation_step.rotary_name = propagation_step.name;
}
else if (propagation_step.maneuver.instruction.type ==
TurnType::EnterRoundaboutIntersection ||
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.
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;
}(propagation_step.maneuver.bearing_before, exit_bearing);
std::cout << "Step: " << propagation_step.maneuver.bearing_before << " "
<< exit_bearing << " result: " << angle << std::endl;
propagation_step.maneuver.instruction.direction_modifier =
::osrm::util::guidance::getTurnDirection(angle);
}
propagation_step.name = step.name; propagation_step.name = step.name;
propagation_step.name_id = step.name_id; propagation_step.name_id = step.name_id;
@ -508,8 +574,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
{ {
// Doing this step in post-processing provides a few challenges we cannot overcome. // Doing this step in post-processing provides a few challenges we cannot overcome.
// The removal of an initial step imposes some copy overhead in the steps, moving all later // The removal of an initial step imposes some copy overhead in the steps, moving all later
// steps to the front. // steps to the front. In addition, we cannot reduce the travel time that is accumulated at a
// In addition, we cannot reduce the travel time that is accumulated at a different location. // different location.
// As a direct implication, we have to keep the time of the initial/final turns (which adds a // As a direct implication, we have to keep the time of the initial/final turns (which adds a
// few seconds of inaccuracy at both ends. This is acceptable, however, since the turn should // few seconds of inaccuracy at both ends. This is acceptable, however, since the turn should
// usually not be as relevant. // usually not be as relevant.
@ -517,14 +583,16 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
if (steps.size() < 2 || geometry.locations.size() <= 2) if (steps.size() < 2 || geometry.locations.size() <= 2)
return; return;
// if phantom node is located at the connection of two segments, either one can be selected as // if phantom node is located at the connection of two segments, either one can be selected
// as
// turn // turn
// //
// a --- b // a --- b
// | // |
// c // c
// //
// If a route from b to c is requested, both a--b and b--c could be selected as start segment. // If a route from b to c is requested, both a--b and b--c could be selected as start
// segment.
// In case of a--b, we end up with an unwanted turn saying turn-right onto b-c. // In case of a--b, we end up with an unwanted turn saying turn-right onto b-c.
// These cases start off with an initial segment which is of zero length. // These cases start off with an initial segment which is of zero length.
// We have to be careful though, since routing that starts in a roundabout has a valid. // We have to be careful though, since routing that starts in a roundabout has a valid.
@ -558,12 +626,12 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
const auto &current_depart = steps.front(); const auto &current_depart = steps.front();
auto &designated_depart = *(steps.begin() + 1); auto &designated_depart = *(steps.begin() + 1);
// FIXME this is required to be consistent with the route durations. The initial turn is // FIXME this is required to be consistent with the route durations. The initial
// not actually part of the route, though // turn is not actually part of the route, though
designated_depart.duration += current_depart.duration; designated_depart.duration += current_depart.duration;
// update initial turn direction/bearings. Due to the duplicated first coordinate, the // update initial turn direction/bearings. Due to the duplicated first coordinate,
// initial bearing is invalid // the initial bearing is invalid
designated_depart.maneuver = detail::stepManeuverFromGeometry( designated_depart.maneuver = detail::stepManeuverFromGeometry(
TurnInstruction::NO_TURN(), WaypointType::Depart, geometry); TurnInstruction::NO_TURN(), WaypointType::Depart, geometry);
@ -595,8 +663,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
BOOST_ASSERT(geometry.locations.size() >= steps.size()); BOOST_ASSERT(geometry.locations.size() >= steps.size());
auto &next_to_last_step = *(steps.end() - 2); auto &next_to_last_step = *(steps.end() - 2);
// in the end, the situation with the roundabout cannot occur. As a result, we can remove all // in the end, the situation with the roundabout cannot occur. As a result, we can remove
// zero-length instructions // all zero-length instructions
if (next_to_last_step.distance <= 1) if (next_to_last_step.distance <= 1)
{ {
geometry.locations.pop_back(); geometry.locations.pop_back();

View File

@ -205,7 +205,7 @@ Intersection IntersectionGenerator::mergeSegregatedRoads(Intersection intersecti
return result; return result;
} }
}; };
if (intersection.size() == 1) if (intersection.size() <= 1)
return intersection; return intersection;
const bool is_connected_to_roundabout = [this,&intersection]() { const bool is_connected_to_roundabout = [this,&intersection]() {
@ -245,7 +245,6 @@ Intersection IntersectionGenerator::mergeSegregatedRoads(Intersection intersecti
intersection.pop_back(); intersection.pop_back();
} }
else if (mergable(0, 1)) else if (mergable(0, 1))
{ {
const double correction_factor = (intersection[1].turn.angle) / 2; const double correction_factor = (intersection[1].turn.angle) / 2;

View File

@ -2,11 +2,13 @@
#include "extractor/guidance/intersection_handler.hpp" #include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/toolkit.hpp" #include "extractor/guidance/toolkit.hpp"
#include "util/guidance/toolkit.hpp"
#include "util/simple_logger.hpp" #include "util/simple_logger.hpp"
#include <algorithm> #include <algorithm>
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData; using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
using osrm::util::guidance::getTurnDirection;
namespace osrm namespace osrm
{ {

View File

@ -2,6 +2,10 @@
#include "extractor/guidance/intersection_scenario_three_way.hpp" #include "extractor/guidance/intersection_scenario_three_way.hpp"
#include "extractor/guidance/toolkit.hpp" #include "extractor/guidance/toolkit.hpp"
#include "util/guidance/toolkit.hpp"
using osrm::util::guidance::angularDeviation;
namespace osrm namespace osrm
{ {
namespace extractor namespace extractor

View File

@ -3,12 +3,16 @@
#include "extractor/guidance/toolkit.hpp" #include "extractor/guidance/toolkit.hpp"
#include "util/simple_logger.hpp" #include "util/simple_logger.hpp"
#include "util/guidance/toolkit.hpp"
#include <limits> #include <limits>
#include <utility> #include <utility>
#include <boost/assert.hpp> #include <boost/assert.hpp>
using osrm::util::guidance::angularDeviation;
using osrm::util::guidance::getTurnDirection;
namespace osrm namespace osrm
{ {
namespace extractor namespace extractor

View File

@ -2,14 +2,18 @@
#include "extractor/guidance/roundabout_handler.hpp" #include "extractor/guidance/roundabout_handler.hpp"
#include "extractor/guidance/toolkit.hpp" #include "extractor/guidance/toolkit.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/guidance/toolkit.hpp"
#include "util/simple_logger.hpp" #include "util/simple_logger.hpp"
#include <algorithm>
#include <cmath> #include <cmath>
#include <set>
#include <unordered_set> #include <unordered_set>
#include <boost/assert.hpp> #include <boost/assert.hpp>
using osrm::util::guidance::getTurnDirection;
namespace osrm namespace osrm
{ {
namespace extractor namespace extractor
@ -19,8 +23,10 @@ namespace guidance
RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_based_graph, RoundaboutHandler::RoundaboutHandler(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,
: IntersectionHandler(node_based_graph, node_info_list, name_table) const CompressedEdgeContainer &compressed_edge_container)
: IntersectionHandler(node_based_graph, node_info_list, name_table),
compressed_edge_container(compressed_edge_container)
{ {
} }
@ -31,7 +37,11 @@ bool RoundaboutHandler::canProcess(const NodeID from_nid,
const Intersection &intersection) const const Intersection &intersection) const
{ {
const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection); const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
return flags.on_roundabout || flags.can_enter; if (!flags.on_roundabout && !flags.can_enter)
return false;
const auto roundabout_type = getRoundaboutType(node_based_graph.GetTarget(via_eid));
return roundabout_type != RoundaboutType::None;
} }
Intersection RoundaboutHandler:: Intersection RoundaboutHandler::
@ -39,10 +49,10 @@ operator()(const NodeID from_nid, const EdgeID via_eid, Intersection intersectio
{ {
invalidateExitAgainstDirection(from_nid, via_eid, intersection); invalidateExitAgainstDirection(from_nid, via_eid, intersection);
const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection); const auto flags = getRoundaboutFlags(from_nid, via_eid, intersection);
const bool is_rotary = isRotary(node_based_graph.GetTarget(via_eid)); const auto roundabout_type = getRoundaboutType(node_based_graph.GetTarget(via_eid));
// find the radius of the roundabout // find the radius of the roundabout
return handleRoundabouts(is_rotary, via_eid, flags.on_roundabout, flags.can_exit_separately, return handleRoundabouts(roundabout_type, via_eid, flags.on_roundabout,
std::move(intersection)); flags.can_exit_separately, std::move(intersection));
} }
detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags( detail::RoundaboutFlags RoundaboutHandler::getRoundaboutFlags(
@ -116,7 +126,74 @@ void RoundaboutHandler::invalidateExitAgainstDirection(const NodeID from_nid,
} }
} }
bool RoundaboutHandler::isRotary(const NodeID nid) const // If we want to see a roundabout as a turn, the exits have to be distinct enough to be seen a
// dedicated turns. We are limiting it to four-way intersections with well distinct bearings.
// All entry/roads and exit roads have to be simple. Not segregated roads.
// Processing segregated roads would technically require an angle of the turn to be available
// in postprocessing since we correct the turn-angle in turn-generaion.
bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
const std::set<NodeID> &roundabout_nodes) const
{
// translate a node ID into its respective coordinate stored in the node_info_list
const auto getCoordinate = [this](const NodeID node) {
return util::Coordinate(node_info_list[node].lon, node_info_list[node].lat);
};
const bool has_limited_size = roundabout_nodes.size() <= 4;
if (!has_limited_size)
return false;
const bool simple_exits = !std::find_if( roundabout_nodes.begin(), roundabout_nodes.end(), [this]( const NodeID node ){
return (node_based_graph.GetOutDegree(node) > 3);
});
if (!simple_exits)
return false;
// Find all exit bearings. Only if they are well distinct (at least 60 degrees between
// them), we allow a roundabout turn
const auto exit_bearings = [this, &roundabout_nodes, getCoordinate]() {
std::vector<double> result;
for (const auto node : roundabout_nodes)
{
// given the reverse edge and the forward edge on a roundabout, a simple entry/exit
// can only contain a single further road
for (const auto edge : node_based_graph.GetAdjacentEdgeRange(node))
{
const auto edge_data = node_based_graph.GetEdgeData(edge);
if (edge_data.roundabout)
continue;
// there is a single non-roundabout edge
const auto src_coordinate = getCoordinate(node);
const auto next_coordinate = getRepresentativeCoordinate(
node, node_based_graph.GetTarget(edge), edge, edge_data.reversed,
compressed_edge_container, node_info_list);
result.push_back(
util::coordinate_calculation::bearing(src_coordinate, next_coordinate));
break;
}
}
std::sort(result.begin(), result.end());
return result;
}();
const bool well_distinct_bearings = [](const std::vector<double> &bearings) {
for (std::size_t bearing_index = 0; bearing_index < bearings.size(); ++bearing_index)
{
const double difference =
std::abs(bearings[(bearing_index + 1) % bearings.size()] - bearings[bearing_index]);
// we assume non-narrow turns as well distinct
if (difference <= NARROW_TURN_ANGLE)
return false;
}
return true;
}(exit_bearings);
return well_distinct_bearings;
}
RoundaboutType RoundaboutHandler::getRoundaboutType(const NodeID nid) const
{ {
// translate a node ID into its respective coordinate stored in the node_info_list // translate a node ID into its respective coordinate stored in the node_info_list
const auto getCoordinate = [this](const NodeID node) { const auto getCoordinate = [this](const NodeID node) {
@ -168,31 +245,33 @@ bool RoundaboutHandler::isRotary(const NodeID nid) const
NodeID last_node = nid; NodeID last_node = nid;
while (0 == roundabout_nodes.count(last_node)) while (0 == roundabout_nodes.count(last_node))
{ {
// only count exits/entry locations
if (node_based_graph.GetOutDegree(last_node) > 2)
roundabout_nodes.insert(last_node); roundabout_nodes.insert(last_node);
const auto eid = getNextOnRoundabout(last_node); const auto eid = getNextOnRoundabout(last_node);
if (eid == SPECIAL_EDGEID) if (eid == SPECIAL_EDGEID)
{ {
util::SimpleLogger().Write(logDEBUG) << "Non-Loop Roundabout found."; return RoundaboutType::None;
return false;
} }
last_node = node_based_graph.GetTarget(eid); last_node = node_based_graph.GetTarget(eid);
if (last_node == nid)
break;
} }
// do we have a dedicated name for the rotary, if not its a roundabout // a roundabout that cannot be entered or exited should not get here
// This function can theoretically fail if the roundabout name is partly if (roundabout_nodes.size() == 0)
// used with a reference and without. This will be fixed automatically return RoundaboutType::None;
// when we handle references separately or if the useage is more consistent
if (roundabout_name_id == 0 || connected_names.count(roundabout_name_id)) // More a traffic loop than anything else, currently treated as roundabout turn
if (roundabout_nodes.size() == 1)
{ {
return false; return RoundaboutType::RoundaboutIntersection;
} }
if (roundabout_nodes.size() <= 1)
{
return false;
}
// calculate the radius of the roundabout/rotary. For two coordinates, we assume a minimal // calculate the radius of the roundabout/rotary. For two coordinates, we assume a minimal
// circle // circle
// with both vertices right at the other side (so half their distance in meters). // with both vertices right at the other side (so half their distance in meters).
@ -217,12 +296,42 @@ bool RoundaboutHandler::isRotary(const NodeID nid) const
// The radius computation can result in infinity, if the three coordinates are non-distinct. // The radius computation can result in infinity, if the three coordinates are non-distinct.
// To stay on the safe side, we say its not a rotary // To stay on the safe side, we say its not a rotary
if (std::isinf(radius)) if (std::isinf(radius))
return false; return RoundaboutType::Roundabout;
return radius > MAX_ROUNDABOUT_RADIUS; // not within the dedicated radii for special roundabouts
if (radius > MAX_ROUNDABOUT_INTERSECTION_RADIUS && radius <= MAX_ROUNDABOUT_RADIUS)
return RoundaboutType::Roundabout;
if (radius > MAX_ROUNDABOUT_RADIUS)
{
// do we have a dedicated name for the rotary, if not its a roundabout
// This function can theoretically fail if the roundabout name is partly
// used with a reference and without. This will be fixed automatically
// when we handle references separately or if the useage is more consistent
if (0 != roundabout_name_id && 0 == connected_names.count(roundabout_name_id))
return RoundaboutType::Rotary;
else
return RoundaboutType::Roundabout;
} }
Intersection RoundaboutHandler::handleRoundabouts(const bool is_rotary, if (radius <= MAX_ROUNDABOUT_INTERSECTION_RADIUS)
{
const bool qualifies_as_roundabout_nitersection =
qualifiesAsRoundaboutIntersection(roundabout_nodes);
if (qualifies_as_roundabout_nitersection)
{
return RoundaboutType::RoundaboutIntersection;
}
else
{
return RoundaboutType::Roundabout;
}
}
return RoundaboutType::Roundabout;
}
Intersection RoundaboutHandler::handleRoundabouts(const RoundaboutType roundabout_type,
const EdgeID via_eid, const EdgeID via_eid,
const bool on_roundabout, const bool on_roundabout,
const bool can_exit_roundabout_separately, const bool can_exit_roundabout_separately,
@ -248,14 +357,14 @@ Intersection RoundaboutHandler::handleRoundabouts(const bool is_rotary,
} }
else else
{ {
turn.instruction = turn.instruction = TurnInstruction::REMAIN_ROUNDABOUT(
TurnInstruction::REMAIN_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle)); roundabout_type, getTurnDirection(turn.angle));
} }
} }
else else
{ {
turn.instruction = turn.instruction =
TurnInstruction::EXIT_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle)); TurnInstruction::EXIT_ROUNDABOUT(roundabout_type, getTurnDirection(turn.angle));
} }
} }
return intersection; return intersection;
@ -269,20 +378,17 @@ Intersection RoundaboutHandler::handleRoundabouts(const bool is_rotary,
const auto &out_data = node_based_graph.GetEdgeData(turn.eid); const auto &out_data = node_based_graph.GetEdgeData(turn.eid);
if (out_data.roundabout) if (out_data.roundabout)
{ {
turn.instruction =
TurnInstruction::ENTER_ROUNDABOUT(is_rotary, getTurnDirection(turn.angle));
if (can_exit_roundabout_separately) if (can_exit_roundabout_separately)
{ turn.instruction = TurnInstruction::ENTER_ROUNDABOUT_AT_EXIT(
if (turn.instruction.type == TurnType::EnterRotary) roundabout_type, getTurnDirection(turn.angle));
turn.instruction.type = TurnType::EnterRotaryAtExit; else
if (turn.instruction.type == TurnType::EnterRoundabout) turn.instruction = TurnInstruction::ENTER_ROUNDABOUT(
turn.instruction.type = TurnType::EnterRoundaboutAtExit; roundabout_type, getTurnDirection(turn.angle));
}
} }
else else
{ {
turn.instruction = TurnInstruction::ENTER_AND_EXIT_ROUNDABOUT( turn.instruction = TurnInstruction::ENTER_AND_EXIT_ROUNDABOUT(
is_rotary, getTurnDirection(turn.angle)); roundabout_type, getTurnDirection(turn.angle));
} }
} }
return intersection; return intersection;

View File

@ -3,6 +3,7 @@
#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/simple_logger.hpp" #include "util/simple_logger.hpp"
#include <cstddef> #include <cstddef>
@ -11,6 +12,8 @@
#include <set> #include <set>
#include <unordered_set> #include <unordered_set>
using osrm::util::guidance::getTurnDirection;
namespace osrm namespace osrm
{ {
namespace extractor namespace extractor
@ -36,7 +39,7 @@ 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, name_table), roundabout_handler(node_based_graph, node_info_list, name_table, compressed_edge_container),
motorway_handler(node_based_graph, node_info_list, name_table), motorway_handler(node_based_graph, node_info_list, name_table),
turn_handler(node_based_graph, node_info_list, name_table) turn_handler(node_based_graph, node_info_list, name_table)
{ {

View File

@ -4,6 +4,7 @@
#include "extractor/guidance/turn_handler.hpp" #include "extractor/guidance/turn_handler.hpp"
#include "util/simple_logger.hpp" #include "util/simple_logger.hpp"
#include "util/guidance/toolkit.hpp"
#include <limits> #include <limits>
#include <utility> #include <utility>
@ -11,6 +12,8 @@
#include <boost/assert.hpp> #include <boost/assert.hpp>
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData; using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
using osrm::util::guidance::getTurnDirection;
using osrm::util::guidance::angularDeviation;
namespace osrm namespace osrm
{ {