Refactor fork handling in guidance (#3264)

refactor fork handler
This commit is contained in:
Huyen Chau Nguyen 2017-01-07 14:13:32 +01:00 committed by GitHub
parent 15c8fd326f
commit f313cb9913
7 changed files with 336 additions and 193 deletions

View File

@ -9,6 +9,7 @@
#include <vector>
#include "util/bearing.hpp"
#include "util/log.hpp"
#include "util/node_based_graph.hpp"
#include "util/typedefs.hpp" // EdgeID
@ -26,6 +27,8 @@ namespace guidance
{
// the shape of an intersection only knows about edge IDs and bearings
// `bearing` is the direction in clockwise angle from true north after taking the turn:
// 0 = heading north, 90 = east, 180 = south, 270 = west
struct IntersectionShapeData
{
EdgeID eid;
@ -180,6 +183,12 @@ template <typename Self> struct EnableIntersectionOps
auto comp = makeCompareAngularDeviation(angle);
return boost::range::min_element(*self(), comp);
}
// returns a non-const_interator
auto findClosestTurn(double angle)
{
auto comp = makeCompareAngularDeviation(angle);
return std::min_element(self()->begin(), self()->end(), comp);
}
/* Check validity of the intersection object. We assume a few basic properties every set of
* connected roads should follow throughout guidance pre-processing. This utility function
@ -259,6 +268,18 @@ template <typename Self> struct EnableIntersectionOps
return filter(*candidate) ? self()->end() : candidate;
}
// check if all roads between begin and end allow entry
template <typename InputIt>
bool hasAllValidEntries(const InputIt begin, const InputIt end) const
{
static_assert(
std::is_base_of<std::input_iterator_tag,
typename std::iterator_traits<InputIt>::iterator_category>::value,
"hasAllValidEntries() only accepts input iterators");
return std::all_of(
begin, end, [](const IntersectionViewData &road) { return road.entry_allowed; });
}
private:
auto self() { return static_cast<Self *>(this); }
auto self() const { return static_cast<const Self *>(this); }
@ -271,6 +292,30 @@ struct IntersectionView final : std::vector<IntersectionViewData>, //
using Base = std::vector<IntersectionViewData>;
};
// `Intersection` is a relative view of an intersection by an incoming edge.
// `Intersection` are streets at an intersection ordered from from sharp right counter-clockwise to
// sharp left where `intersection[0]` is _always_ a u-turn
// An intersection is an ordered list of connected roads ordered from from sharp right
// counter-clockwise to sharp left where `intersection[0]` is always a u-turn
//
// |
// |
// (intersec[3])
// |
// |
// |
// nid ---(via_eid/intersec[0])--- nbg.GetTarget(via) ---(intersec[2])---
// |
// |
// |
// (intersec[1])
// |
// |
//
// intersec := intersection
// nbh := node_based_graph
//
struct Intersection final : std::vector<ConnectedRoad>, //
EnableShapeOps<Intersection>, //
EnableIntersectionOps<Intersection> //

View File

@ -45,7 +45,7 @@ class IntersectionHandler
virtual bool
canProcess(const NodeID nid, const EdgeID via_eid, const Intersection &intersection) const = 0;
// process the intersection
// handle and process the intersection
virtual Intersection
operator()(const NodeID nid, const EdgeID via_eid, Intersection intersection) const = 0;

View File

@ -10,6 +10,8 @@
#include "util/name_table.hpp"
#include "util/node_based_graph.hpp"
#include <boost/optional.hpp>
#include <cstddef>
#include <utility>
#include <vector>
@ -45,9 +47,24 @@ class TurnHandler : public IntersectionHandler
Intersection intersection) const override final;
private:
struct Fork
{
const Intersection::iterator right;
const Intersection::iterator left;
const std::size_t size;
Fork(const Intersection::iterator right, const Intersection::iterator left);
};
bool isObviousOfTwo(const EdgeID via_edge,
const ConnectedRoad &road,
const ConnectedRoad &other) const;
bool hasObvious(const EdgeID &via_edge, const Fork &fork) const;
boost::optional<Fork> findForkCandidatesByGeometry(Intersection &intersection) const;
bool isCompatibleByRoadClass(const Intersection &intersection, const Fork fork) const;
// Dead end.
OSRM_ATTR_WARN_UNUSED
Intersection handleOneWayTurn(Intersection intersection) const;
@ -68,8 +85,7 @@ class TurnHandler : public IntersectionHandler
handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
// Classification
std::pair<std::size_t, std::size_t> findFork(const EdgeID via_edge,
const Intersection &intersection) const;
boost::optional<Fork> findFork(const EdgeID via_edge, Intersection &intersection) const;
OSRM_ATTR_WARN_UNUSED
Intersection assignLeftTurns(const EdgeID via_edge,

View File

@ -3,6 +3,8 @@
#include <limits>
#include <string>
#include <boost/range/adaptors.hpp>
using osrm::util::angularDeviation;
namespace osrm

View File

@ -42,6 +42,8 @@ IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node
{
}
// Inspects an intersection and a turn from via_edge onto road from the possible basic turn types
// (OnRamp, Continue, Turn) find the suitable turn type
TurnType::Enum IntersectionHandler::findBasicTurnType(const EdgeID via_edge,
const ConnectedRoad &road) const
{
@ -254,9 +256,13 @@ void IntersectionHandler::assignFork(const EdgeID via_edge,
else
{
if (low_priority_left && !low_priority_right)
{
left.instruction = {TurnType::Turn, DirectionModifier::SlightLeft};
}
else
{
left.instruction = {TurnType::Fork, DirectionModifier::SlightLeft};
}
}
// right side of fork
@ -265,9 +271,13 @@ void IntersectionHandler::assignFork(const EdgeID via_edge,
else
{
if (low_priority_right && !low_priority_left)
{
right.instruction = {TurnType::Turn, DirectionModifier::SlightRight};
}
else
{
right.instruction = {TurnType::Fork, DirectionModifier::SlightRight};
}
}
}

View File

@ -56,7 +56,8 @@ bool findPreviousIntersection(const NodeID node_v,
if (via_edge_length > COMBINE_DISTANCE_CUTOFF)
return false;
// Node -> Via_Edge -> Intersection[0 == UTURN] -> reverse_of(via_edge) -> Intersection at node
// Node -> Via_Edge -> Intersection[0 == UTURN] -> reverse_of(via_edge) -> Intersection at
// node
// (looking at the reverse direction).
const auto node_w = node_based_graph.GetTarget(via_edge);
const auto u_turn_at_node_w = intersection[0].eid;

View File

@ -9,18 +9,38 @@
#include <utility>
#include <boost/assert.hpp>
#include <boost/optional.hpp>
using osrm::extractor::guidance::getTurnDirection;
using osrm::util::angularDeviation;
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace
{
using namespace osrm::extractor::guidance;
// given two adjacent roads in clockwise order and `road1` being a candidate for a fork,
// return false, if next road `road2` is also a fork candidate or
// return true, if `road2` is not a suitable fork candidate and thus, `road1` the outermost fork
bool isOutermostForkCandidate(const ConnectedRoad &road1, const ConnectedRoad &road2)
{
const auto angle_between_next_road_and_straight = angularDeviation(road2.angle, STRAIGHT_ANGLE);
const auto angle_between_prev_road_and_next = angularDeviation(road1.angle, road2.angle);
const auto angle_between_prev_road_and_straight = angularDeviation(road1.angle, STRAIGHT_ANGLE);
// a road is a fork candidate if it is close to straight or
// close to a street that goes close to straight
// (reverse to find fork non-candidate)
if (angle_between_next_road_and_straight > NARROW_TURN_ANGLE)
{
if (angle_between_prev_road_and_next > NARROW_TURN_ANGLE ||
angle_between_prev_road_and_straight > GROUP_ANGLE)
{
return true;
}
}
return false;
}
bool isEndOfRoad(const ConnectedRoad &,
const ConnectedRoad &possible_right_turn,
const ConnectedRoad &possible_left_turn)
@ -30,6 +50,40 @@ bool isEndOfRoad(const ConnectedRoad &,
angularDeviation(possible_right_turn.angle, possible_left_turn.angle) >
2 * NARROW_TURN_ANGLE;
}
template <typename InputIt>
InputIt findOutermostForkCandidate(const InputIt start, const InputIt end)
{
static_assert(std::is_base_of<std::input_iterator_tag,
typename std::iterator_traits<InputIt>::iterator_category>::value,
"findOutermostForkCandidate() only accepts input iterators");
const auto outermost = std::adjacent_find(start, end, isOutermostForkCandidate);
if (outermost != end)
{
return outermost;
}
// if all roads are part of a fork, set `candidate` to the last road
else
{
return outermost - 1;
}
}
}
namespace osrm
{
namespace extractor
{
namespace guidance
{
// a wrapper to handle road indices of forks at intersections
TurnHandler::Fork::Fork(const Intersection::iterator right, const Intersection::iterator left)
: right(right), left(left), size((left - right) + 1)
{
BOOST_ASSERT(right < left);
BOOST_ASSERT(size >= 2);
BOOST_ASSERT(size <= 3);
}
TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
@ -50,12 +104,16 @@ bool TurnHandler::canProcess(const NodeID, const EdgeID, const Intersection &) c
return true;
}
// Handles and processes possible turns
// Input parameters describe an intersection as described in
// #IntersectionExplanation@intersection_handler.hpp
Intersection TurnHandler::
operator()(const NodeID, const EdgeID via_edge, Intersection intersection) const
{
if (intersection.size() == 1)
return handleOneWayTurn(std::move(intersection));
// if u-turn is allowed, set the turn type of intersection[0] to its basic type and u-turn
if (intersection[0].entry_allowed)
{
intersection[0].instruction = {findBasicTurnType(via_edge, intersection[0]),
@ -86,48 +144,40 @@ Intersection TurnHandler::handleTwoWayTurn(const EdgeID via_edge, Intersection i
return intersection;
}
// checks whether it is obvious to turn on `road` coming from `via_edge` while there is an`other`
// road at the same intersection
bool TurnHandler::isObviousOfTwo(const EdgeID via_edge,
const ConnectedRoad &road,
const ConnectedRoad &other) const
{
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
const auto &via_data = node_based_graph.GetEdgeData(via_edge);
const auto &road_data = node_based_graph.GetEdgeData(road.eid);
const auto &other_data = node_based_graph.GetEdgeData(other.eid);
const auto &via_classification = via_data.road_classification;
const auto &road_classification = road_data.road_classification;
const auto &other_classification = other_data.road_classification;
const auto &first_data = node_based_graph.GetEdgeData(road.eid);
const auto &second_data = node_based_graph.GetEdgeData(other.eid);
const auto &first_classification = first_data.road_classification;
const auto &second_classification = second_data.road_classification;
const bool is_ramp = first_classification.IsRampClass();
const bool is_obvious_by_road_class =
(!is_ramp &&
(2 * first_classification.GetPriority() < second_classification.GetPriority()) &&
in_data.road_classification == first_classification) ||
(!first_classification.IsLowPriorityRoadClass() &&
second_classification.IsLowPriorityRoadClass());
if (is_obvious_by_road_class)
// if one of the given roads is obvious by class, obviousness is trivial
if (obviousByRoadClass(via_classification, road_classification, other_classification))
{
return true;
const bool other_is_obvious_by_road_class =
(!second_classification.IsRampClass() &&
(2 * second_classification.GetPriority() < first_classification.GetPriority()) &&
in_data.road_classification == second_classification) ||
(!second_classification.IsLowPriorityRoadClass() &&
first_classification.IsLowPriorityRoadClass());
if (other_is_obvious_by_road_class)
}
else if (obviousByRoadClass(via_classification, other_classification, road_classification))
{
return false;
}
const bool turn_is_perfectly_straight =
angularDeviation(road.angle, STRAIGHT_ANGLE) < std::numeric_limits<double>::epsilon();
const auto &road_data = node_based_graph.GetEdgeData(road.eid);
const auto same_name = !util::guidance::requiresNameAnnounced(
in_data.name_id, road_data.name_id, name_table, street_name_suffix_table);
if (turn_is_perfectly_straight && in_data.name_id != EMPTY_NAMEID &&
road_data.name_id != EMPTY_NAMEID && same_name)
return true;
if (via_data.name_id != EMPTY_NAMEID)
{
const auto same_name = !util::guidance::requiresNameAnnounced(
via_data.name_id, road_data.name_id, name_table, street_name_suffix_table);
if (turn_is_perfectly_straight && same_name)
{
return true;
}
}
const bool is_much_narrower_than_other =
angularDeviation(other.angle, STRAIGHT_ANGLE) /
@ -139,8 +189,24 @@ bool TurnHandler::isObviousOfTwo(const EdgeID via_edge,
return is_much_narrower_than_other;
}
bool TurnHandler::hasObvious(const EdgeID &via_edge, const Fork &fork) const
{
for (auto road = fork.right; road < fork.left; ++road)
{
if (isObviousOfTwo(via_edge, *road, *(road + 1)) ||
isObviousOfTwo(via_edge, *(road + 1), *road))
{
return true;
}
}
return false;
}
// handles a turn at a three-way intersection _coming from_ `via_edge`
// with `intersection` as described as in #IntersectionExplanation@intersection_handler.hpp
Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const
{
BOOST_ASSERT(intersection.size() == 3);
const auto obvious_index = findObviousTurn(via_edge, intersection);
BOOST_ASSERT(intersection[0].angle < 0.001);
/* Two nearly straight turns -> FORK
@ -151,9 +217,11 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
OOOOOOO
*/
const auto fork_range = findFork(via_edge, intersection);
if (fork_range.first == 1 && fork_range.second == 2 && obvious_index == 0)
assignFork(via_edge, intersection[2], intersection[1]);
auto fork = findFork(via_edge, intersection);
if (fork && obvious_index == 0)
{
assignFork(via_edge, *fork->left, *fork->right);
}
/* T Intersection
@ -174,7 +242,6 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
if (intersection[2].entry_allowed)
{
if (TurnType::OnRamp != findBasicTurnType(via_edge, intersection[2]))
intersection[2].instruction = {TurnType::EndOfRoad, DirectionModifier::Left};
else
intersection[2].instruction = {TurnType::OnRamp, DirectionModifier::Left};
@ -188,7 +255,6 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
{
intersection[1].instruction = getInstructionForObvious(
3, via_edge, isThroughStreet(1, intersection), intersection[1]);
const auto second_direction = (direction_at_one == direction_at_two &&
direction_at_two == DirectionModifier::Straight)
? DirectionModifier::SlightLeft
@ -202,7 +268,6 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
BOOST_ASSERT(obvious_index == 2);
intersection[2].instruction = getInstructionForObvious(
3, via_edge, isThroughStreet(2, intersection), intersection[2]);
const auto first_direction = (direction_at_one == direction_at_two &&
direction_at_one == DirectionModifier::Straight)
? DirectionModifier::SlightRight
@ -225,18 +290,11 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection intersection) const
{
const std::size_t obvious_index = findObviousTurn(via_edge, intersection);
const auto fork_range = findFork(via_edge, intersection);
std::size_t straightmost_turn = 0;
double straightmost_deviation = 180;
for (std::size_t i = 0; i < intersection.size(); ++i)
{
const double deviation = angularDeviation(intersection[i].angle, STRAIGHT_ANGLE);
if (deviation < straightmost_deviation)
{
straightmost_deviation = deviation;
straightmost_turn = i;
}
}
const auto fork = findFork(via_edge, intersection);
const auto straightmost = intersection.findClosestTurn(STRAIGHT_ANGLE);
const auto straightmost_index = std::distance(intersection.begin(), straightmost);
const auto straightmost_angle_dev = angularDeviation(straightmost->angle, STRAIGHT_ANGLE);
// check whether the obvious choice is actually a through street
if (obvious_index != 0)
@ -251,62 +309,65 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index + 1);
intersection = assignRightTurns(via_edge, std::move(intersection), obvious_index);
}
else if (fork_range.first != 0 && fork_range.second - fork_range.first <= 2) // found fork
else if (fork) // found fork
{
if (fork_range.second - fork_range.first == 1)
if (fork->size == 2)
{
auto &left = intersection[fork_range.second];
auto &right = intersection[fork_range.first];
const auto left_classification =
node_based_graph.GetEdgeData(left.eid).road_classification;
node_based_graph.GetEdgeData((*fork->left).eid).road_classification;
const auto right_classification =
node_based_graph.GetEdgeData(right.eid).road_classification;
node_based_graph.GetEdgeData((*fork->right).eid).road_classification;
if (canBeSeenAsFork(left_classification, right_classification))
assignFork(via_edge, left, right);
{
assignFork(via_edge, *fork->left, *fork->right);
}
else if (left_classification.GetPriority() > right_classification.GetPriority())
{
right.instruction =
getInstructionForObvious(intersection.size(), via_edge, false, right);
left.instruction = {findBasicTurnType(via_edge, left),
DirectionModifier::SlightLeft};
(*fork->right).instruction =
getInstructionForObvious(intersection.size(), via_edge, false, *fork->right);
(*fork->left).instruction = {findBasicTurnType(via_edge, *fork->left),
DirectionModifier::SlightLeft};
}
else
{
left.instruction =
getInstructionForObvious(intersection.size(), via_edge, false, left);
right.instruction = {findBasicTurnType(via_edge, right),
DirectionModifier::SlightRight};
(*fork->left).instruction =
getInstructionForObvious(intersection.size(), via_edge, false, *fork->left);
(*fork->right).instruction = {findBasicTurnType(via_edge, *fork->right),
DirectionModifier::SlightRight};
}
}
else if (fork_range.second - fork_range.first == 2)
else if (fork->size == 3)
{
assignFork(via_edge,
intersection[fork_range.second],
intersection[fork_range.first + 1],
intersection[fork_range.first]);
*fork->left,
// middle fork road
*(fork->right + 1),
*fork->right);
}
// assign left/right turns
intersection = assignLeftTurns(via_edge, std::move(intersection), fork_range.second + 1);
intersection = assignRightTurns(via_edge, std::move(intersection), fork_range.first);
const auto left_index = fork->left - intersection.begin();
const auto right_index = fork->right - intersection.begin();
intersection = assignLeftTurns(via_edge, std::move(intersection), left_index + 1);
intersection = assignRightTurns(via_edge, std::move(intersection), right_index);
}
else if (straightmost_deviation < FUZZY_ANGLE_DIFFERENCE &&
!intersection[straightmost_turn].entry_allowed)
else if (straightmost_angle_dev < FUZZY_ANGLE_DIFFERENCE && !straightmost->entry_allowed)
{
// invalid straight turn
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1);
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn);
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index + 1);
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index);
}
// no straight turn
else if (intersection[straightmost_turn].angle > 180)
else if (straightmost->angle > 180)
{
// at most three turns on either side
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn);
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn);
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index);
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index);
}
else if (intersection[straightmost_turn].angle < 180)
else if (straightmost->angle < 180)
{
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1);
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn + 1);
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index + 1);
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index + 1);
}
else
{
@ -470,125 +531,133 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
return intersection;
}
std::pair<std::size_t, std::size_t> TurnHandler::findFork(const EdgeID via_edge,
const Intersection &intersection) const
// finds a fork candidate by just looking at the geometry and angle of an intersection
boost::optional<TurnHandler::Fork>
TurnHandler::findForkCandidatesByGeometry(Intersection &intersection) const
{
std::size_t best = 0;
double best_deviation = 180;
// TODO handle road classes
for (std::size_t i = 1; i < intersection.size(); ++i)
if (intersection.size() >= 3)
{
const double deviation = angularDeviation(intersection[i].angle, STRAIGHT_ANGLE);
if (intersection[i].entry_allowed && deviation < best_deviation)
const auto straightmost = intersection.findClosestTurn(STRAIGHT_ANGLE);
const auto straightmost_index = std::distance(intersection.begin(), straightmost);
const auto straightmost_angle_dev = angularDeviation(straightmost->angle, STRAIGHT_ANGLE);
// Forks can only happen when two or more roads have a pretty narrow angle between each
// other and are close to going straight
//
//
// left right left right
// \ / \ | /
// \ / \|/
// | |
// | |
// | |
//
// possibly a fork possibly a fork
//
//
// left left
// / \
// /____ right \ ______ right
// | |
// | |
// | |
//
// not a fork cause not a fork cause
// it's not going angle is too wide
// straigthish
//
//
// left and right will be indices of the leftmost and rightmost connected roads that are
// fork candidates
if (straightmost_angle_dev <= NARROW_TURN_ANGLE)
{
best_deviation = deviation;
best = i;
// find the rightmost road that might be part of a fork
const auto right = findOutermostForkCandidate(
intersection.rend() - straightmost_index - 1, intersection.rend());
const int right_index = intersection.rend() - right - 1;
const auto forward_right = intersection.begin() + right_index;
// find the leftmost road that might be part of a fork
const auto left = findOutermostForkCandidate(straightmost, intersection.end());
// if the leftmost and rightmost roads with the conditions above are the same
// or if there are more than three fork candidates
// they cannot be fork candidates
if (forward_right < left && left - forward_right + 1 <= 3)
{
return Fork(forward_right, left);
}
}
}
if (best_deviation <= NARROW_TURN_ANGLE)
return boost::none;
}
// check if the fork candidates (all roads between left and right) and the
// incoming edge are compatible by class
bool TurnHandler::isCompatibleByRoadClass(const Intersection &intersection, const Fork fork) const
{
const auto via_class = node_based_graph.GetEdgeData(intersection[0].eid).road_classification;
// if any of the considered roads is a link road, it cannot be a fork
// except if rightmost fork candidate is also a link road
const auto is_right_link_class =
node_based_graph.GetEdgeData((*fork.right).eid).road_classification.IsLinkClass();
if (!std::all_of(fork.right + 1, fork.left + 1, [&](ConnectedRoad &road) {
return is_right_link_class ==
node_based_graph.GetEdgeData(road.eid).road_classification.IsLinkClass();
}))
{
std::size_t left = best, right = best;
while (
left + 1 < intersection.size() &&
(angularDeviation(intersection[left + 1].angle, STRAIGHT_ANGLE) <= NARROW_TURN_ANGLE ||
(angularDeviation(intersection[left].angle, intersection[left + 1].angle) <=
NARROW_TURN_ANGLE &&
angularDeviation(intersection[left].angle, STRAIGHT_ANGLE) <= GROUP_ANGLE)))
++left;
while (
right > 1 &&
(angularDeviation(intersection[right - 1].angle, STRAIGHT_ANGLE) <= NARROW_TURN_ANGLE ||
(angularDeviation(intersection[right].angle, intersection[right - 1].angle) <
NARROW_TURN_ANGLE &&
angularDeviation(intersection[right - 1].angle, STRAIGHT_ANGLE) <= GROUP_ANGLE)))
--right;
return false;
}
if (left == right)
return std::make_pair(std::size_t{0}, std::size_t{0});
return std::all_of(fork.right, fork.left + 1, [&](ConnectedRoad &base) {
const auto base_class = node_based_graph.GetEdgeData(base.eid).road_classification;
// check that there is no turn obvious == check that all turns are non-onvious
return std::all_of(fork.right, fork.left + 1, [&](ConnectedRoad &compare) {
const auto compare_class =
node_based_graph.GetEdgeData(compare.eid).road_classification;
return compare.eid == base.eid ||
!(obviousByRoadClass(via_class, base_class, compare_class));
});
});
}
const bool valid_indices = 0 < right && right < left;
// Checks whether a three-way-intersection coming from `via_edge` is a fork
// with `intersection` as described as in #IntersectionExplanation@intersection_handler.hpp
boost::optional<TurnHandler::Fork> TurnHandler::findFork(const EdgeID via_edge,
Intersection &intersection) const
{
const auto fork = findForkCandidatesByGeometry(intersection);
if (fork)
{
// makes sure that the fork is isolated from other neighbouring streets on the left and
// right side
const auto next =
(fork->left + 1) == intersection.end() ? intersection.begin() : (fork->left + 1);
const bool separated_at_left_side =
angularDeviation(intersection[left].angle,
intersection[(left + 1) % intersection.size()].angle) >= GROUP_ANGLE;
angularDeviation(fork->left->angle, next->angle) >= GROUP_ANGLE;
const bool separated_at_right_side =
right > 0 &&
angularDeviation(intersection[right].angle, intersection[right - 1].angle) >=
GROUP_ANGLE;
angularDeviation(fork->right->angle, (fork->right - 1)->angle) >= GROUP_ANGLE;
const bool not_more_than_three = (left - right) <= 2;
const bool has_obvious = [&]() {
if (left - right == 1)
{
return isObviousOfTwo(via_edge, intersection[left], intersection[right]) ||
isObviousOfTwo(via_edge, intersection[right], intersection[left]);
}
else if (left - right == 2)
{
return isObviousOfTwo(via_edge, intersection[right + 1], intersection[right]) ||
isObviousOfTwo(via_edge, intersection[right], intersection[right + 1]) ||
isObviousOfTwo(via_edge, intersection[left], intersection[right + 1]) ||
isObviousOfTwo(via_edge, intersection[right + 1], intersection[left]);
}
return false;
}();
// check whether there is an obvious turn to take; forks are never obvious - if there is an
// obvious turn, it's not a fork
const bool has_obvious = hasObvious(via_edge, *fork);
// A fork can only happen between edges of similar types where none of the ones is obvious
const bool has_compatible_classes = [&]() {
const bool ramp_class = node_based_graph.GetEdgeData(intersection[right].eid)
.road_classification.IsLinkClass();
for (std::size_t index = right + 1; index <= left; ++index)
if (ramp_class !=
node_based_graph.GetEdgeData(intersection[index].eid)
.road_classification.IsLinkClass())
return false;
const auto in_classification =
node_based_graph.GetEdgeData(intersection[0].eid).road_classification;
for (std::size_t base_index = right; base_index <= left; ++base_index)
{
const auto base_classification =
node_based_graph.GetEdgeData(intersection[base_index].eid).road_classification;
for (std::size_t compare_index = right; compare_index <= left; ++compare_index)
{
if (base_index == compare_index)
continue;
const auto compare_classification =
node_based_graph.GetEdgeData(intersection[compare_index].eid)
.road_classification;
if (obviousByRoadClass(
in_classification, base_classification, compare_classification))
return false;
}
}
return true;
}();
const bool has_compatible_classes = isCompatibleByRoadClass(intersection, *fork);
// check if all entries in the fork range allow entry
const bool only_valid_entries = [&]() {
BOOST_ASSERT(right <= left && left < intersection.size());
const bool only_valid_entries =
intersection.hasAllValidEntries(fork->right, fork->left + 1);
// one past the end of the fork range
const auto end_itr = intersection.begin() + left + 1;
const auto has_entry_forbidden = [](const ConnectedRoad &road) {
return !road.entry_allowed;
};
const auto first_disallowed_entry =
std::find_if(intersection.begin() + right, end_itr, has_entry_forbidden);
// if no entry was found that forbids entry, the intersection entries are all valid.
return first_disallowed_entry == end_itr;
}();
// TODO check whether 2*NARROW_TURN is too large
if (valid_indices && separated_at_left_side && separated_at_right_side &&
not_more_than_three && !has_obvious && has_compatible_classes && only_valid_entries)
return std::make_pair(right, left);
if (separated_at_left_side && separated_at_right_side && !has_obvious &&
has_compatible_classes && only_valid_entries)
{
return fork;
}
}
return std::make_pair(std::size_t{0}, std::size_t{0});
return boost::none;
}
void TurnHandler::handleDistinctConflict(const EdgeID via_edge,