2016-12-07 14:26:34 -05:00
|
|
|
|
#include "extractor/guidance/turn_handler.hpp"
|
2017-01-03 16:11:59 -05:00
|
|
|
|
#include "extractor/guidance/constants.hpp"
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
2016-12-02 04:53:22 -05:00
|
|
|
|
#include "util/bearing.hpp"
|
|
|
|
|
#include "util/guidance/name_announcements.hpp"
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
2016-09-22 10:42:38 -04:00
|
|
|
|
#include <algorithm>
|
2016-04-08 06:49:14 -04:00
|
|
|
|
#include <limits>
|
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
|
|
#include <boost/assert.hpp>
|
2017-01-07 08:13:32 -05:00
|
|
|
|
#include <boost/optional.hpp>
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
2016-12-02 04:53:22 -05:00
|
|
|
|
using osrm::extractor::guidance::getTurnDirection;
|
|
|
|
|
using osrm::util::angularDeviation;
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
2016-11-09 04:34:47 -05:00
|
|
|
|
namespace
|
|
|
|
|
{
|
2017-01-07 08:13:32 -05:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-09 04:34:47 -05:00
|
|
|
|
bool isEndOfRoad(const ConnectedRoad &,
|
|
|
|
|
const ConnectedRoad &possible_right_turn,
|
|
|
|
|
const ConnectedRoad &possible_left_turn)
|
|
|
|
|
{
|
|
|
|
|
return angularDeviation(possible_right_turn.angle, 90) < NARROW_TURN_ANGLE &&
|
|
|
|
|
angularDeviation(possible_left_turn.angle, 270) < NARROW_TURN_ANGLE &&
|
|
|
|
|
angularDeviation(possible_right_turn.angle, possible_left_turn.angle) >
|
|
|
|
|
2 * NARROW_TURN_ANGLE;
|
|
|
|
|
}
|
2017-01-07 08:13:32 -05:00
|
|
|
|
|
|
|
|
|
template <typename InputIt>
|
2017-01-15 10:09:33 -05:00
|
|
|
|
InputIt findOutermostForkCandidate(const InputIt begin, const InputIt end)
|
2017-01-07 08:13:32 -05:00
|
|
|
|
{
|
|
|
|
|
static_assert(std::is_base_of<std::input_iterator_tag,
|
|
|
|
|
typename std::iterator_traits<InputIt>::iterator_category>::value,
|
|
|
|
|
"findOutermostForkCandidate() only accepts input iterators");
|
2017-01-15 10:09:33 -05:00
|
|
|
|
const auto outermost = std::adjacent_find(begin, end, isOutermostForkCandidate);
|
2017-01-07 08:13:32 -05:00
|
|
|
|
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
|
2017-01-15 10:09:33 -05:00
|
|
|
|
TurnHandler::Fork::Fork(const Intersection::iterator intersection_base,
|
|
|
|
|
const Intersection::iterator begin,
|
|
|
|
|
const Intersection::iterator end)
|
|
|
|
|
: intersection_base(intersection_base), begin(begin), end(end), size(std::distance(begin, end))
|
2017-01-07 08:13:32 -05:00
|
|
|
|
{
|
2017-01-15 10:09:33 -05:00
|
|
|
|
BOOST_ASSERT(begin < end);
|
|
|
|
|
BOOST_ASSERT(size == 2 || 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;
|
2016-11-09 04:34:47 -05:00
|
|
|
|
}
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
|
|
|
|
TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
2017-09-25 09:37:11 -04:00
|
|
|
|
const EdgeBasedNodeDataContainer &node_data_container,
|
2017-04-02 19:58:06 -04:00
|
|
|
|
const std::vector<util::Coordinate> &coordinates,
|
2016-04-22 05:31:46 -04:00
|
|
|
|
const util::NameTable &name_table,
|
2016-08-15 06:43:26 -04:00
|
|
|
|
const SuffixTable &street_name_suffix_table,
|
|
|
|
|
const IntersectionGenerator &intersection_generator)
|
|
|
|
|
: IntersectionHandler(node_based_graph,
|
2017-09-25 09:37:11 -04:00
|
|
|
|
node_data_container,
|
2017-04-02 19:58:06 -04:00
|
|
|
|
coordinates,
|
2016-08-15 06:43:26 -04:00
|
|
|
|
name_table,
|
|
|
|
|
street_name_suffix_table,
|
|
|
|
|
intersection_generator)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool TurnHandler::canProcess(const NodeID, const EdgeID, const Intersection &) const
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// Handles and processes possible turns
|
|
|
|
|
// Input parameters describe an intersection as described in
|
|
|
|
|
// #IntersectionExplanation@intersection_handler.hpp
|
2016-04-08 06:49:14 -04:00
|
|
|
|
Intersection TurnHandler::
|
2016-07-04 06:19:49 -04:00
|
|
|
|
operator()(const NodeID, const EdgeID via_edge, Intersection intersection) const
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
if (intersection.size() == 1)
|
|
|
|
|
return handleOneWayTurn(std::move(intersection));
|
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// if u-turn is allowed, set the turn type of intersection[0] to its basic type and u-turn
|
2016-04-08 06:49:14 -04:00
|
|
|
|
if (intersection[0].entry_allowed)
|
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[0].instruction = {findBasicTurnType(via_edge, intersection[0]),
|
|
|
|
|
DirectionModifier::UTurn};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (intersection.size() == 2)
|
2016-07-04 06:19:49 -04:00
|
|
|
|
return handleTwoWayTurn(via_edge, std::move(intersection));
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
|
|
|
|
if (intersection.size() == 3)
|
2016-07-04 06:19:49 -04:00
|
|
|
|
return handleThreeWayTurn(via_edge, std::move(intersection));
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
|
return handleComplexTurn(via_edge, std::move(intersection));
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Intersection TurnHandler::handleOneWayTurn(Intersection intersection) const
|
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
BOOST_ASSERT(intersection[0].angle < 0.001);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
return intersection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Intersection TurnHandler::handleTwoWayTurn(const EdgeID via_edge, Intersection intersection) const
|
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
BOOST_ASSERT(intersection[0].angle < 0.001);
|
|
|
|
|
intersection[1].instruction =
|
2016-04-08 06:49:14 -04:00
|
|
|
|
getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]);
|
|
|
|
|
|
|
|
|
|
return intersection;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// checks whether it is obvious to turn on `road` coming from `via_edge` while there is an`other`
|
|
|
|
|
// road at the same intersection
|
2016-07-04 06:19:49 -04:00
|
|
|
|
bool TurnHandler::isObviousOfTwo(const EdgeID via_edge,
|
|
|
|
|
const ConnectedRoad &road,
|
|
|
|
|
const ConnectedRoad &other) const
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2017-09-25 09:37:11 -04:00
|
|
|
|
const auto &via_data =
|
|
|
|
|
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(via_edge).annotation_data);
|
|
|
|
|
const auto &road_data =
|
|
|
|
|
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data);
|
|
|
|
|
const auto &via_classification =
|
|
|
|
|
node_based_graph.GetEdgeData(via_edge).flags.road_classification;
|
|
|
|
|
const auto &road_classification =
|
|
|
|
|
node_based_graph.GetEdgeData(road.eid).flags.road_classification;
|
|
|
|
|
const auto &other_classification =
|
|
|
|
|
node_based_graph.GetEdgeData(other.eid).flags.road_classification;
|
2016-04-11 06:51:06 -04:00
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// if one of the given roads is obvious by class, obviousness is trivial
|
|
|
|
|
if (obviousByRoadClass(via_classification, road_classification, other_classification))
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
else if (obviousByRoadClass(via_classification, other_classification, road_classification))
|
|
|
|
|
{
|
2016-07-04 06:19:49 -04:00
|
|
|
|
return false;
|
2017-01-07 08:13:32 -05:00
|
|
|
|
}
|
2016-04-11 06:51:06 -04:00
|
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
|
const bool turn_is_perfectly_straight =
|
2016-11-03 05:18:27 -04:00
|
|
|
|
angularDeviation(road.angle, STRAIGHT_ANGLE) < std::numeric_limits<double>::epsilon();
|
2017-11-28 04:34:46 -05:00
|
|
|
|
const auto &via_name = name_table.GetNameForID(via_data.name_id).to_string();
|
|
|
|
|
if (!via_name.empty())
|
2017-01-07 08:13:32 -05:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-11 06:51:06 -04:00
|
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
|
const bool is_much_narrower_than_other =
|
2016-11-03 05:18:27 -04:00
|
|
|
|
angularDeviation(other.angle, STRAIGHT_ANGLE) /
|
|
|
|
|
angularDeviation(road.angle, STRAIGHT_ANGLE) >
|
2016-07-04 06:19:49 -04:00
|
|
|
|
INCREASES_BY_FOURTY_PERCENT &&
|
2016-11-03 05:18:27 -04:00
|
|
|
|
angularDeviation(angularDeviation(other.angle, STRAIGHT_ANGLE),
|
|
|
|
|
angularDeviation(road.angle, STRAIGHT_ANGLE)) > FUZZY_ANGLE_DIFFERENCE;
|
2016-06-13 08:33:36 -04:00
|
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
|
return is_much_narrower_than_other;
|
2016-06-24 10:06:45 -04:00
|
|
|
|
}
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
|
bool TurnHandler::hasObvious(const EdgeID &via_edge, const Fork &fork) const
|
|
|
|
|
{
|
2017-01-15 10:09:33 -05:00
|
|
|
|
auto obvious_road =
|
|
|
|
|
std::adjacent_find(fork.begin, fork.end, [&, this](const auto &a, const auto &b) {
|
|
|
|
|
return this->isObviousOfTwo(via_edge, a, b) || this->isObviousOfTwo(via_edge, b, a);
|
|
|
|
|
});
|
|
|
|
|
// return whether an obvious road was found
|
|
|
|
|
return obvious_road != fork.end;
|
2017-01-07 08:13:32 -05:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// handles a turn at a three-way intersection _coming from_ `via_edge`
|
|
|
|
|
// with `intersection` as described as in #IntersectionExplanation@intersection_handler.hpp
|
2016-07-04 06:19:49 -04:00
|
|
|
|
Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const
|
|
|
|
|
{
|
2017-01-07 08:13:32 -05:00
|
|
|
|
BOOST_ASSERT(intersection.size() == 3);
|
2016-08-08 05:07:27 -04:00
|
|
|
|
const auto obvious_index = findObviousTurn(via_edge, intersection);
|
2016-11-03 05:18:27 -04:00
|
|
|
|
BOOST_ASSERT(intersection[0].angle < 0.001);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
/* Two nearly straight turns -> FORK
|
|
|
|
|
OOOOOOO
|
|
|
|
|
/
|
|
|
|
|
IIIIII
|
|
|
|
|
\
|
|
|
|
|
OOOOOOO
|
|
|
|
|
*/
|
2016-12-07 14:26:34 -05:00
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
|
auto fork = findFork(via_edge, intersection);
|
|
|
|
|
if (fork && obvious_index == 0)
|
|
|
|
|
{
|
2017-01-15 10:09:33 -05:00
|
|
|
|
assignFork(via_edge, fork->getLeft(), fork->getRight());
|
2017-01-07 08:13:32 -05:00
|
|
|
|
}
|
2016-07-04 06:19:49 -04:00
|
|
|
|
|
2016-04-08 06:49:14 -04:00
|
|
|
|
/* T Intersection
|
|
|
|
|
|
|
|
|
|
OOOOOOO T OOOOOOOO
|
|
|
|
|
I
|
|
|
|
|
I
|
|
|
|
|
I
|
|
|
|
|
*/
|
2016-08-08 05:07:27 -04:00
|
|
|
|
else if (isEndOfRoad(intersection[0], intersection[1], intersection[2]) && obvious_index == 0)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
if (intersection[1].entry_allowed)
|
|
|
|
|
{
|
2016-05-03 07:37:41 -04:00
|
|
|
|
if (TurnType::OnRamp != findBasicTurnType(via_edge, intersection[1]))
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[1].instruction = {TurnType::EndOfRoad, DirectionModifier::Right};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
else
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[1].instruction = {TurnType::OnRamp, DirectionModifier::Right};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
if (intersection[2].entry_allowed)
|
|
|
|
|
{
|
2016-05-03 07:37:41 -04:00
|
|
|
|
if (TurnType::OnRamp != findBasicTurnType(via_edge, intersection[2]))
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[2].instruction = {TurnType::EndOfRoad, DirectionModifier::Left};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
else
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[2].instruction = {TurnType::OnRamp, DirectionModifier::Left};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-07 03:34:26 -04:00
|
|
|
|
else if (obvious_index != 0) // has an obvious continuing road/obvious turn
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
const auto direction_at_one = getTurnDirection(intersection[1].angle);
|
|
|
|
|
const auto direction_at_two = getTurnDirection(intersection[2].angle);
|
2016-08-10 08:35:02 -04:00
|
|
|
|
if (obvious_index == 1)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[1].instruction = getInstructionForObvious(
|
2016-04-11 06:51:06 -04:00
|
|
|
|
3, via_edge, isThroughStreet(1, intersection), intersection[1]);
|
2016-10-07 03:34:26 -04:00
|
|
|
|
const auto second_direction = (direction_at_one == direction_at_two &&
|
|
|
|
|
direction_at_two == DirectionModifier::Straight)
|
|
|
|
|
? DirectionModifier::SlightLeft
|
|
|
|
|
: direction_at_two;
|
|
|
|
|
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[2].instruction = {findBasicTurnType(via_edge, intersection[2]),
|
|
|
|
|
second_direction};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-10-07 03:34:26 -04:00
|
|
|
|
BOOST_ASSERT(obvious_index == 2);
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[2].instruction = getInstructionForObvious(
|
2016-04-11 06:51:06 -04:00
|
|
|
|
3, via_edge, isThroughStreet(2, intersection), intersection[2]);
|
2016-10-07 03:34:26 -04:00
|
|
|
|
const auto first_direction = (direction_at_one == direction_at_two &&
|
|
|
|
|
direction_at_one == DirectionModifier::Straight)
|
|
|
|
|
? DirectionModifier::SlightRight
|
|
|
|
|
: direction_at_one;
|
|
|
|
|
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[1].instruction = {findBasicTurnType(via_edge, intersection[1]),
|
|
|
|
|
first_direction};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
2016-10-07 03:34:26 -04:00
|
|
|
|
}
|
|
|
|
|
else // basic turn assignment
|
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[1].instruction = {findBasicTurnType(via_edge, intersection[1]),
|
|
|
|
|
getTurnDirection(intersection[1].angle)};
|
|
|
|
|
intersection[2].instruction = {findBasicTurnType(via_edge, intersection[2]),
|
|
|
|
|
getTurnDirection(intersection[2].angle)};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
return intersection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection intersection) const
|
|
|
|
|
{
|
|
|
|
|
const std::size_t obvious_index = findObviousTurn(via_edge, intersection);
|
2017-01-07 08:13:32 -05:00
|
|
|
|
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);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
|
|
|
|
// check whether the obvious choice is actually a through street
|
|
|
|
|
if (obvious_index != 0)
|
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[obvious_index].instruction =
|
2016-05-27 15:05:04 -04:00
|
|
|
|
getInstructionForObvious(intersection.size(),
|
|
|
|
|
via_edge,
|
|
|
|
|
isThroughStreet(obvious_index, intersection),
|
|
|
|
|
intersection[obvious_index]);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
|
|
|
|
// assign left/right turns
|
2017-01-15 10:09:33 -05:00
|
|
|
|
intersection = assignLeftTurns(via_edge, std::move(intersection), obvious_index);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
intersection = assignRightTurns(via_edge, std::move(intersection), obvious_index);
|
|
|
|
|
}
|
2017-01-07 08:13:32 -05:00
|
|
|
|
else if (fork) // found fork
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2017-01-07 08:13:32 -05:00
|
|
|
|
if (fork->size == 2)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2016-06-24 10:06:45 -04:00
|
|
|
|
const auto left_classification =
|
2017-09-25 09:37:11 -04:00
|
|
|
|
node_based_graph.GetEdgeData(fork->getLeft().eid).flags.road_classification;
|
2016-06-24 10:06:45 -04:00
|
|
|
|
const auto right_classification =
|
2017-09-25 09:37:11 -04:00
|
|
|
|
node_based_graph.GetEdgeData(fork->getRight().eid).flags.road_classification;
|
2016-06-24 10:06:45 -04:00
|
|
|
|
if (canBeSeenAsFork(left_classification, right_classification))
|
2017-01-07 08:13:32 -05:00
|
|
|
|
{
|
2017-01-15 10:09:33 -05:00
|
|
|
|
assignFork(via_edge, fork->getLeft(), fork->getRight());
|
2017-01-07 08:13:32 -05:00
|
|
|
|
}
|
2016-06-24 10:06:45 -04:00
|
|
|
|
else if (left_classification.GetPriority() > right_classification.GetPriority())
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2017-01-15 10:09:33 -05:00
|
|
|
|
fork->getRight().instruction = getInstructionForObvious(
|
|
|
|
|
intersection.size(), via_edge, false, fork->getRight());
|
|
|
|
|
fork->getLeft().instruction = {findBasicTurnType(via_edge, fork->getLeft()),
|
|
|
|
|
DirectionModifier::SlightLeft};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-01-15 10:09:33 -05:00
|
|
|
|
fork->getLeft().instruction =
|
|
|
|
|
getInstructionForObvious(intersection.size(), via_edge, false, fork->getLeft());
|
|
|
|
|
fork->getRight().instruction = {findBasicTurnType(via_edge, fork->getRight()),
|
|
|
|
|
DirectionModifier::SlightRight};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-07 08:13:32 -05:00
|
|
|
|
else if (fork->size == 3)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2016-05-27 15:05:04 -04:00
|
|
|
|
assignFork(via_edge,
|
2017-01-15 10:09:33 -05:00
|
|
|
|
fork->getLeft(),
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// middle fork road
|
2017-01-15 10:09:33 -05:00
|
|
|
|
fork->getMiddle(),
|
|
|
|
|
fork->getRight());
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
2017-01-07 08:13:32 -05:00
|
|
|
|
|
2017-01-15 10:09:33 -05:00
|
|
|
|
intersection = assignLeftTurns(via_edge, std::move(intersection), fork->getLeftIndex());
|
|
|
|
|
intersection = assignRightTurns(via_edge, std::move(intersection), fork->getRightIndex());
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
2017-01-07 08:13:32 -05:00
|
|
|
|
else if (straightmost_angle_dev < FUZZY_ANGLE_DIFFERENCE && !straightmost->entry_allowed)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
// invalid straight turn
|
2017-01-15 10:09:33 -05:00
|
|
|
|
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index);
|
2017-01-07 08:13:32 -05:00
|
|
|
|
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
// no straight turn
|
2017-01-07 08:13:32 -05:00
|
|
|
|
else if (straightmost->angle > 180)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
// at most three turns on either side
|
2017-01-15 10:09:33 -05:00
|
|
|
|
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index - 1);
|
2017-01-07 08:13:32 -05:00
|
|
|
|
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
2017-01-07 08:13:32 -05:00
|
|
|
|
else if (straightmost->angle < 180)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2017-01-15 10:09:33 -05:00
|
|
|
|
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_index);
|
2017-01-07 08:13:32 -05:00
|
|
|
|
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_index + 1);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-05-11 10:15:34 -04:00
|
|
|
|
assignTrivialTurns(via_edge, intersection, 1, intersection.size());
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
2017-08-07 08:10:06 -04:00
|
|
|
|
|
2016-04-08 06:49:14 -04:00
|
|
|
|
return intersection;
|
|
|
|
|
}
|
|
|
|
|
|
2016-04-11 06:51:06 -04:00
|
|
|
|
// Assignment of left turns hands of to right turns.
|
|
|
|
|
// To do so, we mirror every road segment and reverse the order.
|
|
|
|
|
// After the mirror and reversal / we assign right turns and
|
|
|
|
|
// mirror again and restore the original order.
|
2016-04-08 06:49:14 -04:00
|
|
|
|
Intersection TurnHandler::assignLeftTurns(const EdgeID via_edge,
|
|
|
|
|
Intersection intersection,
|
|
|
|
|
const std::size_t starting_at) const
|
|
|
|
|
{
|
2017-01-15 10:09:33 -05:00
|
|
|
|
BOOST_ASSERT(starting_at < intersection.size());
|
2016-05-11 10:15:34 -04:00
|
|
|
|
const auto switch_left_and_right = [](Intersection &intersection) {
|
2016-04-22 05:31:46 -04:00
|
|
|
|
BOOST_ASSERT(!intersection.empty());
|
|
|
|
|
|
|
|
|
|
for (auto &road : intersection)
|
2016-11-03 05:18:27 -04:00
|
|
|
|
road.mirror();
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
2016-04-22 05:31:46 -04:00
|
|
|
|
std::reverse(intersection.begin() + 1, intersection.end());
|
|
|
|
|
};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
2016-04-22 05:31:46 -04:00
|
|
|
|
switch_left_and_right(intersection);
|
2016-04-11 06:51:06 -04:00
|
|
|
|
// account for the u-turn in the beginning
|
2017-01-15 10:09:33 -05:00
|
|
|
|
const auto count = intersection.size() - starting_at;
|
2016-04-11 06:51:06 -04:00
|
|
|
|
intersection = assignRightTurns(via_edge, std::move(intersection), count);
|
2016-04-22 05:31:46 -04:00
|
|
|
|
switch_left_and_right(intersection);
|
2016-04-11 06:51:06 -04:00
|
|
|
|
|
2016-04-08 06:49:14 -04:00
|
|
|
|
return intersection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// can only assign three turns
|
|
|
|
|
Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
|
|
|
|
|
Intersection intersection,
|
|
|
|
|
const std::size_t up_to) const
|
|
|
|
|
{
|
|
|
|
|
BOOST_ASSERT(up_to <= intersection.size());
|
|
|
|
|
const auto count_valid = [&intersection, up_to]() {
|
|
|
|
|
std::size_t count = 0;
|
|
|
|
|
for (std::size_t i = 1; i < up_to; ++i)
|
|
|
|
|
if (intersection[i].entry_allowed)
|
|
|
|
|
++count;
|
|
|
|
|
return count;
|
|
|
|
|
};
|
|
|
|
|
if (up_to <= 1 || count_valid() == 0)
|
|
|
|
|
return intersection;
|
|
|
|
|
// handle single turn
|
|
|
|
|
if (up_to == 2)
|
|
|
|
|
{
|
2016-04-11 06:51:06 -04:00
|
|
|
|
assignTrivialTurns(via_edge, intersection, 1, up_to);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
2016-04-11 06:51:06 -04:00
|
|
|
|
// Handle Turns 1-3
|
2016-04-08 06:49:14 -04:00
|
|
|
|
else if (up_to == 3)
|
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
const auto first_direction = getTurnDirection(intersection[1].angle);
|
|
|
|
|
const auto second_direction = getTurnDirection(intersection[2].angle);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
if (first_direction == second_direction)
|
|
|
|
|
{
|
|
|
|
|
// conflict
|
|
|
|
|
handleDistinctConflict(via_edge, intersection[2], intersection[1]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-04-11 06:51:06 -04:00
|
|
|
|
assignTrivialTurns(via_edge, intersection, 1, up_to);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-11 06:51:06 -04:00
|
|
|
|
// Handle Turns 1-4
|
2016-04-08 06:49:14 -04:00
|
|
|
|
else if (up_to == 4)
|
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
const auto first_direction = getTurnDirection(intersection[1].angle);
|
|
|
|
|
const auto second_direction = getTurnDirection(intersection[2].angle);
|
|
|
|
|
const auto third_direction = getTurnDirection(intersection[3].angle);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
if (first_direction != second_direction && second_direction != third_direction)
|
|
|
|
|
{
|
2016-04-11 06:51:06 -04:00
|
|
|
|
// due to the circular order, the turn directions are unique
|
|
|
|
|
// first_direction != third_direction is implied
|
|
|
|
|
BOOST_ASSERT(first_direction != third_direction);
|
|
|
|
|
assignTrivialTurns(via_edge, intersection, 1, up_to);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
else if (2 >= (intersection[1].entry_allowed + intersection[2].entry_allowed +
|
|
|
|
|
intersection[3].entry_allowed))
|
|
|
|
|
{
|
|
|
|
|
// at least a single invalid
|
|
|
|
|
if (!intersection[3].entry_allowed)
|
|
|
|
|
{
|
|
|
|
|
handleDistinctConflict(via_edge, intersection[2], intersection[1]);
|
|
|
|
|
}
|
|
|
|
|
else if (!intersection[1].entry_allowed)
|
|
|
|
|
{
|
|
|
|
|
handleDistinctConflict(via_edge, intersection[3], intersection[2]);
|
|
|
|
|
}
|
|
|
|
|
else // handles one-valid as well as two valid (1,3)
|
|
|
|
|
{
|
|
|
|
|
handleDistinctConflict(via_edge, intersection[3], intersection[1]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-04-11 06:51:06 -04:00
|
|
|
|
// From here on out, intersection[1-3].entry_allowed has to be true (Otherwise we would have
|
|
|
|
|
// triggered 2>= ...)
|
|
|
|
|
//
|
|
|
|
|
// Conflicting Turns, but at least farther than what we call a narrow turn
|
2016-11-03 05:18:27 -04:00
|
|
|
|
else if (angularDeviation(intersection[1].angle, intersection[2].angle) >=
|
2016-04-08 06:49:14 -04:00
|
|
|
|
NARROW_TURN_ANGLE &&
|
2016-11-03 05:18:27 -04:00
|
|
|
|
angularDeviation(intersection[2].angle, intersection[3].angle) >=
|
2016-04-08 06:49:14 -04:00
|
|
|
|
NARROW_TURN_ANGLE)
|
|
|
|
|
{
|
2016-04-11 06:51:06 -04:00
|
|
|
|
BOOST_ASSERT(intersection[1].entry_allowed && intersection[2].entry_allowed &&
|
|
|
|
|
intersection[3].entry_allowed);
|
|
|
|
|
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[1].instruction = {findBasicTurnType(via_edge, intersection[1]),
|
|
|
|
|
DirectionModifier::SharpRight};
|
|
|
|
|
intersection[2].instruction = {findBasicTurnType(via_edge, intersection[2]),
|
|
|
|
|
DirectionModifier::Right};
|
|
|
|
|
intersection[3].instruction = {findBasicTurnType(via_edge, intersection[3]),
|
|
|
|
|
DirectionModifier::SlightRight};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
2016-04-11 06:51:06 -04:00
|
|
|
|
else if (((first_direction == second_direction && second_direction == third_direction) ||
|
2016-04-08 06:49:14 -04:00
|
|
|
|
(first_direction == second_direction &&
|
2016-11-03 05:18:27 -04:00
|
|
|
|
angularDeviation(intersection[2].angle, intersection[3].angle) < GROUP_ANGLE) ||
|
2016-04-08 06:49:14 -04:00
|
|
|
|
(second_direction == third_direction &&
|
2016-11-03 05:18:27 -04:00
|
|
|
|
angularDeviation(intersection[1].angle, intersection[2].angle) < GROUP_ANGLE)))
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2016-04-11 06:51:06 -04:00
|
|
|
|
BOOST_ASSERT(intersection[1].entry_allowed && intersection[2].entry_allowed &&
|
|
|
|
|
intersection[3].entry_allowed);
|
|
|
|
|
// count backwards from the slightest turn
|
2016-04-18 08:57:12 -04:00
|
|
|
|
assignTrivialTurns(via_edge, intersection, 1, up_to);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
2016-04-11 06:51:06 -04:00
|
|
|
|
else if (((first_direction == second_direction &&
|
2016-11-03 05:18:27 -04:00
|
|
|
|
angularDeviation(intersection[2].angle, intersection[3].angle) >= GROUP_ANGLE) ||
|
2016-04-08 06:49:14 -04:00
|
|
|
|
(second_direction == third_direction &&
|
2016-11-03 05:18:27 -04:00
|
|
|
|
angularDeviation(intersection[1].angle, intersection[2].angle) >= GROUP_ANGLE)))
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2016-04-11 06:51:06 -04:00
|
|
|
|
BOOST_ASSERT(intersection[1].entry_allowed && intersection[2].entry_allowed &&
|
|
|
|
|
intersection[3].entry_allowed);
|
|
|
|
|
|
2016-11-03 05:18:27 -04:00
|
|
|
|
if (angularDeviation(intersection[2].angle, intersection[3].angle) >= GROUP_ANGLE)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
handleDistinctConflict(via_edge, intersection[2], intersection[1]);
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[3].instruction = {findBasicTurnType(via_edge, intersection[3]),
|
|
|
|
|
third_direction};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
intersection[1].instruction = {findBasicTurnType(via_edge, intersection[1]),
|
|
|
|
|
first_direction};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
handleDistinctConflict(via_edge, intersection[3], intersection[2]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-04-11 06:51:06 -04:00
|
|
|
|
assignTrivialTurns(via_edge, intersection, 1, up_to);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-04-11 06:51:06 -04:00
|
|
|
|
assignTrivialTurns(via_edge, intersection, 1, up_to);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
return intersection;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// finds a fork candidate by just looking at the geometry and angle of an intersection
|
|
|
|
|
boost::optional<TurnHandler::Fork>
|
|
|
|
|
TurnHandler::findForkCandidatesByGeometry(Intersection &intersection) const
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2017-01-07 08:13:32 -05:00
|
|
|
|
if (intersection.size() >= 3)
|
|
|
|
|
{
|
|
|
|
|
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);
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// 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
|
2017-01-19 09:14:30 -05:00
|
|
|
|
// / \
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// /____ 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
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
|
if (straightmost_angle_dev <= NARROW_TURN_ANGLE)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// find the rightmost road that might be part of a fork
|
|
|
|
|
const auto right = findOutermostForkCandidate(
|
|
|
|
|
intersection.rend() - straightmost_index - 1, intersection.rend());
|
2017-01-15 10:09:33 -05:00
|
|
|
|
const std::size_t right_index = intersection.rend() - right - 1;
|
2017-01-07 08:13:32 -05:00
|
|
|
|
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
|
2017-01-15 10:09:33 -05:00
|
|
|
|
if (forward_right < left && left - forward_right < 3)
|
2017-01-07 08:13:32 -05:00
|
|
|
|
{
|
2017-01-15 10:09:33 -05:00
|
|
|
|
return Fork(intersection.begin(), forward_right, left + 1);
|
2017-01-07 08:13:32 -05:00
|
|
|
|
}
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
2017-01-07 08:13:32 -05:00
|
|
|
|
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
|
|
|
|
|
{
|
2017-09-25 09:37:11 -04:00
|
|
|
|
const auto via_class =
|
|
|
|
|
node_based_graph.GetEdgeData(intersection[0].eid).flags.road_classification;
|
2017-01-07 08:13:32 -05:00
|
|
|
|
|
|
|
|
|
// 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 =
|
2017-09-25 09:37:11 -04:00
|
|
|
|
node_based_graph.GetEdgeData(fork.getRight().eid).flags.road_classification.IsLinkClass();
|
2017-01-15 10:09:33 -05:00
|
|
|
|
if (!std::all_of(fork.begin + 1, fork.end, [&](ConnectedRoad &road) {
|
2017-01-07 08:13:32 -05:00
|
|
|
|
return is_right_link_class ==
|
2017-09-25 09:37:11 -04:00
|
|
|
|
node_based_graph.GetEdgeData(road.eid).flags.road_classification.IsLinkClass();
|
2017-01-07 08:13:32 -05:00
|
|
|
|
}))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2017-01-15 10:09:33 -05:00
|
|
|
|
return std::all_of(fork.begin, fork.end, [&](ConnectedRoad &base) {
|
2017-09-25 09:37:11 -04:00
|
|
|
|
const auto base_class = node_based_graph.GetEdgeData(base.eid).flags.road_classification;
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// check that there is no turn obvious == check that all turns are non-onvious
|
2017-01-15 10:09:33 -05:00
|
|
|
|
return std::all_of(fork.begin, fork.end, [&](ConnectedRoad &compare) {
|
2017-01-07 08:13:32 -05:00
|
|
|
|
const auto compare_class =
|
2017-09-25 09:37:11 -04:00
|
|
|
|
node_based_graph.GetEdgeData(compare.eid).flags.road_classification;
|
2017-01-07 08:13:32 -05:00
|
|
|
|
return compare.eid == base.eid ||
|
|
|
|
|
!(obviousByRoadClass(via_class, base_class, compare_class));
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// makes sure that the fork is isolated from other neighbouring streets on the left and
|
|
|
|
|
// right side
|
2017-01-15 10:09:33 -05:00
|
|
|
|
const auto next = fork->end == intersection.end() ? intersection.begin() : (fork->end);
|
2016-07-04 06:19:49 -04:00
|
|
|
|
const bool separated_at_left_side =
|
2017-01-15 10:09:33 -05:00
|
|
|
|
angularDeviation(fork->getLeft().angle, next->angle) >= GROUP_ANGLE;
|
|
|
|
|
BOOST_ASSERT((fork->begin - 1) >= intersection.begin());
|
2016-07-04 06:19:49 -04:00
|
|
|
|
const bool separated_at_right_side =
|
2017-01-15 10:09:33 -05:00
|
|
|
|
angularDeviation(fork->getRight().angle, (fork->begin - 1)->angle) >= GROUP_ANGLE;
|
2016-07-04 06:19:49 -04:00
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
|
// 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);
|
2016-07-04 06:19:49 -04:00
|
|
|
|
|
2016-08-17 03:49:19 -04:00
|
|
|
|
// A fork can only happen between edges of similar types where none of the ones is obvious
|
2017-01-07 08:13:32 -05:00
|
|
|
|
const bool has_compatible_classes = isCompatibleByRoadClass(intersection, *fork);
|
2016-07-04 06:19:49 -04:00
|
|
|
|
|
2016-10-04 12:36:39 -04:00
|
|
|
|
// check if all entries in the fork range allow entry
|
2017-01-15 10:09:33 -05:00
|
|
|
|
const bool only_valid_entries = intersection.hasAllValidEntries(fork->begin, fork->end);
|
2017-01-07 08:13:32 -05:00
|
|
|
|
|
2017-01-11 05:09:53 -05:00
|
|
|
|
const auto has_compatible_modes =
|
2017-01-15 10:09:33 -05:00
|
|
|
|
std::all_of(fork->begin, fork->end, [&](const auto &road) {
|
2017-09-25 09:37:11 -04:00
|
|
|
|
return node_data_container
|
|
|
|
|
.GetAnnotation(node_based_graph.GetEdgeData(road.eid).annotation_data)
|
|
|
|
|
.travel_mode ==
|
|
|
|
|
node_data_container
|
|
|
|
|
.GetAnnotation(node_based_graph.GetEdgeData(via_edge).annotation_data)
|
|
|
|
|
.travel_mode;
|
2017-01-11 05:09:53 -05:00
|
|
|
|
});
|
|
|
|
|
|
2017-01-07 08:13:32 -05:00
|
|
|
|
if (separated_at_left_side && separated_at_right_side && !has_obvious &&
|
2017-01-11 05:09:53 -05:00
|
|
|
|
has_compatible_classes && only_valid_entries && has_compatible_modes)
|
2017-01-07 08:13:32 -05:00
|
|
|
|
{
|
|
|
|
|
return fork;
|
|
|
|
|
}
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
2017-01-07 08:13:32 -05:00
|
|
|
|
|
|
|
|
|
return boost::none;
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void TurnHandler::handleDistinctConflict(const EdgeID via_edge,
|
|
|
|
|
ConnectedRoad &left,
|
|
|
|
|
ConnectedRoad &right) const
|
|
|
|
|
{
|
|
|
|
|
// single turn of both is valid (don't change the valid one)
|
|
|
|
|
// or multiple identical angles -> bad OSM intersection
|
2016-11-03 05:18:27 -04:00
|
|
|
|
if ((!left.entry_allowed || !right.entry_allowed) || (left.angle == right.angle))
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
if (left.entry_allowed)
|
2016-11-03 05:18:27 -04:00
|
|
|
|
left.instruction = {findBasicTurnType(via_edge, left), getTurnDirection(left.angle)};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
if (right.entry_allowed)
|
2016-11-03 05:18:27 -04:00
|
|
|
|
right.instruction = {findBasicTurnType(via_edge, right), getTurnDirection(right.angle)};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2016-11-03 05:18:27 -04:00
|
|
|
|
if (getTurnDirection(left.angle) == DirectionModifier::Straight ||
|
|
|
|
|
getTurnDirection(left.angle) == DirectionModifier::SlightLeft ||
|
|
|
|
|
getTurnDirection(right.angle) == DirectionModifier::SlightRight)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2017-09-25 09:37:11 -04:00
|
|
|
|
const auto left_classification =
|
|
|
|
|
node_based_graph.GetEdgeData(left.eid).flags.road_classification;
|
2016-06-24 10:06:45 -04:00
|
|
|
|
const auto right_classification =
|
2017-09-25 09:37:11 -04:00
|
|
|
|
node_based_graph.GetEdgeData(right.eid).flags.road_classification;
|
2017-05-23 05:59:08 -04:00
|
|
|
|
|
|
|
|
|
if (left_classification.GetPriority() > right_classification.GetPriority())
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
// FIXME this should possibly know about the actual roads?
|
|
|
|
|
// here we don't know about the intersection size. To be on the save side,
|
|
|
|
|
// we declare it
|
|
|
|
|
// as complex (at least size 4)
|
2016-11-03 05:18:27 -04:00
|
|
|
|
right.instruction = getInstructionForObvious(4, via_edge, false, right);
|
|
|
|
|
left.instruction = {findBasicTurnType(via_edge, left), DirectionModifier::SlightLeft};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// FIXME this should possibly know about the actual roads?
|
|
|
|
|
// here we don't know about the intersection size. To be on the save side,
|
|
|
|
|
// we declare it
|
|
|
|
|
// as complex (at least size 4)
|
2016-11-03 05:18:27 -04:00
|
|
|
|
left.instruction = getInstructionForObvious(4, via_edge, false, left);
|
|
|
|
|
right.instruction = {findBasicTurnType(via_edge, right),
|
|
|
|
|
DirectionModifier::SlightRight};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
2017-05-23 05:59:08 -04:00
|
|
|
|
return;
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
const auto left_type = findBasicTurnType(via_edge, left);
|
|
|
|
|
const auto right_type = findBasicTurnType(via_edge, right);
|
|
|
|
|
// Two Right Turns
|
2016-11-03 05:18:27 -04:00
|
|
|
|
if (angularDeviation(left.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
// Keep left perfect, shift right
|
2016-11-03 05:18:27 -04:00
|
|
|
|
left.instruction = {left_type, DirectionModifier::Right};
|
|
|
|
|
right.instruction = {right_type, DirectionModifier::SharpRight};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-11-03 05:18:27 -04:00
|
|
|
|
if (angularDeviation(right.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
// Keep Right perfect, shift left
|
2016-11-03 05:18:27 -04:00
|
|
|
|
left.instruction = {left_type, DirectionModifier::SlightRight};
|
|
|
|
|
right.instruction = {right_type, DirectionModifier::Right};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
2017-08-07 08:10:06 -04:00
|
|
|
|
// Two Left Turns
|
2016-11-03 05:18:27 -04:00
|
|
|
|
if (angularDeviation(left.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
// Keep left perfect, shift right
|
2016-11-03 05:18:27 -04:00
|
|
|
|
left.instruction = {left_type, DirectionModifier::Left};
|
|
|
|
|
right.instruction = {right_type, DirectionModifier::SlightLeft};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-11-03 05:18:27 -04:00
|
|
|
|
if (angularDeviation(right.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
|
|
|
|
// Keep Right perfect, shift left
|
2016-11-03 05:18:27 -04:00
|
|
|
|
left.instruction = {left_type, DirectionModifier::SharpLeft};
|
|
|
|
|
right.instruction = {right_type, DirectionModifier::Left};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Shift the lesser penalty
|
2016-11-03 05:18:27 -04:00
|
|
|
|
if (getTurnDirection(left.angle) == DirectionModifier::SharpLeft)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
left.instruction = {left_type, DirectionModifier::SharpLeft};
|
|
|
|
|
right.instruction = {right_type, DirectionModifier::Left};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
2016-11-03 05:18:27 -04:00
|
|
|
|
if (getTurnDirection(right.angle) == DirectionModifier::SharpRight)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
left.instruction = {left_type, DirectionModifier::Right};
|
|
|
|
|
right.instruction = {right_type, DirectionModifier::SharpRight};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-07 08:10:06 -04:00
|
|
|
|
// turn to the right
|
|
|
|
|
if (getTurnDirection(left.angle) <= 180)
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
if (angularDeviation(left.angle, 85) >= angularDeviation(right.angle, 85))
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2017-08-07 08:10:06 -04:00
|
|
|
|
left.instruction = {left_type, DirectionModifier::SlightRight};
|
|
|
|
|
right.instruction = {right_type, DirectionModifier::Right};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2017-08-07 08:10:06 -04:00
|
|
|
|
left.instruction = {left_type, DirectionModifier::Right};
|
|
|
|
|
right.instruction = {right_type, DirectionModifier::SharpRight};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
if (angularDeviation(left.angle, 265) >= angularDeviation(right.angle, 265))
|
2016-04-08 06:49:14 -04:00
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
left.instruction = {left_type, DirectionModifier::SharpLeft};
|
|
|
|
|
right.instruction = {right_type, DirectionModifier::Left};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
|
left.instruction = {left_type, DirectionModifier::Left};
|
|
|
|
|
right.instruction = {right_type, DirectionModifier::SlightLeft};
|
2016-04-08 06:49:14 -04:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace guidance
|
|
|
|
|
} // namespace extractor
|
|
|
|
|
} // namespace osrm
|