explicitly check for 90 degree turns / turning onto segregated roads
This commit is contained in:
parent
b8651bfac9
commit
704cf314d4
@ -140,6 +140,22 @@ class MergableRoadDetector
|
|||||||
// The detector wants to prevent merges that are connected to `b-e`
|
// The detector wants to prevent merges that are connected to `b-e`
|
||||||
bool IsLinkRoad(const NodeID intersection_node, const MergableRoadData &road) const;
|
bool IsLinkRoad(const NodeID intersection_node, const MergableRoadData &road) const;
|
||||||
|
|
||||||
|
// The condition suppresses roads merging for intersections like
|
||||||
|
// . .
|
||||||
|
// . .
|
||||||
|
// ---- ----
|
||||||
|
// . .
|
||||||
|
// . .
|
||||||
|
// but will allow roads merging for intersections like
|
||||||
|
// -------
|
||||||
|
// / \
|
||||||
|
// ---- ----
|
||||||
|
// \ /
|
||||||
|
// -------
|
||||||
|
bool IsCircularShape(const NodeID intersection_node,
|
||||||
|
const MergableRoadData &lhs,
|
||||||
|
const MergableRoadData &rhs) const;
|
||||||
|
|
||||||
const util::NodeBasedDynamicGraph &node_based_graph;
|
const util::NodeBasedDynamicGraph &node_based_graph;
|
||||||
const EdgeBasedNodeDataContainer &node_data_container;
|
const EdgeBasedNodeDataContainer &node_data_container;
|
||||||
const std::vector<util::Coordinate> &node_coordinates;
|
const std::vector<util::Coordinate> &node_coordinates;
|
||||||
@ -149,6 +165,9 @@ class MergableRoadDetector
|
|||||||
// name detection
|
// name detection
|
||||||
const util::NameTable &name_table;
|
const util::NameTable &name_table;
|
||||||
const SuffixTable &street_name_suffix_table;
|
const SuffixTable &street_name_suffix_table;
|
||||||
|
|
||||||
|
// limit for detecting circles / parallel roads
|
||||||
|
const static double constexpr distance_to_extract = 150;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace guidance
|
} // namespace guidance
|
||||||
|
@ -123,7 +123,8 @@ struct SelectStraightmostRoadByNameAndOnlyChoice
|
|||||||
{
|
{
|
||||||
SelectStraightmostRoadByNameAndOnlyChoice(const NameID desired_name_id,
|
SelectStraightmostRoadByNameAndOnlyChoice(const NameID desired_name_id,
|
||||||
const double initial_bearing,
|
const double initial_bearing,
|
||||||
const bool requires_entry);
|
const bool requires_entry,
|
||||||
|
const bool stop_on_ambiguous_turns);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* !! REQUIRED - Function for the use of TraverseRoad in the graph walker.
|
* !! REQUIRED - Function for the use of TraverseRoad in the graph walker.
|
||||||
@ -141,6 +142,7 @@ struct SelectStraightmostRoadByNameAndOnlyChoice
|
|||||||
const NameID desired_name_id;
|
const NameID desired_name_id;
|
||||||
const double initial_bearing;
|
const double initial_bearing;
|
||||||
const bool requires_entry;
|
const bool requires_entry;
|
||||||
|
const bool stop_on_ambiguous_turns;
|
||||||
};
|
};
|
||||||
|
|
||||||
// find the next intersection given a hop limit
|
// find the next intersection given a hop limit
|
||||||
|
@ -109,7 +109,13 @@ bool MergableRoadDetector::CanMergeRoad(const NodeID intersection_node,
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
// finally check if two roads describe the direction
|
// finally check if two roads describe the direction
|
||||||
return HaveSameDirection(intersection_node, lhs, rhs);
|
if (HaveSameDirection(intersection_node, lhs, rhs))
|
||||||
|
{
|
||||||
|
// do not merge traffic circles and similar
|
||||||
|
return !IsCircularShape(intersection_node, lhs, rhs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MergableRoadDetector::HaveIdenticalNames(const NameID lhs, const NameID rhs) const
|
bool MergableRoadDetector::HaveIdenticalNames(const NameID lhs, const NameID rhs) const
|
||||||
@ -184,7 +190,8 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
|||||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(lhs.eid).annotation_data)
|
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(lhs.eid).annotation_data)
|
||||||
.name_id,
|
.name_id,
|
||||||
lhs.bearing,
|
lhs.bearing,
|
||||||
/*requires entry=*/false);
|
/*requires entry=*/false,
|
||||||
|
false);
|
||||||
|
|
||||||
NodeBasedGraphWalker graph_walker(
|
NodeBasedGraphWalker graph_walker(
|
||||||
node_based_graph, node_data_container, intersection_generator);
|
node_based_graph, node_data_container, intersection_generator);
|
||||||
@ -270,14 +277,10 @@ bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
|
|||||||
node_based_graph.GetTarget(right_accumulator.via_edge_id);
|
node_based_graph.GetTarget(right_accumulator.via_edge_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
bool MergableRoadDetector::IsCircularShape(const NodeID intersection_node,
|
||||||
const MergableRoadData &lhs,
|
const MergableRoadData &lhs,
|
||||||
const MergableRoadData &rhs) const
|
const MergableRoadData &rhs) const
|
||||||
{
|
{
|
||||||
if (angularDeviation(lhs.bearing, rhs.bearing) > MERGABLE_ANGLE_DIFFERENCE)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Find a coordinate following a road that is far away
|
|
||||||
NodeBasedGraphWalker graph_walker(
|
NodeBasedGraphWalker graph_walker(
|
||||||
node_based_graph, node_data_container, intersection_generator);
|
node_based_graph, node_data_container, intersection_generator);
|
||||||
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
|
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
|
||||||
@ -286,7 +289,8 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
|||||||
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(edge_id).annotation_data)
|
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(edge_id).annotation_data)
|
||||||
.name_id,
|
.name_id,
|
||||||
lhs.bearing,
|
lhs.bearing,
|
||||||
/*requires_entry=*/false);
|
/*requires_entry=*/false,
|
||||||
|
false);
|
||||||
graph_walker.TraverseRoad(intersection_node, edge_id, accumulator, selector);
|
graph_walker.TraverseRoad(intersection_node, edge_id, accumulator, selector);
|
||||||
|
|
||||||
return std::make_pair(accumulator.accumulated_length, accumulator.coordinates);
|
return std::make_pair(accumulator.accumulated_length, accumulator.coordinates);
|
||||||
@ -295,26 +299,12 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
|||||||
std::vector<util::Coordinate> coordinates_to_the_left, coordinates_to_the_right;
|
std::vector<util::Coordinate> coordinates_to_the_left, coordinates_to_the_right;
|
||||||
double distance_traversed_to_the_left, distance_traversed_to_the_right;
|
double distance_traversed_to_the_left, distance_traversed_to_the_right;
|
||||||
|
|
||||||
// many roads only do short parallel segments. To get a good impression of how `parallel` two
|
|
||||||
// roads are, we look 100 meters down the road (wich can be quite short for very broad roads).
|
|
||||||
const double constexpr distance_to_extract = 150;
|
|
||||||
|
|
||||||
std::tie(distance_traversed_to_the_left, coordinates_to_the_left) =
|
std::tie(distance_traversed_to_the_left, coordinates_to_the_left) =
|
||||||
getCoordinatesAlongWay(lhs.eid, distance_to_extract);
|
getCoordinatesAlongWay(lhs.eid, distance_to_extract);
|
||||||
|
|
||||||
// tuned parameter, if we didn't get as far as 40 meters, we might barely look past an
|
|
||||||
// intersection.
|
|
||||||
const auto constexpr MINIMUM_LENGTH_FOR_PARALLEL_DETECTION = 40;
|
|
||||||
// quit early if the road is not very long
|
|
||||||
if (distance_traversed_to_the_left <= MINIMUM_LENGTH_FOR_PARALLEL_DETECTION)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
std::tie(distance_traversed_to_the_right, coordinates_to_the_right) =
|
std::tie(distance_traversed_to_the_right, coordinates_to_the_right) =
|
||||||
getCoordinatesAlongWay(rhs.eid, distance_to_extract);
|
getCoordinatesAlongWay(rhs.eid, distance_to_extract);
|
||||||
|
|
||||||
if (distance_traversed_to_the_right <= MINIMUM_LENGTH_FOR_PARALLEL_DETECTION)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
const auto connect_again = (coordinates_to_the_left.back() == coordinates_to_the_right.back());
|
const auto connect_again = (coordinates_to_the_left.back() == coordinates_to_the_right.back());
|
||||||
|
|
||||||
// Tuning parameter to detect and don't merge roads close to circular shapes
|
// Tuning parameter to detect and don't merge roads close to circular shapes
|
||||||
@ -344,9 +334,55 @@ bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
|||||||
// then don't merge roads if A/L² is greater than the lower bound
|
// then don't merge roads if A/L² is greater than the lower bound
|
||||||
BOOST_ASSERT(area_to_squared_perimeter_ratio <= 1. / (4 * M_PI));
|
BOOST_ASSERT(area_to_squared_perimeter_ratio <= 1. / (4 * M_PI));
|
||||||
if (area_to_squared_perimeter_ratio >= CIRCULAR_POLYGON_ISOPERIMETRIC_LOWER_BOUND)
|
if (area_to_squared_perimeter_ratio >= CIRCULAR_POLYGON_ISOPERIMETRIC_LOWER_BOUND)
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
|
||||||
|
const MergableRoadData &lhs,
|
||||||
|
const MergableRoadData &rhs) const
|
||||||
|
{
|
||||||
|
if (angularDeviation(lhs.bearing, rhs.bearing) > MERGABLE_ANGLE_DIFFERENCE)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Find a coordinate following a road that is far away
|
||||||
|
NodeBasedGraphWalker graph_walker(
|
||||||
|
node_based_graph, node_data_container, intersection_generator);
|
||||||
|
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
|
||||||
|
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
|
||||||
|
SelectStraightmostRoadByNameAndOnlyChoice selector(
|
||||||
|
node_data_container.GetAnnotation(node_based_graph.GetEdgeData(edge_id).annotation_data)
|
||||||
|
.name_id,
|
||||||
|
lhs.bearing,
|
||||||
|
/*requires_entry=*/false,
|
||||||
|
true);
|
||||||
|
graph_walker.TraverseRoad(intersection_node, edge_id, accumulator, selector);
|
||||||
|
|
||||||
|
return std::make_pair(accumulator.accumulated_length, accumulator.coordinates);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<util::Coordinate> coordinates_to_the_left, coordinates_to_the_right;
|
||||||
|
double distance_traversed_to_the_left, distance_traversed_to_the_right;
|
||||||
|
|
||||||
|
std::tie(distance_traversed_to_the_left, coordinates_to_the_left) =
|
||||||
|
getCoordinatesAlongWay(lhs.eid, distance_to_extract);
|
||||||
|
|
||||||
|
// tuned parameter, if we didn't get as far as 40 meters, we might barely look past an
|
||||||
|
// intersection.
|
||||||
|
const auto constexpr MINIMUM_LENGTH_FOR_PARALLEL_DETECTION = 40;
|
||||||
|
// quit early if the road is not very long
|
||||||
|
if (distance_traversed_to_the_left <= MINIMUM_LENGTH_FOR_PARALLEL_DETECTION)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
std::tie(distance_traversed_to_the_right, coordinates_to_the_right) =
|
||||||
|
getCoordinatesAlongWay(rhs.eid, distance_to_extract);
|
||||||
|
|
||||||
|
if (distance_traversed_to_the_right <= MINIMUM_LENGTH_FOR_PARALLEL_DETECTION)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const auto connect_again = (coordinates_to_the_left.back() == coordinates_to_the_right.back());
|
||||||
// sampling to correctly weight longer segments in regression calculations
|
// sampling to correctly weight longer segments in regression calculations
|
||||||
const auto constexpr SAMPLE_INTERVAL = 5;
|
const auto constexpr SAMPLE_INTERVAL = 5;
|
||||||
coordinates_to_the_left = coordinate_extractor.SampleCoordinates(
|
coordinates_to_the_left = coordinate_extractor.SampleCoordinates(
|
||||||
|
@ -103,9 +103,12 @@ operator()(const NodeID /*nid*/,
|
|||||||
|
|
||||||
// ---------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------
|
||||||
SelectStraightmostRoadByNameAndOnlyChoice::SelectStraightmostRoadByNameAndOnlyChoice(
|
SelectStraightmostRoadByNameAndOnlyChoice::SelectStraightmostRoadByNameAndOnlyChoice(
|
||||||
const NameID desired_name_id, const double initial_bearing, const bool requires_entry)
|
const NameID desired_name_id,
|
||||||
|
const double initial_bearing,
|
||||||
|
const bool requires_entry,
|
||||||
|
const bool stop_on_ambiguous_turns)
|
||||||
: desired_name_id(desired_name_id), initial_bearing(initial_bearing),
|
: desired_name_id(desired_name_id), initial_bearing(initial_bearing),
|
||||||
requires_entry(requires_entry)
|
requires_entry(requires_entry), stop_on_ambiguous_turns(stop_on_ambiguous_turns)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,6 +158,36 @@ operator()(const NodeID /*nid*/,
|
|||||||
std::min_element(std::next(std::begin(intersection)), std::end(intersection), comparator);
|
std::min_element(std::next(std::begin(intersection)), std::end(intersection), comparator);
|
||||||
|
|
||||||
const auto is_valid_choice = !requires_entry || min_element->entry_allowed;
|
const auto is_valid_choice = !requires_entry || min_element->entry_allowed;
|
||||||
|
|
||||||
|
if (!is_valid_choice)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
// only road exiting or continuing in the same general direction
|
||||||
|
const auto has_valid_angle =
|
||||||
|
((intersection.size() == 2 ||
|
||||||
|
intersection.findClosestTurn(STRAIGHT_ANGLE) == min_element) &&
|
||||||
|
angularDeviation(min_element->angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) &&
|
||||||
|
angularDeviation(initial_bearing, min_element->bearing) < NARROW_TURN_ANGLE;
|
||||||
|
|
||||||
|
if (has_valid_angle)
|
||||||
|
return (*min_element).eid;
|
||||||
|
|
||||||
|
// in some cases, stronger turns are appropriate. We allow turns of just a bit over 90 degrees,
|
||||||
|
// if it's not a end of road situation. These angles come into play where roads split into dual
|
||||||
|
// carriage-ways.
|
||||||
|
//
|
||||||
|
// e - - f
|
||||||
|
// a - - - - b
|
||||||
|
// c - - d
|
||||||
|
// |
|
||||||
|
// g
|
||||||
|
//
|
||||||
|
// is technically
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// a - - - - b (ce) - - (fg)
|
||||||
|
// |
|
||||||
|
// g
|
||||||
const auto is_only_choice_with_same_name =
|
const auto is_only_choice_with_same_name =
|
||||||
count_desired_name <= 2 && // <= in case we come from a bridge, otherwise we have a u-turn
|
count_desired_name <= 2 && // <= in case we come from a bridge, otherwise we have a u-turn
|
||||||
// and the outgoing edge
|
// and the outgoing edge
|
||||||
@ -162,11 +195,6 @@ operator()(const NodeID /*nid*/,
|
|||||||
.GetAnnotation(node_based_graph.GetEdgeData(min_element->eid).annotation_data)
|
.GetAnnotation(node_based_graph.GetEdgeData(min_element->eid).annotation_data)
|
||||||
.name_id == desired_name_id &&
|
.name_id == desired_name_id &&
|
||||||
angularDeviation(min_element->angle, STRAIGHT_ANGLE) < 100; // don't do crazy turns
|
angularDeviation(min_element->angle, STRAIGHT_ANGLE) < 100; // don't do crazy turns
|
||||||
const auto has_valid_angle =
|
|
||||||
((intersection.size() == 2 ||
|
|
||||||
intersection.findClosestTurn(STRAIGHT_ANGLE) == min_element) &&
|
|
||||||
angularDeviation(min_element->angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) &&
|
|
||||||
angularDeviation(initial_bearing, min_element->bearing) < NARROW_TURN_ANGLE;
|
|
||||||
|
|
||||||
// do not allow major turns in the road, if other similar turns are present
|
// do not allow major turns in the road, if other similar turns are present
|
||||||
// e.g.a turn at the end of the road:
|
// e.g.a turn at the end of the road:
|
||||||
@ -180,31 +208,29 @@ operator()(const NodeID /*nid*/,
|
|||||||
// Such a turn can never be part of a merge
|
// Such a turn can never be part of a merge
|
||||||
// We check if there is a similar turn to the other side. If such a turn exists, we consider the
|
// We check if there is a similar turn to the other side. If such a turn exists, we consider the
|
||||||
// continuation of the road not possible
|
// continuation of the road not possible
|
||||||
if (util::angularDeviation(STRAIGHT_ANGLE, min_element->angle) > GROUP_ANGLE)
|
if (stop_on_ambiguous_turns &&
|
||||||
|
util::angularDeviation(STRAIGHT_ANGLE, min_element->angle) > GROUP_ANGLE)
|
||||||
{
|
{
|
||||||
auto deviation = util::angularDeviation(STRAIGHT_ANGLE, min_element->angle);
|
auto opposite = intersection.findClosestTurn(util::bearing::reverse(min_element->angle));
|
||||||
auto opposite_angle = min_element->angle >= STRAIGHT_ANGLE ? (STRAIGHT_ANGLE - deviation)
|
auto opposite_deviation = util::angularDeviation(min_element->angle, opposite->angle);
|
||||||
: (STRAIGHT_ANGLE + deviation);
|
// d - - - - c - - - -e
|
||||||
auto opposite = intersection.findClosestTurn(opposite_angle);
|
// |
|
||||||
auto opposite_deviation = util::angularDeviation(STRAIGHT_ANGLE, opposite->angle);
|
// |
|
||||||
if (opposite_deviation <= deviation || (deviation / opposite_deviation) < 1.5)
|
// a - - - - b
|
||||||
{
|
// from b-c onto min_element d with opposite side e
|
||||||
|
if (opposite_deviation > (180 - FUZZY_ANGLE_DIFFERENCE))
|
||||||
return {};
|
return {};
|
||||||
}
|
|
||||||
|
|
||||||
|
// e
|
||||||
|
// |
|
||||||
|
// a - - - - b - - - - -d
|
||||||
|
// doing a left turn while straight is a choice
|
||||||
auto const best = intersection.findClosestTurn(STRAIGHT_ANGLE);
|
auto const best = intersection.findClosestTurn(STRAIGHT_ANGLE);
|
||||||
if (util::angularDeviation(best->angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE)
|
if (util::angularDeviation(best->angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE)
|
||||||
{
|
|
||||||
return {};
|
return {};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// in cases where we have two edges between roads, we can have quite severe angles due to the
|
return is_only_choice_with_same_name ? boost::optional<EdgeID>(min_element->eid) : boost::none;
|
||||||
// random split OSRM does to break up parallel edges at any coordinate
|
|
||||||
if (!is_valid_choice || !(is_only_choice_with_same_name || has_valid_angle))
|
|
||||||
return {};
|
|
||||||
else
|
|
||||||
return (*min_element).eid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------------
|
||||||
|
Loading…
Reference in New Issue
Block a user