2016-09-06 09:47:03 -04:00
|
|
|
#include "extractor/guidance/turn_handler.hpp"
|
2016-04-08 06:49:14 -04:00
|
|
|
#include "extractor/guidance/constants.hpp"
|
2016-04-11 06:51:06 -04:00
|
|
|
#include "extractor/guidance/intersection_scenario_three_way.hpp"
|
2016-04-08 06:49:14 -04:00
|
|
|
#include "extractor/guidance/toolkit.hpp"
|
|
|
|
|
2016-04-18 07:41:19 -04:00
|
|
|
#include "util/guidance/toolkit.hpp"
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
|
|
#include <limits>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include <boost/assert.hpp>
|
|
|
|
|
|
|
|
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
|
2016-04-18 07:41:19 -04:00
|
|
|
using osrm::util::guidance::getTurnDirection;
|
|
|
|
using osrm::util::guidance::angularDeviation;
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
|
|
namespace osrm
|
|
|
|
{
|
|
|
|
namespace extractor
|
|
|
|
{
|
|
|
|
namespace guidance
|
|
|
|
{
|
|
|
|
|
|
|
|
TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
|
|
|
const std::vector<QueryNode> &node_info_list,
|
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,
|
|
|
|
node_info_list,
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
|
|
|
if (intersection[0].entry_allowed)
|
|
|
|
{
|
2016-07-04 06:19:49 -04:00
|
|
|
intersection[0].turn.instruction = {findBasicTurnType(via_edge, intersection[0]),
|
2016-04-08 06:49:14 -04:00
|
|
|
DirectionModifier::UTurn};
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
BOOST_ASSERT(intersection[0].turn.angle < 0.001);
|
|
|
|
return intersection;
|
|
|
|
}
|
|
|
|
|
|
|
|
Intersection TurnHandler::handleTwoWayTurn(const EdgeID via_edge, Intersection intersection) const
|
|
|
|
{
|
|
|
|
BOOST_ASSERT(intersection[0].turn.angle < 0.001);
|
|
|
|
intersection[1].turn.instruction =
|
|
|
|
getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]);
|
|
|
|
|
|
|
|
return 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
|
|
|
{
|
2016-05-11 10:15:34 -04:00
|
|
|
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
2016-04-11 06:51:06 -04:00
|
|
|
|
2016-06-24 10:06:45 -04:00
|
|
|
const auto &first_data = node_based_graph.GetEdgeData(road.turn.eid);
|
|
|
|
const auto &second_data = node_based_graph.GetEdgeData(other.turn.eid);
|
|
|
|
const auto &first_classification = first_data.road_classification;
|
|
|
|
const auto &second_classification = second_data.road_classification;
|
|
|
|
const bool is_ramp = first_classification.IsRampClass();
|
2016-07-04 06:19:49 -04:00
|
|
|
const bool is_obvious_by_road_class =
|
2016-06-24 10:06:45 -04:00
|
|
|
(!is_ramp &&
|
|
|
|
(2 * first_classification.GetPriority() < second_classification.GetPriority()) &&
|
|
|
|
in_data.road_classification == first_classification) ||
|
|
|
|
(!first_classification.IsLowPriorityRoadClass() &&
|
|
|
|
second_classification.IsLowPriorityRoadClass());
|
2016-04-11 06:51:06 -04:00
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
if (is_obvious_by_road_class)
|
|
|
|
return true;
|
2016-04-11 06:51:06 -04:00
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
const bool other_is_obvious_by_road_class =
|
2016-06-24 10:06:45 -04:00
|
|
|
(!second_classification.IsRampClass() &&
|
|
|
|
(2 * second_classification.GetPriority() < first_classification.GetPriority()) &&
|
|
|
|
in_data.road_classification == second_classification) ||
|
|
|
|
(!second_classification.IsLowPriorityRoadClass() &&
|
|
|
|
first_classification.IsLowPriorityRoadClass());
|
2016-04-11 06:51:06 -04:00
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
if (other_is_obvious_by_road_class)
|
|
|
|
return false;
|
2016-04-11 06:51:06 -04:00
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
const bool turn_is_perfectly_straight =
|
|
|
|
angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < std::numeric_limits<double>::epsilon();
|
2016-04-11 06:51:06 -04:00
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
if (turn_is_perfectly_straight && in_data.name_id != EMPTY_NAMEID &&
|
|
|
|
in_data.name_id == node_based_graph.GetEdgeData(road.turn.eid).name_id)
|
|
|
|
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 =
|
|
|
|
angularDeviation(other.turn.angle, STRAIGHT_ANGLE) /
|
|
|
|
angularDeviation(road.turn.angle, STRAIGHT_ANGLE) >
|
|
|
|
INCREASES_BY_FOURTY_PERCENT &&
|
|
|
|
angularDeviation(angularDeviation(other.turn.angle, STRAIGHT_ANGLE),
|
|
|
|
angularDeviation(road.turn.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
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const
|
|
|
|
{
|
2016-08-08 05:07:27 -04:00
|
|
|
const auto obvious_index = findObviousTurn(via_edge, intersection);
|
2016-07-04 06:19:49 -04:00
|
|
|
BOOST_ASSERT(intersection[0].turn.angle < 0.001);
|
2016-04-08 06:49:14 -04:00
|
|
|
/* Two nearly straight turns -> FORK
|
|
|
|
OOOOOOO
|
|
|
|
/
|
|
|
|
IIIIII
|
|
|
|
\
|
|
|
|
OOOOOOO
|
|
|
|
*/
|
2016-07-04 06:19:49 -04:00
|
|
|
const auto fork_range = findFork(via_edge, intersection);
|
|
|
|
if (fork_range.first == 1 && fork_range.second == 2)
|
|
|
|
assignFork(via_edge, intersection[2], intersection[1]);
|
|
|
|
|
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-04-08 06:49:14 -04:00
|
|
|
intersection[1].turn.instruction = {TurnType::EndOfRoad, DirectionModifier::Right};
|
|
|
|
else
|
2016-05-03 07:37:41 -04:00
|
|
|
intersection[1].turn.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-04-08 06:49:14 -04:00
|
|
|
|
|
|
|
intersection[2].turn.instruction = {TurnType::EndOfRoad, DirectionModifier::Left};
|
|
|
|
else
|
2016-05-03 07:37:41 -04:00
|
|
|
intersection[2].turn.instruction = {TurnType::OnRamp, DirectionModifier::Left};
|
2016-04-08 06:49:14 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-08-10 08:35:02 -04:00
|
|
|
if (obvious_index == 1)
|
2016-04-08 06:49:14 -04:00
|
|
|
{
|
2016-04-11 06:51:06 -04:00
|
|
|
intersection[1].turn.instruction = getInstructionForObvious(
|
|
|
|
3, via_edge, isThroughStreet(1, intersection), intersection[1]);
|
2016-04-08 06:49:14 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
|
|
|
|
getTurnDirection(intersection[1].turn.angle)};
|
|
|
|
}
|
|
|
|
|
2016-08-10 08:35:02 -04:00
|
|
|
if (obvious_index == 2)
|
2016-04-08 06:49:14 -04:00
|
|
|
{
|
2016-04-11 06:51:06 -04:00
|
|
|
intersection[2].turn.instruction = getInstructionForObvious(
|
|
|
|
3, via_edge, isThroughStreet(2, intersection), intersection[2]);
|
2016-04-08 06:49:14 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
|
|
|
|
getTurnDirection(intersection[2].turn.angle)};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return intersection;
|
|
|
|
}
|
|
|
|
|
|
|
|
Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection intersection) const
|
|
|
|
{
|
|
|
|
const std::size_t obvious_index = findObviousTurn(via_edge, intersection);
|
2016-07-04 06:19:49 -04:00
|
|
|
const auto fork_range = findFork(via_edge, intersection);
|
2016-04-08 06:49:14 -04:00
|
|
|
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].turn.angle, STRAIGHT_ANGLE);
|
|
|
|
if (deviation < straightmost_deviation)
|
|
|
|
{
|
|
|
|
straightmost_deviation = deviation;
|
|
|
|
straightmost_turn = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-11 10:15:34 -04:00
|
|
|
// check whether there is a turn of the same name
|
|
|
|
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
|
|
|
|
|
|
|
|
const bool has_same_name_turn = [&]() {
|
|
|
|
for (std::size_t i = 1; i < intersection.size(); ++i)
|
|
|
|
{
|
|
|
|
if (node_based_graph.GetEdgeData(intersection[i].turn.eid).name_id == in_data.name_id)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}();
|
|
|
|
|
2016-04-08 06:49:14 -04:00
|
|
|
// check whether the obvious choice is actually a through street
|
|
|
|
if (obvious_index != 0)
|
|
|
|
{
|
2016-05-27 15:05:04 -04:00
|
|
|
intersection[obvious_index].turn.instruction =
|
|
|
|
getInstructionForObvious(intersection.size(),
|
|
|
|
via_edge,
|
|
|
|
isThroughStreet(obvious_index, intersection),
|
|
|
|
intersection[obvious_index]);
|
2016-05-11 10:15:34 -04:00
|
|
|
if (has_same_name_turn &&
|
|
|
|
node_based_graph.GetEdgeData(intersection[obvious_index].turn.eid).name_id !=
|
|
|
|
in_data.name_id &&
|
|
|
|
intersection[obvious_index].turn.instruction.type == TurnType::NewName)
|
|
|
|
{
|
|
|
|
// this is a special case that is necessary to correctly handle obvious turns on
|
|
|
|
// continuing streets. Right now osrm does not know about right of way. If a street
|
|
|
|
// turns to the left just like:
|
|
|
|
//
|
|
|
|
// a
|
|
|
|
// a
|
|
|
|
// aaaaaaa b b
|
|
|
|
//
|
|
|
|
// And another road exits here, we don't want to call it a new name, even though the
|
|
|
|
// turn is obvious and does not require steering. To correctly handle these situations
|
|
|
|
// in turn collapsing, we use the turn + straight combination here
|
|
|
|
intersection[obvious_index].turn.instruction.type = TurnType::Turn;
|
|
|
|
intersection[obvious_index].turn.instruction.direction_modifier =
|
|
|
|
DirectionModifier::Straight;
|
|
|
|
}
|
2016-04-08 06:49:14 -04:00
|
|
|
|
|
|
|
// assign left/right turns
|
|
|
|
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
|
|
|
|
{
|
|
|
|
if (fork_range.second - fork_range.first == 1)
|
|
|
|
{
|
|
|
|
auto &left = intersection[fork_range.second];
|
|
|
|
auto &right = intersection[fork_range.first];
|
2016-06-24 10:06:45 -04:00
|
|
|
const auto left_classification =
|
|
|
|
node_based_graph.GetEdgeData(left.turn.eid).road_classification;
|
|
|
|
const auto right_classification =
|
|
|
|
node_based_graph.GetEdgeData(right.turn.eid).road_classification;
|
|
|
|
if (canBeSeenAsFork(left_classification, right_classification))
|
2016-04-08 06:49:14 -04:00
|
|
|
assignFork(via_edge, left, right);
|
2016-06-24 10:06:45 -04:00
|
|
|
else if (left_classification.GetPriority() > right_classification.GetPriority())
|
2016-04-08 06:49:14 -04:00
|
|
|
{
|
|
|
|
right.turn.instruction =
|
|
|
|
getInstructionForObvious(intersection.size(), via_edge, false, right);
|
|
|
|
left.turn.instruction = {findBasicTurnType(via_edge, left),
|
|
|
|
DirectionModifier::SlightLeft};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
left.turn.instruction =
|
|
|
|
getInstructionForObvious(intersection.size(), via_edge, false, left);
|
|
|
|
right.turn.instruction = {findBasicTurnType(via_edge, right),
|
|
|
|
DirectionModifier::SlightRight};
|
|
|
|
}
|
|
|
|
}
|
2016-04-21 16:51:38 -04:00
|
|
|
else if (fork_range.second - fork_range.first == 2)
|
2016-04-08 06:49:14 -04:00
|
|
|
{
|
2016-05-27 15:05:04 -04:00
|
|
|
assignFork(via_edge,
|
|
|
|
intersection[fork_range.second],
|
|
|
|
intersection[fork_range.first + 1],
|
|
|
|
intersection[fork_range.first]);
|
2016-04-08 06:49:14 -04:00
|
|
|
}
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
else if (straightmost_deviation < FUZZY_ANGLE_DIFFERENCE &&
|
|
|
|
!intersection[straightmost_turn].entry_allowed)
|
|
|
|
{
|
|
|
|
// invalid straight turn
|
|
|
|
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1);
|
|
|
|
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn);
|
|
|
|
}
|
|
|
|
// no straight turn
|
|
|
|
else if (intersection[straightmost_turn].turn.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);
|
|
|
|
}
|
|
|
|
else if (intersection[straightmost_turn].turn.angle < 180)
|
|
|
|
{
|
|
|
|
intersection = assignLeftTurns(via_edge, std::move(intersection), straightmost_turn + 1);
|
|
|
|
intersection = assignRightTurns(via_edge, std::move(intersection), straightmost_turn + 1);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-05-11 10:15:34 -04:00
|
|
|
assignTrivialTurns(via_edge, intersection, 1, intersection.size());
|
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
|
|
|
|
{
|
2016-04-11 06:51:06 -04: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)
|
|
|
|
road = mirror(std::move(road));
|
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
|
|
|
|
const auto count = intersection.size() - starting_at + 1;
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
const auto first_direction = getTurnDirection(intersection[1].turn.angle);
|
|
|
|
const auto second_direction = getTurnDirection(intersection[2].turn.angle);
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
const auto first_direction = getTurnDirection(intersection[1].turn.angle);
|
|
|
|
const auto second_direction = getTurnDirection(intersection[2].turn.angle);
|
|
|
|
const auto third_direction = getTurnDirection(intersection[3].turn.angle);
|
|
|
|
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
|
|
|
|
else if (angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >=
|
2016-04-08 06:49:14 -04:00
|
|
|
NARROW_TURN_ANGLE &&
|
|
|
|
angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >=
|
|
|
|
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-04-08 06:49:14 -04:00
|
|
|
intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
|
|
|
|
DirectionModifier::SharpRight};
|
|
|
|
intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
|
|
|
|
DirectionModifier::Right};
|
|
|
|
intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]),
|
|
|
|
DirectionModifier::SlightRight};
|
|
|
|
}
|
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 &&
|
|
|
|
angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) <
|
|
|
|
GROUP_ANGLE) ||
|
|
|
|
(second_direction == third_direction &&
|
|
|
|
angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) <
|
|
|
|
GROUP_ANGLE)))
|
|
|
|
{
|
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-04-08 06:49:14 -04:00
|
|
|
angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >=
|
|
|
|
GROUP_ANGLE) ||
|
|
|
|
(second_direction == third_direction &&
|
|
|
|
angularDeviation(intersection[1].turn.angle, intersection[2].turn.angle) >=
|
|
|
|
GROUP_ANGLE)))
|
|
|
|
{
|
2016-04-11 06:51:06 -04:00
|
|
|
BOOST_ASSERT(intersection[1].entry_allowed && intersection[2].entry_allowed &&
|
|
|
|
intersection[3].entry_allowed);
|
|
|
|
|
2016-04-08 06:49:14 -04:00
|
|
|
if (angularDeviation(intersection[2].turn.angle, intersection[3].turn.angle) >=
|
|
|
|
GROUP_ANGLE)
|
|
|
|
{
|
|
|
|
handleDistinctConflict(via_edge, intersection[2], intersection[1]);
|
|
|
|
intersection[3].turn.instruction = {findBasicTurnType(via_edge, intersection[3]),
|
|
|
|
third_direction};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
|
|
|
|
first_direction};
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
std::pair<std::size_t, std::size_t> TurnHandler::findFork(const EdgeID via_edge,
|
|
|
|
const Intersection &intersection) const
|
2016-04-08 06:49:14 -04:00
|
|
|
{
|
|
|
|
|
|
|
|
std::size_t best = 0;
|
|
|
|
double best_deviation = 180;
|
|
|
|
|
|
|
|
// TODO handle road classes
|
|
|
|
for (std::size_t i = 1; i < intersection.size(); ++i)
|
|
|
|
{
|
|
|
|
const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
|
|
|
|
if (intersection[i].entry_allowed && deviation < best_deviation)
|
|
|
|
{
|
|
|
|
best_deviation = deviation;
|
|
|
|
best = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (best_deviation <= NARROW_TURN_ANGLE)
|
|
|
|
{
|
|
|
|
std::size_t left = best, right = best;
|
|
|
|
while (left + 1 < intersection.size() &&
|
2016-07-04 06:19:49 -04:00
|
|
|
(angularDeviation(intersection[left + 1].turn.angle, STRAIGHT_ANGLE) <=
|
|
|
|
NARROW_TURN_ANGLE ||
|
|
|
|
(angularDeviation(intersection[left].turn.angle,
|
|
|
|
intersection[left + 1].turn.angle) <= NARROW_TURN_ANGLE &&
|
|
|
|
angularDeviation(intersection[left].turn.angle, STRAIGHT_ANGLE) <= GROUP_ANGLE)))
|
2016-04-08 06:49:14 -04:00
|
|
|
++left;
|
2016-07-04 06:19:49 -04:00
|
|
|
while (
|
|
|
|
right > 1 &&
|
|
|
|
(angularDeviation(intersection[right - 1].turn.angle, STRAIGHT_ANGLE) <=
|
|
|
|
NARROW_TURN_ANGLE ||
|
|
|
|
(angularDeviation(intersection[right].turn.angle, intersection[right - 1].turn.angle) <
|
|
|
|
NARROW_TURN_ANGLE &&
|
|
|
|
angularDeviation(intersection[right - 1].turn.angle, STRAIGHT_ANGLE) <= GROUP_ANGLE)))
|
2016-04-08 06:49:14 -04:00
|
|
|
--right;
|
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
if (left == right)
|
|
|
|
return std::make_pair(std::size_t{0}, std::size_t{0});
|
|
|
|
|
|
|
|
const bool valid_indices = 0 < right && right < left;
|
|
|
|
const bool separated_at_left_side =
|
2016-04-08 06:49:14 -04:00
|
|
|
angularDeviation(intersection[left].turn.angle,
|
2016-04-21 16:51:38 -04:00
|
|
|
intersection[(left + 1) % intersection.size()].turn.angle) >=
|
2016-07-04 06:19:49 -04:00
|
|
|
GROUP_ANGLE;
|
|
|
|
const bool separated_at_right_side =
|
|
|
|
right > 0 &&
|
2016-04-08 06:49:14 -04:00
|
|
|
angularDeviation(intersection[right].turn.angle, intersection[right - 1].turn.angle) >=
|
2016-07-04 06:19:49 -04:00
|
|
|
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;
|
|
|
|
}();
|
|
|
|
|
|
|
|
const bool has_compatible_classes = [&]() {
|
2016-06-24 10:06:45 -04:00
|
|
|
const bool ramp_class = node_based_graph.GetEdgeData(intersection[right].turn.eid)
|
|
|
|
.road_classification.IsLinkClass();
|
2016-07-04 06:19:49 -04:00
|
|
|
for (std::size_t index = right + 1; index <= left; ++index)
|
|
|
|
if (ramp_class !=
|
2016-06-24 10:06:45 -04:00
|
|
|
node_based_graph.GetEdgeData(intersection[index].turn.eid)
|
|
|
|
.road_classification.IsLinkClass())
|
2016-07-04 06:19:49 -04:00
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
}();
|
|
|
|
|
|
|
|
// 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)
|
2016-04-08 06:49:14 -04:00
|
|
|
return std::make_pair(right, left);
|
|
|
|
}
|
|
|
|
return std::make_pair(std::size_t{0}, std::size_t{0});
|
|
|
|
}
|
|
|
|
|
|
|
|
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
|
|
|
|
if ((!left.entry_allowed || !right.entry_allowed) || (left.turn.angle == right.turn.angle))
|
|
|
|
{
|
|
|
|
if (left.entry_allowed)
|
|
|
|
left.turn.instruction = {findBasicTurnType(via_edge, left),
|
|
|
|
getTurnDirection(left.turn.angle)};
|
|
|
|
if (right.entry_allowed)
|
|
|
|
right.turn.instruction = {findBasicTurnType(via_edge, right),
|
|
|
|
getTurnDirection(right.turn.angle)};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getTurnDirection(left.turn.angle) == DirectionModifier::Straight ||
|
|
|
|
getTurnDirection(left.turn.angle) == DirectionModifier::SlightLeft ||
|
|
|
|
getTurnDirection(right.turn.angle) == DirectionModifier::SlightRight)
|
|
|
|
{
|
2016-06-24 10:06:45 -04:00
|
|
|
const auto left_classification =
|
|
|
|
node_based_graph.GetEdgeData(left.turn.eid).road_classification;
|
|
|
|
const auto right_classification =
|
|
|
|
node_based_graph.GetEdgeData(right.turn.eid).road_classification;
|
|
|
|
if (canBeSeenAsFork(left_classification, right_classification))
|
2016-04-08 06:49:14 -04:00
|
|
|
assignFork(via_edge, left, right);
|
2016-06-24 10:06:45 -04:00
|
|
|
else 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)
|
|
|
|
right.turn.instruction = getInstructionForObvious(4, via_edge, false, right);
|
|
|
|
left.turn.instruction = {findBasicTurnType(via_edge, left),
|
|
|
|
DirectionModifier::SlightLeft};
|
|
|
|
}
|
|
|
|
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)
|
|
|
|
left.turn.instruction = getInstructionForObvious(4, via_edge, false, left);
|
|
|
|
right.turn.instruction = {findBasicTurnType(via_edge, right),
|
|
|
|
DirectionModifier::SlightRight};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const auto left_type = findBasicTurnType(via_edge, left);
|
|
|
|
const auto right_type = findBasicTurnType(via_edge, right);
|
|
|
|
// Two Right Turns
|
|
|
|
if (angularDeviation(left.turn.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
|
|
|
{
|
|
|
|
// Keep left perfect, shift right
|
|
|
|
left.turn.instruction = {left_type, DirectionModifier::Right};
|
|
|
|
right.turn.instruction = {right_type, DirectionModifier::SharpRight};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (angularDeviation(right.turn.angle, 90) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
|
|
|
{
|
|
|
|
// Keep Right perfect, shift left
|
|
|
|
left.turn.instruction = {left_type, DirectionModifier::SlightRight};
|
|
|
|
right.turn.instruction = {right_type, DirectionModifier::Right};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Two Right Turns
|
|
|
|
if (angularDeviation(left.turn.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
|
|
|
{
|
|
|
|
// Keep left perfect, shift right
|
|
|
|
left.turn.instruction = {left_type, DirectionModifier::Left};
|
|
|
|
right.turn.instruction = {right_type, DirectionModifier::SlightLeft};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (angularDeviation(right.turn.angle, 270) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION)
|
|
|
|
{
|
|
|
|
// Keep Right perfect, shift left
|
|
|
|
left.turn.instruction = {left_type, DirectionModifier::SharpLeft};
|
|
|
|
right.turn.instruction = {right_type, DirectionModifier::Left};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Shift the lesser penalty
|
|
|
|
if (getTurnDirection(left.turn.angle) == DirectionModifier::SharpLeft)
|
|
|
|
{
|
|
|
|
left.turn.instruction = {left_type, DirectionModifier::SharpLeft};
|
|
|
|
right.turn.instruction = {right_type, DirectionModifier::Left};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (getTurnDirection(right.turn.angle) == DirectionModifier::SharpRight)
|
|
|
|
{
|
|
|
|
left.turn.instruction = {left_type, DirectionModifier::Right};
|
|
|
|
right.turn.instruction = {right_type, DirectionModifier::SharpRight};
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (getTurnDirection(left.turn.angle) == DirectionModifier::Right)
|
|
|
|
{
|
|
|
|
if (angularDeviation(left.turn.angle, 90) > angularDeviation(right.turn.angle, 90))
|
|
|
|
{
|
|
|
|
left.turn.instruction = {left_type, DirectionModifier::SlightRight};
|
|
|
|
right.turn.instruction = {right_type, DirectionModifier::Right};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
left.turn.instruction = {left_type, DirectionModifier::Right};
|
|
|
|
right.turn.instruction = {right_type, DirectionModifier::SharpRight};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (angularDeviation(left.turn.angle, 270) > angularDeviation(right.turn.angle, 270))
|
|
|
|
{
|
|
|
|
left.turn.instruction = {left_type, DirectionModifier::SharpLeft};
|
|
|
|
right.turn.instruction = {right_type, DirectionModifier::Left};
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
left.turn.instruction = {left_type, DirectionModifier::Left};
|
|
|
|
right.turn.instruction = {right_type, DirectionModifier::SlightLeft};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace guidance
|
|
|
|
} // namespace extractor
|
|
|
|
} // namespace osrm
|