Refactors the Turn Handler's Fork Abstraction, resolves #3457.

This commit is contained in:
Huyen Chau Nguyen 2017-01-15 16:09:33 +01:00 committed by Daniel J. H
parent b8beac2d00
commit a40abacfca
2 changed files with 87 additions and 59 deletions

View File

@ -49,10 +49,21 @@ class TurnHandler : public IntersectionHandler
private: private:
struct Fork struct Fork
{ {
const Intersection::iterator right; const Intersection::iterator intersection_base;
const Intersection::iterator left; const Intersection::iterator begin;
const Intersection::iterator end;
const std::size_t size; const std::size_t size;
Fork(const Intersection::iterator right, const Intersection::iterator left); Fork(const Intersection::iterator intersection_base,
const Intersection::iterator begin,
const Intersection::iterator end);
ConnectedRoad &getRight() const;
ConnectedRoad &getLeft() const;
ConnectedRoad &getMiddle() const;
ConnectedRoad &getRight();
ConnectedRoad &getLeft();
ConnectedRoad &getMiddle();
std::size_t getRightIndex() const;
std::size_t getLeftIndex() const;
}; };
bool isObviousOfTwo(const EdgeID via_edge, bool isObviousOfTwo(const EdgeID via_edge,

View File

@ -52,12 +52,12 @@ bool isEndOfRoad(const ConnectedRoad &,
} }
template <typename InputIt> template <typename InputIt>
InputIt findOutermostForkCandidate(const InputIt start, const InputIt end) InputIt findOutermostForkCandidate(const InputIt begin, const InputIt end)
{ {
static_assert(std::is_base_of<std::input_iterator_tag, static_assert(std::is_base_of<std::input_iterator_tag,
typename std::iterator_traits<InputIt>::iterator_category>::value, typename std::iterator_traits<InputIt>::iterator_category>::value,
"findOutermostForkCandidate() only accepts input iterators"); "findOutermostForkCandidate() only accepts input iterators");
const auto outermost = std::adjacent_find(start, end, isOutermostForkCandidate); const auto outermost = std::adjacent_find(begin, end, isOutermostForkCandidate);
if (outermost != end) if (outermost != end)
{ {
return outermost; return outermost;
@ -78,12 +78,36 @@ namespace guidance
{ {
// a wrapper to handle road indices of forks at intersections // a wrapper to handle road indices of forks at intersections
TurnHandler::Fork::Fork(const Intersection::iterator right, const Intersection::iterator left) TurnHandler::Fork::Fork(const Intersection::iterator intersection_base,
: right(right), left(left), size((left - right) + 1) const Intersection::iterator begin,
const Intersection::iterator end)
: intersection_base(intersection_base), begin(begin), end(end), size(std::distance(begin, end))
{ {
BOOST_ASSERT(right < left); BOOST_ASSERT(begin < end);
BOOST_ASSERT(size >= 2); BOOST_ASSERT(size == 2 || size == 3);
BOOST_ASSERT(size <= 3); }
ConnectedRoad &TurnHandler::Fork::getRight() { return *begin; }
ConnectedRoad &TurnHandler::Fork::getLeft() { return *(end - 1); }
ConnectedRoad &TurnHandler::Fork::getMiddle()
{
BOOST_ASSERT(size == 3);
return *(begin + 1);
}
ConnectedRoad &TurnHandler::Fork::getRight() const { return *begin; }
ConnectedRoad &TurnHandler::Fork::getLeft() const { return *(end - 1); }
ConnectedRoad &TurnHandler::Fork::getMiddle() const
{
BOOST_ASSERT(size == 3);
return *(begin + 1);
}
std::size_t TurnHandler::Fork::getRightIndex() const
{
return std::distance(intersection_base, begin);
}
std::size_t TurnHandler::Fork::getLeftIndex() const
{
return std::distance(intersection_base, end) - 1;
} }
TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph, TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
@ -191,15 +215,12 @@ bool TurnHandler::isObviousOfTwo(const EdgeID via_edge,
bool TurnHandler::hasObvious(const EdgeID &via_edge, const Fork &fork) const bool TurnHandler::hasObvious(const EdgeID &via_edge, const Fork &fork) const
{ {
for (auto road = fork.right; road < fork.left; ++road) auto obvious_road =
{ std::adjacent_find(fork.begin, fork.end, [&, this](const auto &a, const auto &b) {
if (isObviousOfTwo(via_edge, *road, *(road + 1)) || return this->isObviousOfTwo(via_edge, a, b) || this->isObviousOfTwo(via_edge, b, a);
isObviousOfTwo(via_edge, *(road + 1), *road)) });
{ // return whether an obvious road was found
return true; return obvious_road != fork.end;
}
}
return false;
} }
// handles a turn at a three-way intersection _coming from_ `via_edge` // handles a turn at a three-way intersection _coming from_ `via_edge`
@ -220,7 +241,7 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
auto fork = findFork(via_edge, intersection); auto fork = findFork(via_edge, intersection);
if (fork && obvious_index == 0) if (fork && obvious_index == 0)
{ {
assignFork(via_edge, *fork->left, *fork->right); assignFork(via_edge, fork->getLeft(), fork->getRight());
} }
/* T Intersection /* T Intersection
@ -306,7 +327,7 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
intersection[obvious_index]); intersection[obvious_index]);
// assign left/right turns // assign left/right turns
intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index + 1); intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index);
intersection = assignRightTurns(via_edge, std::move(intersection), obvious_index); intersection = assignRightTurns(via_edge, std::move(intersection), obvious_index);
} }
else if (fork) // found fork else if (fork) // found fork
@ -314,59 +335,56 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
if (fork->size == 2) if (fork->size == 2)
{ {
const auto left_classification = const auto left_classification =
node_based_graph.GetEdgeData((*fork->left).eid).road_classification; node_based_graph.GetEdgeData(fork->getLeft().eid).road_classification;
const auto right_classification = const auto right_classification =
node_based_graph.GetEdgeData((*fork->right).eid).road_classification; node_based_graph.GetEdgeData(fork->getRight().eid).road_classification;
if (canBeSeenAsFork(left_classification, right_classification)) if (canBeSeenAsFork(left_classification, right_classification))
{ {
assignFork(via_edge, *fork->left, *fork->right); assignFork(via_edge, fork->getLeft(), fork->getRight());
} }
else if (left_classification.GetPriority() > right_classification.GetPriority()) else if (left_classification.GetPriority() > right_classification.GetPriority())
{ {
(*fork->right).instruction = fork->getRight().instruction = getInstructionForObvious(
getInstructionForObvious(intersection.size(), via_edge, false, *fork->right); intersection.size(), via_edge, false, fork->getRight());
(*fork->left).instruction = {findBasicTurnType(via_edge, *fork->left), fork->getLeft().instruction = {findBasicTurnType(via_edge, fork->getLeft()),
DirectionModifier::SlightLeft}; DirectionModifier::SlightLeft};
} }
else else
{ {
(*fork->left).instruction = fork->getLeft().instruction =
getInstructionForObvious(intersection.size(), via_edge, false, *fork->left); getInstructionForObvious(intersection.size(), via_edge, false, fork->getLeft());
(*fork->right).instruction = {findBasicTurnType(via_edge, *fork->right), fork->getRight().instruction = {findBasicTurnType(via_edge, fork->getRight()),
DirectionModifier::SlightRight}; DirectionModifier::SlightRight};
} }
} }
else if (fork->size == 3) else if (fork->size == 3)
{ {
assignFork(via_edge, assignFork(via_edge,
*fork->left, fork->getLeft(),
// middle fork road // middle fork road
*(fork->right + 1), fork->getMiddle(),
*fork->right); fork->getRight());
} }
// assign left/right turns
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 = assignLeftTurns(via_edge, std::move(intersection), fork->getLeftIndex());
intersection = assignRightTurns(via_edge, std::move(intersection), right_index); intersection = assignRightTurns(via_edge, std::move(intersection), fork->getRightIndex());
} }
else if (straightmost_angle_dev < FUZZY_ANGLE_DIFFERENCE && !straightmost->entry_allowed) else if (straightmost_angle_dev < FUZZY_ANGLE_DIFFERENCE && !straightmost->entry_allowed)
{ {
// invalid straight turn // invalid straight turn
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index + 1); intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index);
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index); intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index);
} }
// no straight turn // no straight turn
else if (straightmost->angle > 180) else if (straightmost->angle > 180)
{ {
// at most three turns on either side // at most three turns on either side
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index); intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index - 1);
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index); intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index);
} }
else if (straightmost->angle < 180) else if (straightmost->angle < 180)
{ {
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index + 1); intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index);
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index + 1); intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index + 1);
} }
else else
@ -384,7 +402,7 @@ Intersection TurnHandler::assignLeftTurns(const EdgeID via_edge,
Intersection intersection, Intersection intersection,
const std::size_t starting_at) const const std::size_t starting_at) const
{ {
BOOST_ASSERT(starting_at <= intersection.size()); BOOST_ASSERT(starting_at < intersection.size());
const auto switch_left_and_right = [](Intersection &intersection) { const auto switch_left_and_right = [](Intersection &intersection) {
BOOST_ASSERT(!intersection.empty()); BOOST_ASSERT(!intersection.empty());
@ -396,7 +414,7 @@ Intersection TurnHandler::assignLeftTurns(const EdgeID via_edge,
switch_left_and_right(intersection); switch_left_and_right(intersection);
// account for the u-turn in the beginning // account for the u-turn in the beginning
const auto count = intersection.size() - starting_at + 1; const auto count = intersection.size() - starting_at;
intersection = assignRightTurns(via_edge, std::move(intersection), count); intersection = assignRightTurns(via_edge, std::move(intersection), count);
switch_left_and_right(intersection); switch_left_and_right(intersection);
@ -575,7 +593,7 @@ TurnHandler::findForkCandidatesByGeometry(Intersection &intersection) const
// find the rightmost road that might be part of a fork // find the rightmost road that might be part of a fork
const auto right = findOutermostForkCandidate( const auto right = findOutermostForkCandidate(
intersection.rend() - straightmost_index - 1, intersection.rend()); intersection.rend() - straightmost_index - 1, intersection.rend());
const int right_index = intersection.rend() - right - 1; const std::size_t right_index = intersection.rend() - right - 1;
const auto forward_right = intersection.begin() + right_index; const auto forward_right = intersection.begin() + right_index;
// find the leftmost road that might be part of a fork // find the leftmost road that might be part of a fork
const auto left = findOutermostForkCandidate(straightmost, intersection.end()); const auto left = findOutermostForkCandidate(straightmost, intersection.end());
@ -583,9 +601,9 @@ TurnHandler::findForkCandidatesByGeometry(Intersection &intersection) const
// if the leftmost and rightmost roads with the conditions above are the same // if the leftmost and rightmost roads with the conditions above are the same
// or if there are more than three fork candidates // or if there are more than three fork candidates
// they cannot be fork candidates // they cannot be fork candidates
if (forward_right < left && left - forward_right + 1 <= 3) if (forward_right < left && left - forward_right < 3)
{ {
return Fork(forward_right, left); return Fork(intersection.begin(), forward_right, left + 1);
} }
} }
} }
@ -601,8 +619,8 @@ bool TurnHandler::isCompatibleByRoadClass(const Intersection &intersection, cons
// if any of the considered roads is a link road, it cannot be a fork // 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 // except if rightmost fork candidate is also a link road
const auto is_right_link_class = const auto is_right_link_class =
node_based_graph.GetEdgeData((*fork.right).eid).road_classification.IsLinkClass(); node_based_graph.GetEdgeData(fork.getRight().eid).road_classification.IsLinkClass();
if (!std::all_of(fork.right + 1, fork.left + 1, [&](ConnectedRoad &road) { if (!std::all_of(fork.begin + 1, fork.end, [&](ConnectedRoad &road) {
return is_right_link_class == return is_right_link_class ==
node_based_graph.GetEdgeData(road.eid).road_classification.IsLinkClass(); node_based_graph.GetEdgeData(road.eid).road_classification.IsLinkClass();
})) }))
@ -610,10 +628,10 @@ bool TurnHandler::isCompatibleByRoadClass(const Intersection &intersection, cons
return false; return false;
} }
return std::all_of(fork.right, fork.left + 1, [&](ConnectedRoad &base) { return std::all_of(fork.begin, fork.end, [&](ConnectedRoad &base) {
const auto base_class = node_based_graph.GetEdgeData(base.eid).road_classification; 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 // 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) { return std::all_of(fork.begin, fork.end, [&](ConnectedRoad &compare) {
const auto compare_class = const auto compare_class =
node_based_graph.GetEdgeData(compare.eid).road_classification; node_based_graph.GetEdgeData(compare.eid).road_classification;
return compare.eid == base.eid || return compare.eid == base.eid ||
@ -632,12 +650,12 @@ boost::optional<TurnHandler::Fork> TurnHandler::findFork(const EdgeID via_edge,
{ {
// makes sure that the fork is isolated from other neighbouring streets on the left and // makes sure that the fork is isolated from other neighbouring streets on the left and
// right side // right side
const auto next = const auto next = fork->end == intersection.end() ? intersection.begin() : (fork->end);
(fork->left + 1) == intersection.end() ? intersection.begin() : (fork->left + 1);
const bool separated_at_left_side = const bool separated_at_left_side =
angularDeviation(fork->left->angle, next->angle) >= GROUP_ANGLE; angularDeviation(fork->getLeft().angle, next->angle) >= GROUP_ANGLE;
BOOST_ASSERT((fork->begin - 1) >= intersection.begin());
const bool separated_at_right_side = const bool separated_at_right_side =
angularDeviation(fork->right->angle, (fork->right - 1)->angle) >= GROUP_ANGLE; angularDeviation(fork->getRight().angle, (fork->begin - 1)->angle) >= GROUP_ANGLE;
// check whether there is an obvious turn to take; forks are never obvious - if there is an // check whether there is an obvious turn to take; forks are never obvious - if there is an
// obvious turn, it's not a fork // obvious turn, it's not a fork
@ -647,11 +665,10 @@ boost::optional<TurnHandler::Fork> TurnHandler::findFork(const EdgeID via_edge,
const bool has_compatible_classes = isCompatibleByRoadClass(intersection, *fork); const bool has_compatible_classes = isCompatibleByRoadClass(intersection, *fork);
// check if all entries in the fork range allow entry // check if all entries in the fork range allow entry
const bool only_valid_entries = const bool only_valid_entries = intersection.hasAllValidEntries(fork->begin, fork->end);
intersection.hasAllValidEntries(fork->right, fork->left + 1);
const auto has_compatible_modes = const auto has_compatible_modes =
std::all_of(fork->right, fork->left + 1, [&](const auto &road) { std::all_of(fork->begin, fork->end, [&](const auto &road) {
return node_based_graph.GetEdgeData(road.eid).travel_mode == return node_based_graph.GetEdgeData(road.eid).travel_mode ==
node_based_graph.GetEdgeData(via_edge).travel_mode; node_based_graph.GetEdgeData(via_edge).travel_mode;
}); });