2016-08-08 09:44:58 -04:00
|
|
|
#include "extractor/guidance/turn_lane_matcher.hpp"
|
2016-12-02 04:53:22 -05:00
|
|
|
#include "util/bearing.hpp"
|
2016-05-13 13:18:00 -04:00
|
|
|
|
|
|
|
#include <boost/assert.hpp>
|
2016-06-15 08:38:24 -04:00
|
|
|
#include <boost/numeric/conversion/cast.hpp>
|
|
|
|
|
|
|
|
#include <functional>
|
2016-05-13 13:18:00 -04:00
|
|
|
|
2016-12-02 04:53:22 -05:00
|
|
|
using osrm::util::angularDeviation;
|
|
|
|
|
2016-05-13 13:18:00 -04:00
|
|
|
namespace osrm
|
|
|
|
{
|
|
|
|
namespace extractor
|
|
|
|
{
|
|
|
|
namespace guidance
|
|
|
|
{
|
|
|
|
namespace lanes
|
|
|
|
{
|
|
|
|
|
|
|
|
// Translate Turn Tags into a Matching Direction Modifier
|
2016-06-30 03:31:08 -04:00
|
|
|
DirectionModifier::Enum getMatchingModifier(const TurnLaneType::Mask tag)
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
2016-06-21 04:41:08 -04:00
|
|
|
const constexpr TurnLaneType::Mask tag_by_modifier[] = {TurnLaneType::uturn,
|
|
|
|
TurnLaneType::sharp_right,
|
|
|
|
TurnLaneType::right,
|
|
|
|
TurnLaneType::slight_right,
|
|
|
|
TurnLaneType::straight,
|
|
|
|
TurnLaneType::slight_left,
|
|
|
|
TurnLaneType::left,
|
|
|
|
TurnLaneType::sharp_left,
|
|
|
|
TurnLaneType::merge_to_left,
|
|
|
|
TurnLaneType::merge_to_right};
|
2016-05-13 13:18:00 -04:00
|
|
|
const auto index =
|
|
|
|
std::distance(tag_by_modifier, std::find(tag_by_modifier, tag_by_modifier + 10, tag));
|
|
|
|
|
|
|
|
BOOST_ASSERT(index <= 10);
|
|
|
|
|
|
|
|
const constexpr DirectionModifier::Enum modifiers[11] = {
|
|
|
|
DirectionModifier::UTurn,
|
|
|
|
DirectionModifier::SharpRight,
|
|
|
|
DirectionModifier::Right,
|
|
|
|
DirectionModifier::SlightRight,
|
|
|
|
DirectionModifier::Straight,
|
|
|
|
DirectionModifier::SlightLeft,
|
|
|
|
DirectionModifier::Left,
|
|
|
|
DirectionModifier::SharpLeft,
|
|
|
|
DirectionModifier::Straight,
|
|
|
|
DirectionModifier::Straight,
|
|
|
|
DirectionModifier::UTurn}; // fallback for invalid tags
|
|
|
|
|
|
|
|
return modifiers[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
// check whether a match of a given tag and a turn instruction can be seen as valid
|
2016-06-30 03:31:08 -04:00
|
|
|
bool isValidMatch(const TurnLaneType::Mask tag, const TurnInstruction instruction)
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
2016-12-02 04:53:22 -05:00
|
|
|
using extractor::guidance::hasLeftModifier;
|
|
|
|
using extractor::guidance::hasRightModifier;
|
2016-05-13 13:18:00 -04:00
|
|
|
const auto isMirroredModifier = [](const TurnInstruction instruction) {
|
|
|
|
return instruction.type == TurnType::Merge;
|
|
|
|
};
|
|
|
|
|
2016-06-21 04:41:08 -04:00
|
|
|
if (tag == TurnLaneType::uturn)
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
|
|
|
return hasLeftModifier(instruction) ||
|
|
|
|
instruction.direction_modifier == DirectionModifier::UTurn;
|
|
|
|
}
|
2016-06-21 04:41:08 -04:00
|
|
|
else if (tag == TurnLaneType::sharp_right || tag == TurnLaneType::right ||
|
|
|
|
tag == TurnLaneType::slight_right)
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
|
|
|
if (isMirroredModifier(instruction))
|
|
|
|
return hasLeftModifier(instruction);
|
|
|
|
else
|
|
|
|
// needs to be adjusted for left side driving
|
|
|
|
return leavesRoundabout(instruction) || hasRightModifier(instruction);
|
|
|
|
}
|
2016-06-21 04:41:08 -04:00
|
|
|
else if (tag == TurnLaneType::straight)
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
|
|
|
return instruction.direction_modifier == DirectionModifier::Straight ||
|
|
|
|
instruction.type == TurnType::Suppressed || instruction.type == TurnType::NewName ||
|
|
|
|
instruction.type == TurnType::StayOnRoundabout || entersRoundabout(instruction) ||
|
|
|
|
(instruction.type ==
|
|
|
|
TurnType::Fork && // Forks can be experienced, even for straight segments
|
|
|
|
(instruction.direction_modifier == DirectionModifier::SlightLeft ||
|
|
|
|
instruction.direction_modifier == DirectionModifier::SlightRight)) ||
|
|
|
|
(instruction.type ==
|
|
|
|
TurnType::Continue && // Forks can be experienced, even for straight segments
|
|
|
|
(instruction.direction_modifier == DirectionModifier::SlightLeft ||
|
|
|
|
instruction.direction_modifier == DirectionModifier::SlightRight)) ||
|
|
|
|
instruction.type == TurnType::UseLane;
|
|
|
|
}
|
2016-06-21 04:41:08 -04:00
|
|
|
else if (tag == TurnLaneType::slight_left || tag == TurnLaneType::left ||
|
|
|
|
tag == TurnLaneType::sharp_left)
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
|
|
|
if (isMirroredModifier(instruction))
|
|
|
|
return hasRightModifier(instruction);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Needs to be fixed for left side driving
|
|
|
|
return (instruction.type == TurnType::StayOnRoundabout) || hasLeftModifier(instruction);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-07-22 03:08:40 -04:00
|
|
|
double getMatchingQuality(const TurnLaneType::Mask tag, const ConnectedRoad &road)
|
2016-06-30 03:31:08 -04:00
|
|
|
{
|
|
|
|
const constexpr double idealized_turn_angles[] = {0, 35, 90, 135, 180, 225, 270, 315};
|
2016-09-06 09:47:03 -04:00
|
|
|
const auto modifier = getMatchingModifier(tag);
|
|
|
|
BOOST_ASSERT(static_cast<std::size_t>(modifier) <
|
|
|
|
sizeof(idealized_turn_angles) / sizeof(*idealized_turn_angles));
|
2016-09-08 08:03:02 -04:00
|
|
|
const auto idealized_angle = idealized_turn_angles[modifier];
|
2016-11-03 05:18:27 -04:00
|
|
|
return angularDeviation(idealized_angle, road.angle);
|
2016-06-30 03:31:08 -04:00
|
|
|
}
|
|
|
|
|
2016-06-21 04:41:08 -04:00
|
|
|
// Every tag is somewhat idealized in form of the expected angle. A through lane should go straight
|
|
|
|
// (or follow a 180 degree turn angle between in/out segments.) The following function tries to find
|
|
|
|
// the best possible match for every tag in a given intersection, considering a few corner cases
|
|
|
|
// introduced to OSRM handling u-turns
|
2016-06-30 03:31:08 -04:00
|
|
|
typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask tag,
|
2016-05-13 13:18:00 -04:00
|
|
|
const Intersection &intersection)
|
|
|
|
{
|
2016-07-22 03:08:40 -04:00
|
|
|
return std::min_element(intersection.begin(),
|
|
|
|
intersection.end(),
|
|
|
|
[tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
|
|
|
|
// prefer valid matches
|
2016-11-03 05:18:27 -04:00
|
|
|
if (isValidMatch(tag, lhs.instruction) !=
|
|
|
|
isValidMatch(tag, rhs.instruction))
|
|
|
|
return isValidMatch(tag, lhs.instruction);
|
2016-07-22 03:08:40 -04:00
|
|
|
|
|
|
|
// if the entry allowed flags don't match, we select the one with
|
|
|
|
// entry allowed set to true
|
|
|
|
if (lhs.entry_allowed != rhs.entry_allowed)
|
|
|
|
return lhs.entry_allowed;
|
|
|
|
|
|
|
|
return getMatchingQuality(tag, lhs) < getMatchingQuality(tag, rhs);
|
|
|
|
});
|
2016-05-13 13:18:00 -04:00
|
|
|
}
|
|
|
|
|
2016-06-21 04:41:08 -04:00
|
|
|
// Reverse is a special case, because it requires access to the leftmost tag. It has its own
|
|
|
|
// matching function as a result of that. The leftmost tag is required, since u-turns are disabled
|
|
|
|
// by default in OSRM. Therefor we cannot check whether a turn is allowed, since it could be
|
|
|
|
// possible that it is forbidden. In addition, the best u-turn angle does not necessarily represent
|
|
|
|
// the u-turn, since it could be a sharp-left turn instead on a road with a middle island.
|
2016-07-22 03:08:40 -04:00
|
|
|
typename Intersection::const_iterator findBestMatchForReverse(const TurnLaneType::Mask neighbor_tag,
|
|
|
|
const Intersection &intersection)
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
2016-08-08 09:44:58 -04:00
|
|
|
const auto neighbor_itr = findBestMatch(neighbor_tag, intersection);
|
2016-08-12 11:04:40 -04:00
|
|
|
if (neighbor_itr + 1 == intersection.cend())
|
2016-05-13 13:18:00 -04:00
|
|
|
return intersection.begin();
|
|
|
|
|
2016-06-21 04:41:08 -04:00
|
|
|
const TurnLaneType::Mask tag = TurnLaneType::uturn;
|
2016-05-13 13:18:00 -04:00
|
|
|
return std::min_element(
|
2016-08-08 09:44:58 -04:00
|
|
|
intersection.begin() + std::distance(intersection.begin(), neighbor_itr),
|
2016-05-13 13:18:00 -04:00
|
|
|
intersection.end(),
|
2016-06-30 03:31:08 -04:00
|
|
|
[tag](const ConnectedRoad &lhs, const ConnectedRoad &rhs) {
|
2016-05-13 13:18:00 -04:00
|
|
|
// prefer valid matches
|
2016-11-03 05:18:27 -04:00
|
|
|
if (isValidMatch(tag, lhs.instruction) != isValidMatch(tag, rhs.instruction))
|
|
|
|
return isValidMatch(tag, lhs.instruction);
|
2016-06-30 03:31:08 -04:00
|
|
|
|
2016-05-13 13:18:00 -04:00
|
|
|
// if the entry allowed flags don't match, we select the one with
|
|
|
|
// entry allowed set to true
|
|
|
|
if (lhs.entry_allowed != rhs.entry_allowed)
|
|
|
|
return lhs.entry_allowed;
|
|
|
|
|
2016-07-22 03:08:40 -04:00
|
|
|
return getMatchingQuality(tag, lhs) < getMatchingQuality(tag, rhs);
|
2016-05-13 13:18:00 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2016-06-21 04:41:08 -04:00
|
|
|
// a match is trivial if all turns can be associated with their best match in a valid way and the
|
|
|
|
// matches occur in order
|
2016-05-13 13:18:00 -04:00
|
|
|
bool canMatchTrivially(const Intersection &intersection, const LaneDataVector &lane_data)
|
|
|
|
{
|
|
|
|
std::size_t road_index = 1, lane = 0;
|
2016-08-08 09:44:58 -04:00
|
|
|
if (!lane_data.empty() && lane_data.front().tag == TurnLaneType::uturn)
|
|
|
|
{
|
|
|
|
// the very first is a u-turn to the right
|
|
|
|
if (intersection[0].entry_allowed)
|
|
|
|
lane = 1;
|
|
|
|
}
|
2016-05-13 13:18:00 -04:00
|
|
|
for (; road_index < intersection.size() && lane < lane_data.size(); ++road_index)
|
|
|
|
{
|
|
|
|
if (intersection[road_index].entry_allowed)
|
|
|
|
{
|
|
|
|
BOOST_ASSERT(lane_data[lane].from != INVALID_LANEID);
|
2016-11-03 05:18:27 -04:00
|
|
|
if (!isValidMatch(lane_data[lane].tag, intersection[road_index].instruction))
|
2016-05-13 13:18:00 -04:00
|
|
|
return false;
|
|
|
|
|
|
|
|
if (findBestMatch(lane_data[lane].tag, intersection) !=
|
|
|
|
intersection.begin() + road_index)
|
|
|
|
return false;
|
2016-06-21 04:41:08 -04:00
|
|
|
|
2016-05-13 13:18:00 -04:00
|
|
|
++lane;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return lane == lane_data.size() ||
|
2016-06-21 04:41:08 -04:00
|
|
|
(lane + 1 == lane_data.size() && lane_data.back().tag == TurnLaneType::uturn);
|
2016-05-13 13:18:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Intersection triviallyMatchLanesToTurns(Intersection intersection,
|
|
|
|
const LaneDataVector &lane_data,
|
2016-06-15 08:38:24 -04:00
|
|
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
2016-06-21 04:41:08 -04:00
|
|
|
const LaneDescriptionID lane_string_id,
|
2016-12-02 04:53:22 -05:00
|
|
|
util::guidance::LaneDataIdMap &lane_data_to_id)
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
|
|
|
std::size_t road_index = 1, lane = 0;
|
2016-06-15 08:38:24 -04:00
|
|
|
|
|
|
|
const auto matchRoad = [&](ConnectedRoad &road, const TurnLaneData &data) {
|
2016-12-02 04:53:22 -05:00
|
|
|
util::guidance::LaneTupleIdPair key{{LaneID(data.to - data.from + 1), data.from},
|
|
|
|
lane_string_id};
|
2016-06-15 08:38:24 -04:00
|
|
|
|
|
|
|
auto lane_data_id = boost::numeric_cast<LaneDataID>(lane_data_to_id.size());
|
|
|
|
const auto it = lane_data_to_id.find(key);
|
|
|
|
|
|
|
|
if (it == lane_data_to_id.end())
|
|
|
|
lane_data_to_id.insert({key, lane_data_id});
|
|
|
|
else
|
|
|
|
lane_data_id = it->second;
|
|
|
|
|
|
|
|
// set lane id instead after the switch:
|
2016-11-03 05:18:27 -04:00
|
|
|
road.lane_data_id = lane_data_id;
|
2016-06-15 08:38:24 -04:00
|
|
|
};
|
|
|
|
|
2016-08-08 09:44:58 -04:00
|
|
|
if (!lane_data.empty() && lane_data.front().tag == TurnLaneType::uturn)
|
|
|
|
{
|
|
|
|
// the very first is a u-turn to the right
|
|
|
|
if (intersection[0].entry_allowed)
|
|
|
|
{
|
|
|
|
std::size_t u_turn = 0;
|
2016-11-03 05:18:27 -04:00
|
|
|
if (node_based_graph.GetEdgeData(intersection[0].eid).reversed)
|
2016-08-08 09:44:58 -04:00
|
|
|
{
|
|
|
|
if (intersection.size() <= 1 || !intersection[1].entry_allowed ||
|
2016-11-03 05:18:27 -04:00
|
|
|
intersection[1].instruction.direction_modifier != DirectionModifier::SharpRight)
|
2016-08-08 09:44:58 -04:00
|
|
|
{
|
|
|
|
// cannot match u-turn in a valid way
|
|
|
|
return intersection;
|
|
|
|
}
|
|
|
|
u_turn = 1;
|
|
|
|
road_index = 2;
|
|
|
|
}
|
|
|
|
intersection[u_turn].entry_allowed = true;
|
2016-11-03 05:18:27 -04:00
|
|
|
intersection[u_turn].instruction.type = TurnType::Turn;
|
|
|
|
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
|
2016-08-08 09:44:58 -04:00
|
|
|
|
|
|
|
matchRoad(intersection[u_turn], lane_data.back());
|
|
|
|
// continue with the first lane
|
|
|
|
lane = 1;
|
|
|
|
}
|
2016-08-12 11:04:40 -04:00
|
|
|
else
|
|
|
|
return intersection;
|
2016-08-08 09:44:58 -04:00
|
|
|
}
|
|
|
|
|
2016-05-13 13:18:00 -04:00
|
|
|
for (; road_index < intersection.size() && lane < lane_data.size(); ++road_index)
|
|
|
|
{
|
|
|
|
if (intersection[road_index].entry_allowed)
|
|
|
|
{
|
|
|
|
BOOST_ASSERT(lane_data[lane].from != INVALID_LANEID);
|
2016-11-03 05:18:27 -04:00
|
|
|
BOOST_ASSERT(isValidMatch(lane_data[lane].tag, intersection[road_index].instruction));
|
2016-05-13 13:18:00 -04:00
|
|
|
BOOST_ASSERT(findBestMatch(lane_data[lane].tag, intersection) ==
|
|
|
|
intersection.begin() + road_index);
|
|
|
|
|
2016-11-03 05:18:27 -04:00
|
|
|
if (TurnType::Suppressed == intersection[road_index].instruction.type &&
|
2016-07-08 05:45:36 -04:00
|
|
|
!lane_data[lane].suppress_assignment)
|
2016-11-03 05:18:27 -04:00
|
|
|
intersection[road_index].instruction.type = TurnType::UseLane;
|
2016-05-13 13:18:00 -04:00
|
|
|
|
2016-06-15 08:38:24 -04:00
|
|
|
matchRoad(intersection[road_index], lane_data[lane]);
|
2016-05-13 13:18:00 -04:00
|
|
|
++lane;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// handle reverse tag, if present
|
2016-06-21 04:41:08 -04:00
|
|
|
if (lane + 1 == lane_data.size() && lane_data.back().tag == TurnLaneType::uturn)
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
|
|
|
std::size_t u_turn = 0;
|
2016-11-03 05:18:27 -04:00
|
|
|
if (node_based_graph.GetEdgeData(intersection[0].eid).reversed)
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
2016-08-08 09:44:58 -04:00
|
|
|
if (!intersection.back().entry_allowed ||
|
2016-11-03 05:18:27 -04:00
|
|
|
intersection.back().instruction.direction_modifier != DirectionModifier::SharpLeft)
|
2016-05-13 13:18:00 -04:00
|
|
|
{
|
|
|
|
// cannot match u-turn in a valid way
|
2016-07-07 06:45:24 -04:00
|
|
|
return intersection;
|
2016-05-13 13:18:00 -04:00
|
|
|
}
|
|
|
|
u_turn = intersection.size() - 1;
|
|
|
|
}
|
|
|
|
intersection[u_turn].entry_allowed = true;
|
2016-11-03 05:18:27 -04:00
|
|
|
intersection[u_turn].instruction.type = TurnType::Turn;
|
|
|
|
intersection[u_turn].instruction.direction_modifier = DirectionModifier::UTurn;
|
2016-06-15 08:38:24 -04:00
|
|
|
|
|
|
|
matchRoad(intersection[u_turn], lane_data.back());
|
2016-05-13 13:18:00 -04:00
|
|
|
}
|
2016-07-07 06:45:24 -04:00
|
|
|
return intersection;
|
2016-05-13 13:18:00 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace lane_matching
|
|
|
|
} // namespace guidance
|
|
|
|
} // namespace extractor
|
|
|
|
} // namespace osrm
|