turn lane handler moved to scenario based handling
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
#include "util/debug.hpp"
|
||||
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/turn_discovery.hpp"
|
||||
#include "extractor/guidance/turn_lane_augmentation.hpp"
|
||||
@@ -30,13 +32,31 @@ std::size_t getNumberOfTurns(const Intersection &intersection)
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const constexpr char *TurnLaneHandler::scenario_names[TurnLaneScenario::NUM_SCENARIOS];
|
||||
|
||||
TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
const std::vector<TurnLaneType::Mask> &turn_lane_masks,
|
||||
const TurnAnalysis &turn_analysis)
|
||||
std::vector<std::uint32_t> &turn_lane_offsets,
|
||||
std::vector<TurnLaneType::Mask> &turn_lane_masks,
|
||||
LaneDescriptionMap &lane_description_map,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
const TurnAnalysis &turn_analysis,
|
||||
LaneDataIdMap &id_map)
|
||||
: node_based_graph(node_based_graph), turn_lane_offsets(turn_lane_offsets),
|
||||
turn_lane_masks(turn_lane_masks), turn_analysis(turn_analysis)
|
||||
turn_lane_masks(turn_lane_masks), lane_description_map(lane_description_map),
|
||||
node_info_list(node_info_list), turn_analysis(turn_analysis), id_map(id_map)
|
||||
{
|
||||
count_handled = new unsigned;
|
||||
count_called = new unsigned;
|
||||
*count_handled = *count_called = 0;
|
||||
}
|
||||
|
||||
TurnLaneHandler::~TurnLaneHandler()
|
||||
{
|
||||
std::cout << "Handled: " << *count_handled << " of " << *count_called
|
||||
<< " lanes: " << (double)(*count_handled * 100) / (*count_called) << " %."
|
||||
<< std::endl;
|
||||
delete count_called;
|
||||
delete count_handled;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -57,54 +77,199 @@ TurnLaneHandler::TurnLaneHandler(const util::NodeBasedDynamicGraph &node_based_g
|
||||
assignment onto the turns.
|
||||
For example: (130, turn slight right), (180, ramp straight), (320, turn sharp left).
|
||||
*/
|
||||
Intersection TurnLaneHandler::assignTurnLanes(const NodeID at,
|
||||
const EdgeID via_edge,
|
||||
Intersection intersection,
|
||||
LaneDataIdMap &id_map) const
|
||||
Intersection
|
||||
TurnLaneHandler::assignTurnLanes(const NodeID at, const EdgeID via_edge, Intersection intersection)
|
||||
{
|
||||
// if only a uturn exists, there is nothing we can do
|
||||
if (intersection.size() == 1)
|
||||
return intersection;
|
||||
|
||||
const auto &data = node_based_graph.GetEdgeData(via_edge);
|
||||
// Extract a lane description for the ID
|
||||
// A list of output parameters to be filled in during deduceScenario.
|
||||
// Data for the current intersection
|
||||
LaneDescriptionID lane_description_id = INVALID_LANE_DESCRIPTIONID;
|
||||
LaneDataVector lane_data;
|
||||
// Data for the previous intersection
|
||||
NodeID previous_node = SPECIAL_NODEID;
|
||||
EdgeID previous_via_edge = SPECIAL_EDGEID;
|
||||
Intersection previous_intersection;
|
||||
LaneDataVector previous_lane_data;
|
||||
LaneDescriptionID previous_description_id = INVALID_LANE_DESCRIPTIONID;
|
||||
|
||||
const auto turn_lane_description =
|
||||
data.lane_description_id != INVALID_LANE_DESCRIPTIONID
|
||||
? TurnLaneDescription(
|
||||
turn_lane_masks.begin() + turn_lane_offsets[data.lane_description_id],
|
||||
turn_lane_masks.begin() + turn_lane_offsets[data.lane_description_id + 1])
|
||||
: TurnLaneDescription();
|
||||
const auto scenario = deduceScenario(at,
|
||||
via_edge,
|
||||
intersection,
|
||||
lane_description_id,
|
||||
lane_data,
|
||||
previous_node,
|
||||
previous_via_edge,
|
||||
previous_intersection,
|
||||
previous_lane_data,
|
||||
previous_description_id);
|
||||
|
||||
BOOST_ASSERT(turn_lane_description.empty() ||
|
||||
turn_lane_description.size() == (turn_lane_offsets[data.lane_description_id + 1] -
|
||||
turn_lane_offsets[data.lane_description_id]));
|
||||
std::cout << "[turn lane] " << scenario_names[scenario] << std::endl;
|
||||
|
||||
// going straight, due to traffic signals, we can have uncompressed geometry
|
||||
if (intersection.size() == 2 &&
|
||||
((data.lane_description_id != INVALID_LANE_DESCRIPTIONID &&
|
||||
data.lane_description_id ==
|
||||
node_based_graph.GetEdgeData(intersection[1].turn.eid).lane_description_id) ||
|
||||
angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE))
|
||||
if (scenario != TurnLaneHandler::NONE)
|
||||
(*count_called)++;
|
||||
|
||||
switch (scenario)
|
||||
{
|
||||
// A turn based on current lane data
|
||||
case TurnLaneScenario::SIMPLE:
|
||||
case TurnLaneScenario::PARTITION_LOCAL:
|
||||
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(std::move(intersection), lane_data, lane_description_id);
|
||||
|
||||
// Cases operating on data carried over from a previous lane
|
||||
case TurnLaneScenario::SIMPLE_PREVIOUS:
|
||||
case TurnLaneScenario::PARTITION_PREVIOUS:
|
||||
previous_lane_data =
|
||||
handleNoneValueAtSimpleTurn(std::move(previous_lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(
|
||||
std::move(intersection), previous_lane_data, previous_description_id);
|
||||
|
||||
// Sliproads-turns that are to be handled as a single entity
|
||||
case TurnLaneScenario::SLIPROAD:
|
||||
return handleSliproadTurn(std::move(intersection),
|
||||
lane_description_id,
|
||||
std::move(lane_data),
|
||||
previous_intersection,
|
||||
previous_description_id,
|
||||
previous_lane_data);
|
||||
case TurnLaneScenario::MERGE:
|
||||
return intersection;
|
||||
default:
|
||||
// All different values that we cannot handle. For some me might want to print debug output
|
||||
// later on, when the handling is actually improved to work in close to all cases.
|
||||
// case TurnLaneScenario::UNKNOWN:
|
||||
// case TurnLaneScenario::NONE:
|
||||
// case TurnLaneScenario::INVALID:
|
||||
{
|
||||
/*
|
||||
static int print_count = 0;
|
||||
if (TurnLaneScenario::NONE != scenario && print_count++ < 10)
|
||||
{
|
||||
std::cout << "[Unhandled] " << (int)lane_description_id << " -- "
|
||||
<< (int)previous_description_id << "\n"
|
||||
<< std::endl;
|
||||
util::guidance::printTurnAssignmentData(
|
||||
at, lane_data, intersection, node_info_list);
|
||||
|
||||
auto lane_data = laneDataFromDescription(turn_lane_description);
|
||||
if (previous_node != SPECIAL_NODEID)
|
||||
util::guidance::printTurnAssignmentData(
|
||||
previous_node, previous_lane_data, previous_intersection, node_info_list);
|
||||
}
|
||||
*/
|
||||
}
|
||||
return intersection;
|
||||
}
|
||||
}
|
||||
|
||||
// Find out which scenario we have to handle
|
||||
TurnLaneHandler::TurnLaneScenario
|
||||
TurnLaneHandler::deduceScenario(const NodeID at,
|
||||
const EdgeID via_edge,
|
||||
const Intersection &intersection,
|
||||
// Output Variables
|
||||
LaneDescriptionID &lane_description_id,
|
||||
LaneDataVector &lane_data,
|
||||
NodeID &previous_node,
|
||||
EdgeID &previous_via_edge,
|
||||
Intersection &previous_intersection,
|
||||
LaneDataVector &previous_lane_data,
|
||||
LaneDescriptionID &previous_description_id)
|
||||
{
|
||||
// if only a uturn exists, there is nothing we can do
|
||||
if (intersection.size() == 1)
|
||||
return TurnLaneHandler::NONE;
|
||||
|
||||
extractLaneData(via_edge, lane_description_id, lane_data);
|
||||
|
||||
// traffic lights are not compressed during our preprocessing. Due to this *shortcoming*, we can
|
||||
// get to the following situation:
|
||||
//
|
||||
// d
|
||||
// a - b - c
|
||||
// e
|
||||
//
|
||||
// with a traffic light at b and a-b as well as b-c offering the same turn lanes.
|
||||
// In such a situation, we don't need to handle the lanes at a-b, since we will get the same
|
||||
// information at b-c, where the actual turns are taking place.
|
||||
const bool is_going_straight_and_turns_continue =
|
||||
(intersection.size() == 2 &&
|
||||
((lane_description_id != INVALID_LANE_DESCRIPTIONID &&
|
||||
lane_description_id ==
|
||||
node_based_graph.GetEdgeData(intersection[1].turn.eid).lane_description_id) ||
|
||||
angularDeviation(intersection[1].turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE));
|
||||
|
||||
if (is_going_straight_and_turns_continue)
|
||||
return TurnLaneHandler::NONE;
|
||||
|
||||
// if we see an invalid conversion, we stop immediately
|
||||
if (!turn_lane_description.empty() && lane_data.empty())
|
||||
return intersection;
|
||||
if (lane_description_id != INVALID_LANE_DESCRIPTIONID && lane_data.empty())
|
||||
return TurnLaneScenario::INVALID;
|
||||
|
||||
// might be reasonable to handle multiple turns, if we know of a sequence of lanes
|
||||
// e.g. one direction per lane, if three lanes and right, through, left available
|
||||
if (!turn_lane_description.empty() && lane_data.size() == 1 &&
|
||||
if (lane_description_id != INVALID_LANE_DESCRIPTIONID && lane_data.size() == 1 &&
|
||||
lane_data[0].tag == TurnLaneType::none)
|
||||
return intersection;
|
||||
return TurnLaneScenario::NONE;
|
||||
|
||||
// Due to sliproads, we might need access to the previous intersection at this point already;
|
||||
previous_node = SPECIAL_NODEID;
|
||||
previous_via_edge = SPECIAL_EDGEID;
|
||||
if (findPreviousIntersection(at,
|
||||
via_edge,
|
||||
intersection,
|
||||
turn_analysis,
|
||||
node_based_graph,
|
||||
previous_node,
|
||||
previous_via_edge,
|
||||
previous_intersection))
|
||||
{
|
||||
extractLaneData(previous_via_edge, previous_description_id, previous_lane_data);
|
||||
for (std::size_t road_index = 0; road_index < previous_intersection.size(); ++road_index)
|
||||
{
|
||||
const auto &road = previous_intersection[road_index];
|
||||
// in case of a sliproad that is connected to road of simlar angle, we handle the
|
||||
// turn as a combined turn
|
||||
if (road.turn.instruction.type == TurnType::Sliproad)
|
||||
{
|
||||
if (via_edge == road.turn.eid)
|
||||
return TurnLaneScenario::SLIPROAD;
|
||||
|
||||
const auto &closest_road = [&]() {
|
||||
if (road_index + 1 == previous_intersection.size())
|
||||
{
|
||||
BOOST_ASSERT(road_index > 1);
|
||||
return previous_intersection[road_index - 1];
|
||||
}
|
||||
else if (road_index == 1)
|
||||
{
|
||||
BOOST_ASSERT(road_index + 1 < previous_intersection.size());
|
||||
return previous_intersection[road_index + 1];
|
||||
}
|
||||
else if (angularDeviation(road.turn.angle,
|
||||
previous_intersection.at(road_index - 1).turn.angle) <
|
||||
angularDeviation(road.turn.angle,
|
||||
previous_intersection.at(road_index + 1).turn.angle))
|
||||
return previous_intersection[road_index - 1];
|
||||
else
|
||||
return previous_intersection[road_index + 1];
|
||||
}();
|
||||
|
||||
if (via_edge == closest_road.turn.eid)
|
||||
return TurnLaneScenario::SLIPROAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const std::size_t possible_entries = getNumberOfTurns(intersection);
|
||||
|
||||
// merge does not justify an instruction
|
||||
const bool has_merge_lane =
|
||||
hasTag(TurnLaneType::merge_to_left | TurnLaneType::merge_to_right, lane_data);
|
||||
if (has_merge_lane)
|
||||
return TurnLaneScenario::MERGE;
|
||||
|
||||
// Dead end streets that don't have any left-tag. This can happen due to the fallbacks for
|
||||
// broken data/barriers.
|
||||
@@ -114,12 +279,13 @@ Intersection TurnLaneHandler::assignTurnLanes(const NodeID at,
|
||||
lane_data) &&
|
||||
lane_data.size() + 1 == possible_entries);
|
||||
|
||||
if (has_merge_lane || has_non_usable_u_turn)
|
||||
return intersection;
|
||||
if (has_non_usable_u_turn)
|
||||
return TurnLaneScenario::INVALID;
|
||||
|
||||
// check if a u-turn is allowed (for some reason) and is missing from the list of tags (u-turn
|
||||
// is often allowed from the `left` lane without an additional indication dedicated to u-turns).
|
||||
const bool is_missing_valid_u_turn =
|
||||
!lane_data.empty() && canMatchTrivially(intersection, lane_data) &&
|
||||
lane_data.size() !=
|
||||
static_cast<std::size_t>((
|
||||
!hasTag(TurnLaneType::uturn, lane_data) && intersection[0].entry_allowed ? 1 : 0)) +
|
||||
@@ -132,18 +298,31 @@ Intersection TurnLaneHandler::assignTurnLanes(const NodeID at,
|
||||
lane_data.push_back({TurnLaneType::uturn, lane_data.back().to, lane_data.back().to});
|
||||
|
||||
bool is_simple = isSimpleIntersection(lane_data, intersection);
|
||||
// simple intersections can be assigned directly
|
||||
|
||||
if (is_simple)
|
||||
{
|
||||
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(
|
||||
std::move(intersection), lane_data, data.lane_description_id, id_map);
|
||||
}
|
||||
// if the intersection is not simple but we have lane data, we check for intersections with
|
||||
// middle islands. We have two cases. The first one is providing lane data on the current
|
||||
// segment and we only need to consider the part of the current segment. In this case we
|
||||
// partition the data and only consider the first part.
|
||||
else if (!lane_data.empty())
|
||||
return TurnLaneScenario::SIMPLE;
|
||||
|
||||
// In case of intersections that don't offer all turns right away, we have to account for
|
||||
// *delayed* turns. Consider the following example:
|
||||
//
|
||||
// e
|
||||
// a - b - c - f
|
||||
// d
|
||||
//
|
||||
// With lanes on a-b indicating: | left | through | right |.
|
||||
// While right obviously refers to a-b-d, through and left refer to a-b-c-f and a-b-c-e
|
||||
// respectively. While we are at a-b, we have to consider the right turn only.
|
||||
// The turn a-b-c gets assigned the lanes of both *left* and *through*.
|
||||
// At b-c, we get access to either a new set of lanes, or -- via the previous intersection
|
||||
// -- to
|
||||
// the second part of | left | through | right |. Lane anticipation can then deduce which
|
||||
// lanes
|
||||
// correspond to what and suppress unnecessary instructions.
|
||||
//
|
||||
// For our initial case, we consider only the turns that are available at the current
|
||||
// location,
|
||||
// which are given by partitioning the lane data and selecting the first part.
|
||||
if (!lane_data.empty())
|
||||
{
|
||||
if (lane_data.size() >= possible_entries)
|
||||
{
|
||||
@@ -155,115 +334,73 @@ Intersection TurnLaneHandler::assignTurnLanes(const NodeID at,
|
||||
// check if we were successfull in trimming
|
||||
if (lane_data.size() == possible_entries &&
|
||||
isSimpleIntersection(lane_data, intersection))
|
||||
{
|
||||
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(
|
||||
std::move(intersection), lane_data, data.lane_description_id, id_map);
|
||||
}
|
||||
return TurnLaneScenario::PARTITION_LOCAL;
|
||||
}
|
||||
}
|
||||
// The second part does not provide lane data on the current segment, but on the segment prior
|
||||
// to the turn. We try to partition the data and only consider the second part.
|
||||
else if (turn_lane_description.empty())
|
||||
{
|
||||
// acquire the lane data of a previous segment and, if possible, use it for the current
|
||||
// intersection.
|
||||
return handleTurnAtPreviousIntersection(at, via_edge, std::move(intersection), id_map);
|
||||
// If partitioning doesn't solve the problem, we don't know how to handle it right now
|
||||
return TurnLaneScenario::UNKNOWN;
|
||||
}
|
||||
|
||||
return intersection;
|
||||
if (lane_description_id != INVALID_LANE_DESCRIPTIONID)
|
||||
return TurnLaneScenario::UNKNOWN;
|
||||
|
||||
// acquire the lane data of a previous segment and, if possible, use it for the current
|
||||
// intersection.
|
||||
if (previous_via_edge == SPECIAL_EDGEID)
|
||||
return TurnLaneScenario::NONE;
|
||||
|
||||
if (previous_lane_data.empty())
|
||||
return TurnLaneScenario::NONE;
|
||||
|
||||
const bool previous_has_merge_lane =
|
||||
hasTag(TurnLaneType::merge_to_left | TurnLaneType::merge_to_right, previous_lane_data);
|
||||
if (previous_has_merge_lane)
|
||||
return TurnLaneScenario::MERGE;
|
||||
|
||||
const auto is_simple_previous =
|
||||
isSimpleIntersection(previous_lane_data, intersection) && previous_intersection.size() == 2;
|
||||
if (is_simple_previous)
|
||||
return TurnLaneScenario::SIMPLE_PREVIOUS;
|
||||
|
||||
// This is the second part of the previously described partitioning scenario.
|
||||
if (previous_lane_data.size() >= getNumberOfTurns(previous_intersection) &&
|
||||
previous_intersection.size() != 2)
|
||||
{
|
||||
previous_lane_data = partitionLaneData(node_based_graph.GetTarget(previous_via_edge),
|
||||
std::move(previous_lane_data),
|
||||
previous_intersection)
|
||||
.second;
|
||||
|
||||
std::sort(previous_lane_data.begin(), previous_lane_data.end());
|
||||
|
||||
// check if we were successfull in trimming
|
||||
if ((previous_lane_data.size() == possible_entries) &&
|
||||
isSimpleIntersection(previous_lane_data, intersection))
|
||||
return TurnLaneScenario::PARTITION_PREVIOUS;
|
||||
}
|
||||
|
||||
return TurnLaneScenario::UNKNOWN;
|
||||
}
|
||||
|
||||
// At segregated intersections, turn lanes will often only be specified up until the first turn. To
|
||||
// actually take the turn, we need to look back to the edge we drove onto the intersection with.
|
||||
Intersection TurnLaneHandler::handleTurnAtPreviousIntersection(const NodeID at,
|
||||
const EdgeID via_edge,
|
||||
Intersection intersection,
|
||||
LaneDataIdMap &id_map) const
|
||||
void TurnLaneHandler::extractLaneData(const EdgeID via_edge,
|
||||
LaneDescriptionID &lane_description_id,
|
||||
LaneDataVector &lane_data) const
|
||||
{
|
||||
NodeID previous_node = SPECIAL_NODEID;
|
||||
Intersection previous_intersection;
|
||||
EdgeID previous_id = SPECIAL_EDGEID;
|
||||
LaneDataVector lane_data;
|
||||
const auto &edge_data = node_based_graph.GetEdgeData(via_edge);
|
||||
// TODO access correct data
|
||||
lane_description_id = edge_data.lane_description_id;
|
||||
const auto lane_description =
|
||||
lane_description_id != INVALID_LANE_DESCRIPTIONID
|
||||
? TurnLaneDescription(turn_lane_masks.begin() + turn_lane_offsets[lane_description_id],
|
||||
turn_lane_masks.begin() +
|
||||
turn_lane_offsets[lane_description_id + 1])
|
||||
: TurnLaneDescription();
|
||||
|
||||
// Get the previous lane string. We only accept strings that stem from a not-simple intersection
|
||||
// and are not empty.
|
||||
const auto previous_lane_description = [&]() -> TurnLaneDescription {
|
||||
if (!findPreviousIntersection(at,
|
||||
via_edge,
|
||||
intersection,
|
||||
turn_analysis,
|
||||
node_based_graph,
|
||||
previous_node,
|
||||
previous_id,
|
||||
previous_intersection))
|
||||
return {};
|
||||
if (!lane_description.empty())
|
||||
lane_data = laneDataFromDescription(lane_description);
|
||||
|
||||
BOOST_ASSERT(previous_id != SPECIAL_EDGEID);
|
||||
|
||||
const auto &previous_edge_data = node_based_graph.GetEdgeData(previous_id);
|
||||
// TODO access correct data
|
||||
const auto previous_description =
|
||||
previous_edge_data.lane_description_id != INVALID_LANE_DESCRIPTIONID
|
||||
? TurnLaneDescription(
|
||||
turn_lane_masks.begin() +
|
||||
turn_lane_offsets[previous_edge_data.lane_description_id],
|
||||
turn_lane_masks.begin() +
|
||||
turn_lane_offsets[previous_edge_data.lane_description_id + 1])
|
||||
: TurnLaneDescription();
|
||||
if (previous_description.empty())
|
||||
return previous_description;
|
||||
|
||||
previous_intersection = turn_analysis.assignTurnTypes(
|
||||
previous_node, previous_id, std::move(previous_intersection));
|
||||
|
||||
lane_data = laneDataFromDescription(previous_description);
|
||||
|
||||
if (isSimpleIntersection(lane_data, previous_intersection))
|
||||
return {};
|
||||
else
|
||||
return previous_description;
|
||||
}();
|
||||
|
||||
// no lane string, no problems
|
||||
if (previous_lane_description.empty())
|
||||
return intersection;
|
||||
|
||||
// stop on invalid lane data conversion
|
||||
if (lane_data.empty())
|
||||
return intersection;
|
||||
|
||||
const auto &previous_data = node_based_graph.GetEdgeData(previous_id);
|
||||
const auto is_simple = isSimpleIntersection(lane_data, intersection);
|
||||
if (is_simple)
|
||||
{
|
||||
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(
|
||||
std::move(intersection), lane_data, previous_data.lane_description_id, id_map);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (lane_data.size() >= getNumberOfTurns(previous_intersection) &&
|
||||
previous_intersection.size() != 2)
|
||||
{
|
||||
lane_data = partitionLaneData(node_based_graph.GetTarget(previous_id),
|
||||
std::move(lane_data),
|
||||
previous_intersection)
|
||||
.second;
|
||||
|
||||
std::sort(lane_data.begin(), lane_data.end());
|
||||
|
||||
// check if we were successfull in trimming
|
||||
if (lane_data.size() == getNumberOfTurns(intersection) &&
|
||||
isSimpleIntersection(lane_data, intersection))
|
||||
{
|
||||
lane_data = handleNoneValueAtSimpleTurn(std::move(lane_data), intersection);
|
||||
return simpleMatchTuplesToTurns(
|
||||
std::move(intersection), lane_data, previous_data.lane_description_id, id_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
return intersection;
|
||||
BOOST_ASSERT(lane_description.empty() ||
|
||||
lane_description.size() == (turn_lane_offsets[lane_description_id + 1] -
|
||||
turn_lane_offsets[lane_description_id]));
|
||||
}
|
||||
|
||||
/* A simple intersection does not depend on the next intersection coming up. This is important
|
||||
@@ -414,8 +551,10 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
* case left) turn lane as if it were to continue straight onto the intersection and
|
||||
* look back between (1) and (2) to make sure we find the correct lane for the left-turn.
|
||||
*
|
||||
* Intersections like these have two parts. Turns that can be made at the first intersection and
|
||||
* turns that have to be made at the second. The partitioning returns the lane data split into
|
||||
* Intersections like these have two parts. Turns that can be made at the first intersection
|
||||
* and
|
||||
* turns that have to be made at the second. The partitioning returns the lane data split
|
||||
* into
|
||||
* two parts, one for the first and one for the second intersection.
|
||||
*/
|
||||
|
||||
@@ -433,7 +572,8 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
std::vector<bool> matched_at_first(turn_lane_data.size(), false);
|
||||
std::vector<bool> matched_at_second(turn_lane_data.size(), false);
|
||||
|
||||
// find out about the next intersection. To check for valid matches, we also need the turn types
|
||||
// find out about the next intersection. To check for valid matches, we also need the turn
|
||||
// types
|
||||
auto next_intersection = turn_analysis.getIntersection(at, straightmost->turn.eid);
|
||||
next_intersection =
|
||||
turn_analysis.assignTurnTypes(at, straightmost->turn.eid, std::move(next_intersection));
|
||||
@@ -447,7 +587,8 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
continue;
|
||||
|
||||
const auto best_match = findBestMatch(turn_lane_data[lane].tag, intersection);
|
||||
if (isValidMatch(turn_lane_data[lane].tag, best_match->turn.instruction))
|
||||
if (best_match->entry_allowed &&
|
||||
isValidMatch(turn_lane_data[lane].tag, best_match->turn.instruction))
|
||||
{
|
||||
matched_at_first[lane] = true;
|
||||
|
||||
@@ -457,9 +598,20 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
|
||||
const auto best_match_at_next_intersection =
|
||||
findBestMatch(turn_lane_data[lane].tag, next_intersection);
|
||||
if (isValidMatch(turn_lane_data[lane].tag,
|
||||
if (best_match_at_next_intersection->entry_allowed &&
|
||||
isValidMatch(turn_lane_data[lane].tag,
|
||||
best_match_at_next_intersection->turn.instruction))
|
||||
matched_at_second[lane] = true;
|
||||
{
|
||||
if (!matched_at_first[lane] || turn_lane_data[lane].tag == TurnLaneType::straight ||
|
||||
getMatchingQuality(turn_lane_data[lane].tag, *best_match) >
|
||||
getMatchingQuality(turn_lane_data[lane].tag, *best_match_at_next_intersection))
|
||||
{
|
||||
if (turn_lane_data[lane].tag != TurnLaneType::straight)
|
||||
matched_at_first[lane] = false;
|
||||
|
||||
matched_at_second[lane] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// we need to match all items to either the current or the next intersection
|
||||
if (!(matched_at_first[lane] || matched_at_second[lane]))
|
||||
@@ -504,7 +656,6 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
LaneDataVector first, second;
|
||||
for (std::size_t lane = 0; lane < turn_lane_data.size(); ++lane)
|
||||
{
|
||||
|
||||
if (matched_at_second[lane])
|
||||
second.push_back(turn_lane_data[lane]);
|
||||
|
||||
@@ -535,8 +686,7 @@ std::pair<LaneDataVector, LaneDataVector> TurnLaneHandler::partitionLaneData(
|
||||
|
||||
Intersection TurnLaneHandler::simpleMatchTuplesToTurns(Intersection intersection,
|
||||
const LaneDataVector &lane_data,
|
||||
const LaneDescriptionID lane_description_id,
|
||||
LaneDataIdMap &id_map) const
|
||||
const LaneDescriptionID lane_description_id)
|
||||
{
|
||||
if (lane_data.empty() || !canMatchTrivially(intersection, lane_data))
|
||||
return intersection;
|
||||
@@ -545,10 +695,127 @@ Intersection TurnLaneHandler::simpleMatchTuplesToTurns(Intersection intersection
|
||||
!hasTag(TurnLaneType::none | TurnLaneType::merge_to_left | TurnLaneType::merge_to_right,
|
||||
lane_data));
|
||||
|
||||
(*count_handled)++;
|
||||
|
||||
return triviallyMatchLanesToTurns(
|
||||
std::move(intersection), lane_data, node_based_graph, lane_description_id, id_map);
|
||||
}
|
||||
|
||||
Intersection
|
||||
TurnLaneHandler::handleSliproadTurn(Intersection intersection,
|
||||
const LaneDescriptionID lane_description_id,
|
||||
LaneDataVector lane_data,
|
||||
const Intersection &previous_intersection,
|
||||
const LaneDescriptionID &previous_lane_description_id,
|
||||
const LaneDataVector &previous_lane_data)
|
||||
{
|
||||
const auto sliproad_index =
|
||||
std::distance(previous_intersection.begin(),
|
||||
std::find_if(previous_intersection.begin(),
|
||||
previous_intersection.end(),
|
||||
[](const ConnectedRoad &road) {
|
||||
return road.turn.instruction.type == TurnType::Sliproad;
|
||||
}));
|
||||
|
||||
BOOST_ASSERT(sliproad_index <= previous_intersection.size());
|
||||
const auto &sliproad = previous_intersection[sliproad_index];
|
||||
|
||||
// code duplicatino with deduceScenario: TODO refactor
|
||||
const auto &main_road = [&]() {
|
||||
if (sliproad_index + 1 == previous_intersection.size())
|
||||
{
|
||||
BOOST_ASSERT(sliproad_index > 1);
|
||||
return previous_intersection[sliproad_index - 1];
|
||||
}
|
||||
else if (sliproad_index == 1)
|
||||
{
|
||||
BOOST_ASSERT(sliproad_index + 1 < previous_intersection.size());
|
||||
return previous_intersection[sliproad_index + 1];
|
||||
}
|
||||
else if (angularDeviation(sliproad.turn.angle,
|
||||
previous_intersection.at(sliproad_index - 1).turn.angle) <
|
||||
angularDeviation(sliproad.turn.angle,
|
||||
previous_intersection.at(sliproad_index + 1).turn.angle))
|
||||
return previous_intersection[sliproad_index - 1];
|
||||
else
|
||||
return previous_intersection[sliproad_index + 1];
|
||||
}();
|
||||
const auto main_description_id =
|
||||
node_based_graph.GetEdgeData(main_road.turn.eid).lane_description_id;
|
||||
const auto sliproad_description_id =
|
||||
node_based_graph.GetEdgeData(sliproad.turn.eid).lane_description_id;
|
||||
|
||||
if (main_description_id == INVALID_LANE_DESCRIPTIONID ||
|
||||
sliproad_description_id == INVALID_LANE_DESCRIPTIONID)
|
||||
return intersection;
|
||||
|
||||
TurnLaneDescription combined_description;
|
||||
// is the sliproad going off to the right?
|
||||
if (main_road.turn.angle > sliproad.turn.angle)
|
||||
{
|
||||
combined_description.insert(
|
||||
combined_description.end(),
|
||||
turn_lane_masks.begin() + turn_lane_offsets[main_description_id],
|
||||
turn_lane_masks.begin() + turn_lane_offsets[main_description_id + 1]);
|
||||
|
||||
combined_description.insert(
|
||||
combined_description.end(),
|
||||
turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id],
|
||||
turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id + 1]);
|
||||
|
||||
// if we handle the main road, we have to adjust the lane-data
|
||||
if (main_description_id == lane_description_id)
|
||||
{
|
||||
const auto offset = turn_lane_offsets[sliproad_description_id + 1] -
|
||||
turn_lane_offsets[sliproad_description_id];
|
||||
for (auto &item : lane_data)
|
||||
{
|
||||
item.from += offset;
|
||||
item.to += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
// or to the left?
|
||||
else
|
||||
{
|
||||
combined_description.insert(
|
||||
combined_description.end(),
|
||||
turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id],
|
||||
turn_lane_masks.begin() + turn_lane_offsets[sliproad_description_id + 1]);
|
||||
combined_description.insert(
|
||||
combined_description.end(),
|
||||
turn_lane_masks.begin() + turn_lane_offsets[main_description_id],
|
||||
turn_lane_masks.begin() + turn_lane_offsets[main_description_id + 1]);
|
||||
|
||||
// if we are handling the sliproad, we have to adjust its lane data
|
||||
if (sliproad_description_id == lane_description_id)
|
||||
{
|
||||
const auto offset =
|
||||
turn_lane_offsets[main_description_id + 1] - turn_lane_offsets[main_description_id];
|
||||
for (auto &item : lane_data)
|
||||
{
|
||||
item.from += offset;
|
||||
item.to += offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const auto combined_id = [&]() {
|
||||
auto itr = lane_description_map.find(combined_description);
|
||||
if (lane_description_map.find(combined_description) == lane_description_map.end())
|
||||
{
|
||||
const auto new_id = boost::numeric_cast<LaneDescriptionID>(lane_description_map.size());
|
||||
lane_description_map[combined_description] = new_id;
|
||||
return new_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
return itr->second;
|
||||
}
|
||||
}();
|
||||
return simpleMatchTuplesToTurns(std::move(intersection), lane_data, combined_id);
|
||||
}
|
||||
|
||||
} // namespace lanes
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
|
||||
Reference in New Issue
Block a user