Refactors and improves the Sliproad Handler, resolves #3109

This commit is contained in:
Daniel J. Hofmann
2016-10-26 14:32:29 -07:00
committed by Patrick Niklaus
parent df3c39cef5
commit 875f482203
16 changed files with 1797 additions and 759 deletions
+652 -210
View File
@@ -4,11 +4,14 @@
#include "util/coordinate_calculation.hpp"
#include "util/guidance/name_announcements.hpp"
#include <cmath>
#include <algorithm>
#include <iterator>
#include <limits>
#include <boost/assert.hpp>
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
using osrm::extractor::guidance::getTurnDirection;
using osrm::util::angularDeviation;
@@ -32,7 +35,8 @@ SliproadHandler::SliproadHandler(const IntersectionGenerator &intersection_gener
{
}
// included for interface reasons only
// The intersection has to connect a Sliproad, see the example scenario below:
// Intersection at `d`: Sliproad `bd` connecting `cd` and the road starting at `d`.
bool SliproadHandler::canProcess(const NodeID /*nid*/,
const EdgeID /*via_eid*/,
const Intersection &intersection) const
@@ -40,261 +44,699 @@ bool SliproadHandler::canProcess(const NodeID /*nid*/,
return intersection.size() > 2;
}
// Detect sliproad b-d in the following example:
//
// .
// e
// .
// .
// a ... b .... c .
// ` .
// ` .
// ` .
// d
// .
//
// ^ a nid
// ^ ab source_edge_id
// ^ b intersection
Intersection SliproadHandler::
operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) const
operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection intersection) const
{
BOOST_ASSERT(intersection.size() > 2);
// Potential splitting / start of a Sliproad (b)
auto intersection_node_id = node_based_graph.GetTarget(source_edge_id);
// if there is no turn, there is no sliproad
if (intersection.size() <= 2)
return intersection;
// Road index prefering non-sliproads (bc)
auto obvious = getObviousIndexWithSliproads(source_edge_id, intersection, intersection_node_id);
const auto findNextIntersectionForRoad =
[&](const NodeID at_node, const ConnectedRoad &road, NodeID &output_node) {
auto intersection = intersection_generator(at_node, road.eid);
auto in_edge = road.eid;
// skip over traffic lights
// to prevent ending up in an endless loop, we remember all visited nodes. This is
// necessary, since merging of roads can actually create enterable loops of degree two
std::unordered_set<NodeID> visited_nodes;
auto node = at_node;
while (intersection.size() == 2 && visited_nodes.count(node) == 0)
{
visited_nodes.insert(node);
node = node_based_graph.GetTarget(in_edge);
if (node == at_node)
{
// we ended up in a loop without exit
output_node = SPECIAL_NODEID;
intersection.clear();
return intersection;
}
in_edge = intersection[1].eid;
output_node = node_based_graph.GetTarget(in_edge);
intersection = intersection_generator(node, in_edge);
}
if (intersection.size() <= 2)
{
output_node = SPECIAL_NODEID;
intersection.clear();
}
return intersection;
};
const std::size_t obvious_turn_index = [&]() -> std::size_t {
const auto index = findObviousTurn(source_edge_id, intersection);
if (index != 0)
return index;
else if (intersection.size() == 3 && intersection[1].instruction.type == TurnType::Fork)
{
// Forks themselves do not contain a `obvious` turn index. If we look at a fork that has
// a one-sided sliproad, however, the non-sliproad can be considered `obvious`. Here we
// assume that this could be the case and check for a potential sliproad/non-sliproad
// situation.
NodeID intersection_node_one = SPECIAL_NODEID, intersection_node_two = SPECIAL_NODEID;
const auto intersection_following_index_one = findNextIntersectionForRoad(
intersection_node_id, intersection[1], intersection_node_one);
const auto intersection_following_index_two = findNextIntersectionForRoad(
intersection_node_id, intersection[2], intersection_node_two);
// in case of broken roads, we return
if (intersection_following_index_one.empty() ||
intersection_following_index_two.empty())
return 0;
// In case of loops at the end of the road, we will arrive back at the intersection
// itself. If that is the case, the road is obviously not a sliproad.
// a sliproad has to enter a road without choice
const auto couldBeSliproad = [](const IntersectionView &intersection) {
if (intersection.size() != 3)
return false;
if ((intersection[1].entry_allowed && intersection[2].entry_allowed) ||
intersection[0].entry_allowed)
return false;
return true;
};
if (couldBeSliproad(intersection_following_index_one) &&
intersection_node_id != intersection_node_two)
return 2;
else if (couldBeSliproad(intersection_following_index_two) &&
intersection_node_id != intersection_node_one)
return 1;
else
return 0;
}
else
return 0;
}();
if (obvious_turn_index == 0)
return intersection;
const auto &next_road = intersection[obvious_turn_index];
const auto linkTest = [this, next_road](const ConnectedRoad &road) {
return !node_based_graph.GetEdgeData(road.eid).roundabout && road.entry_allowed &&
angularDeviation(road.angle, STRAIGHT_ANGLE) <= 2 * NARROW_TURN_ANGLE &&
!hasRoundaboutType(road.instruction) &&
angularDeviation(next_road.angle, road.angle) >
std::numeric_limits<double>::epsilon();
};
bool hasNarrow =
std::find_if(intersection.begin(), intersection.end(), linkTest) != intersection.end();
if (!hasNarrow)
return intersection;
const auto source_edge_data = node_based_graph.GetEdgeData(source_edge_id);
// check whether the continue road is valid
const auto check_valid = [this, source_edge_data](const ConnectedRoad &road) {
const auto road_edge_data = node_based_graph.GetEdgeData(road.eid);
// Test to see if the source edge and the one we're looking at are the same road
const auto same_name = !util::guidance::requiresNameAnnounced(
road_edge_data.name_id, source_edge_data.name_id, name_table, street_name_suffix_table);
return road_edge_data.road_classification == source_edge_data.road_classification &&
road_edge_data.name_id != EMPTY_NAMEID && source_edge_data.name_id != EMPTY_NAMEID &&
same_name && road.entry_allowed;
};
if (!check_valid(next_road))
return intersection;
// Threshold check, if the intersection is too far away, don't bother continuing
const auto coordinate_extractor = intersection_generator.GetCoordinateExtractor();
const auto next_road_length = util::coordinate_calculation::getLength(
coordinate_extractor.GetForwardCoordinatesAlongRoad(
node_based_graph.GetTarget(source_edge_id), next_road.eid),
&util::coordinate_calculation::haversineDistance);
if (next_road_length > MAX_SLIPROAD_THRESHOLD)
if (!obvious)
{
return intersection;
}
auto next_intersection_node = node_based_graph.GetTarget(next_road.eid);
const auto next_road_next_intersection =
findNextIntersectionForRoad(intersection_node_id, next_road, next_intersection_node);
// Potential non-sliproad road (bc), leading to the intersection (c) the Sliproad (bd) shortcuts
const auto &main_road = intersection[*obvious];
if (next_road_next_intersection.empty())
// The road leading to the intersection (bc) has to continue from our source
if (!roadContinues(source_edge_id, main_road.eid))
{
return intersection;
}
// Link-check for (bc) and later on (cd) which both are getting shortcutted by Sliproad
const auto is_potential_link = [this, main_road](const ConnectedRoad &road) {
if (!road.entry_allowed)
{
return false;
}
// Prevent from starting in or going onto a roundabout
auto onto_roundabout = hasRoundaboutType(road.instruction);
if (onto_roundabout)
{
return false;
}
// Narrow turn angle for road (bd) and guard against data issues (overlapping roads)
auto is_narrow = angularDeviation(road.angle, STRAIGHT_ANGLE) <= 2 * NARROW_TURN_ANGLE;
auto same_angle = angularDeviation(main_road.angle, road.angle) //
<= std::numeric_limits<double>::epsilon();
if (!is_narrow || same_angle)
{
return false;
}
const auto &road_data = node_based_graph.GetEdgeData(road.eid);
auto is_roundabout = road_data.roundabout;
if (is_roundabout)
{
return false;
}
return true;
};
if (!std::any_of(begin(intersection), end(intersection), is_potential_link))
{
return intersection;
}
// If the intersection is too far away, don't bother continuing
if (nextIntersectionIsTooFarAway(intersection_node_id, main_road.eid))
{
return intersection;
}
// Try to find the intersection at (c) which the Sliproad shortcuts
const auto main_road_intersection = getNextIntersection(intersection_node_id, main_road.eid);
if (!main_road_intersection)
{
return intersection;
}
if (main_road_intersection->intersection.isDeadEnd())
{
return intersection;
}
// If we are at a traffic loop at the end of a road, don't consider it a sliproad
if (intersection_node_id == next_intersection_node)
return intersection;
std::unordered_set<NameID> target_road_names;
for (const auto &road : next_road_next_intersection)
if (intersection_node_id == main_road_intersection->node)
{
const auto &target_data = node_based_graph.GetEdgeData(road.eid);
target_road_names.insert(target_data.name_id);
return intersection;
}
for (auto &road : intersection)
std::vector<NameID> target_road_name_ids;
target_road_name_ids.reserve(main_road_intersection->intersection.size());
for (const auto &road : main_road_intersection->intersection)
{
if (linkTest(road))
const auto &target_data = node_based_graph.GetEdgeData(road.eid);
target_road_name_ids.push_back(target_data.name_id);
}
auto sliproad_found = false;
// For all roads at the main intersection except the UTurn road: check Sliproad scenarios.
for (std::size_t road_index = 1, last = intersection.size(); road_index < last; ++road_index)
{
const auto index_left_of_main_road = (*obvious - 1) % intersection.size();
const auto index_right_of_main_road = (*obvious + 1) % intersection.size();
// Be strict and require the Sliproad to be either left or right of the main road.
if (road_index != index_left_of_main_road && road_index != index_right_of_main_road)
continue;
auto &sliproad = intersection[road_index]; // this is what we're checking and assigning to
const auto &sliproad_data = node_based_graph.GetEdgeData(sliproad.eid);
// Intersection is orderd: 0 is UTurn, then from sharp right to sharp left.
// We already have an obvious index (bc) for going straight-ish.
const auto is_right_sliproad_turn = road_index < *obvious;
const auto is_left_sliproad_turn = road_index > *obvious;
// Road at the intersection the main road leads onto which the sliproad arrives onto
const auto crossing_road = [&] {
if (is_left_sliproad_turn)
return main_road_intersection->intersection.getLeftmostRoad();
if (is_right_sliproad_turn)
return main_road_intersection->intersection.getRightmostRoad();
BOOST_ASSERT_MSG(false, "Sliproad is neither a left nor right of obvious main road");
return main_road_intersection->intersection.getLeftmostRoad();
}();
const auto &crossing_road_data = node_based_graph.GetEdgeData(crossing_road.eid);
// The crossing road at the main road's intersection must not be incoming-only
if (crossing_road_data.reversed)
{
EdgeID candidate_in = road.eid;
const auto target_intersection = [&](NodeID node) {
auto intersection = intersection_generator(node, candidate_in);
// skip over traffic lights
if (intersection.size() == 2)
{
node = node_based_graph.GetTarget(candidate_in);
candidate_in = intersection[1].eid;
intersection = intersection_generator(node, candidate_in);
}
return intersection;
}(intersection_node_id);
continue;
}
const auto link_data = node_based_graph.GetEdgeData(road.eid);
// Check if the road continues here
const bool is_through_street =
!target_intersection.empty() && link_data.name_id != EMPTY_NAMEID &&
target_intersection.end() !=
std::find_if(
target_intersection.begin() + 1,
target_intersection.end(),
[this, &link_data](const IntersectionViewData &road) {
const auto &road_edge_data = node_based_graph.GetEdgeData(road.eid);
// Discard service and other low priority roads - never Sliproad candidate
if (sliproad_data.road_classification.IsLowPriorityRoadClass())
{
continue;
}
const auto same_name =
road_edge_data.name_id != EMPTY_NAMEID &&
!util::guidance::requiresNameAnnounced(road_edge_data.name_id,
link_data.name_id,
name_table,
street_name_suffix_table);
// Incoming-only can never be a Sliproad
if (sliproad_data.reversed)
{
continue;
}
return same_name;
});
// This is what we know so far:
//
// .
// e
// .
// .
// a ... b .... c . < `main_road_intersection` is intersection at `c`
// ` .
// ` .
// ` .
// d < `target_intersection` is intersection at `d`
// . `sliproad_edge_target` is node `d`
// e
//
//
// ^ `sliproad` is `bd`
// ^ `intersection` is intersection at `b`
// if the sliproad candidate is a through street, we cannot handle it as a sliproad
if (is_through_street)
continue;
if (!is_potential_link(sliproad))
{
continue;
}
for (const auto &candidate_road : target_intersection)
// The Sliproad graph edge - in the following we use the graph walker to
// adjust this edge forward jumping over artificial intersections.
auto sliproad_edge = sliproad.eid;
// Starting out at the intersection and going onto the Sliproad we skip artificial
// degree two intersections and limit the max hop count in doing so.
IntersectionFinderAccumulator intersection_finder{10, intersection_generator};
const SkipTrafficSignalBarrierRoadSelector road_selector{};
(void)graph_walker.TraverseRoad(intersection_node_id, // start node
sliproad_edge, // onto edge
intersection_finder, // accumulator
road_selector); // selector
sliproad_edge = intersection_finder.via_edge_id;
const auto target_intersection = intersection_finder.intersection;
// Constrain the Sliproad's target to sliproad, outgoing, incoming from main intersection
if (target_intersection.size() != 3)
{
continue;
}
const NodeID sliproad_edge_target = node_based_graph.GetTarget(sliproad_edge);
// Distinct triangle nodes `bcd`
if (intersection_node_id == main_road_intersection->node ||
intersection_node_id == sliproad_edge_target ||
main_road_intersection->node == sliproad_edge_target)
{
continue;
}
// If the sliproad candidate is a through street, we cannot handle it as a sliproad.
if (isThroughStreet(sliproad_edge, target_intersection))
{
continue;
}
// The turn off of the Sliproad has to be obvious and a narrow turn
{
const auto index = findObviousTurn(sliproad_edge, target_intersection);
if (index == 0)
{
const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.eid);
if (target_road_names.count(candidate_data.name_id) > 0)
{
if (node_based_graph.GetTarget(candidate_road.eid) == next_intersection_node)
{
road.instruction.type = TurnType::Sliproad;
break;
}
else
{
const auto skip_traffic_light_intersection = intersection_generator(
node_based_graph.GetTarget(candidate_in), candidate_road.eid);
if (skip_traffic_light_intersection.size() == 2 &&
node_based_graph.GetTarget(skip_traffic_light_intersection[1].eid) ==
next_intersection_node)
{
continue;
}
road.instruction.type = TurnType::Sliproad;
break;
}
}
const auto onto = target_intersection[index];
const auto angle_deviation = angularDeviation(onto.angle, STRAIGHT_ANGLE);
const auto is_narrow_turn = angle_deviation <= 2 * NARROW_TURN_ANGLE;
if (!is_narrow_turn)
{
continue;
}
}
// Check for curvature. Depending on the turn's direction at `c`. Scenario for right turn:
//
// a ... b .... c . a ... b .... c . a ... b .... c .
// ` . ` . . . .
// ` . . . . .
// ` . .. . .
// d d . d
//
// Sliproad Not a Sliproad
{
const auto &extractor = intersection_generator.GetCoordinateExtractor();
const NodeID start = intersection_node_id; // b
const EdgeID edge = sliproad_edge; // bd
const auto coords = extractor.GetForwardCoordinatesAlongRoad(start, edge);
BOOST_ASSERT(coords.size() >= 2);
// Now keep start and end coordinate fix and check for curvature
const auto start_coord = coords.front();
const auto end_coord = coords.back();
const auto first = std::begin(coords) + 1;
const auto last = std::end(coords) - 1;
auto snuggles = false;
using namespace util::coordinate_calculation;
// In addition, if it's a right/left turn we expect the rightmost/leftmost
// turn at `c` to be more or less ~90 degree for a Sliproad scenario.
double deviation_from_straight = 0;
if (is_right_sliproad_turn)
{
snuggles = std::all_of(first, last, [=](auto each) { //
return !isCCW(start_coord, each, end_coord);
});
const auto rightmost = main_road_intersection->intersection.getRightmostRoad();
deviation_from_straight = angularDeviation(rightmost.angle, STRAIGHT_ANGLE);
}
else if (is_left_sliproad_turn)
{
snuggles = std::all_of(first, last, [=](auto each) { //
return isCCW(start_coord, each, end_coord);
});
const auto leftmost = main_road_intersection->intersection.getLeftmostRoad();
deviation_from_straight = angularDeviation(leftmost.angle, STRAIGHT_ANGLE);
}
// The data modelling for small Sliproads is not reliable enough.
// Only check for curvature and ~90 degree when it makes sense to do so.
const constexpr auto MIN_LENGTH = 3.;
const auto length = haversineDistance(node_info_list[intersection_node_id],
node_info_list[main_road_intersection->node]);
const double perpendicular_angle = 90 + FUZZY_ANGLE_DIFFERENCE;
if (length >= MIN_LENGTH)
{
if (!snuggles)
{
continue;
}
if (deviation_from_straight > perpendicular_angle)
{
continue;
}
}
}
// Check for area under triangle `bdc`.
//
// a ... b .... c .
// ` .
// ` .
// ` .
// d
//
const auto area_threshold = std::pow(
scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, sliproad_data.road_classification),
2.);
if (!isValidSliproadArea(area_threshold,
intersection_node_id,
main_road_intersection->node,
sliproad_edge_target))
{
continue;
}
// Check all roads at `d` if one is connected to `c`, is so `bd` is Sliproad.
for (const auto &candidate_road : target_intersection)
{
const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.eid);
// Name mismatch: check roads at `c` and `d` for same name
const auto name_mismatch = [&](const NameID road_name_id) {
const auto unnamed = road_name_id == EMPTY_NAMEID;
return unnamed ||
util::guidance::requiresNameAnnounced(road_name_id, //
candidate_data.name_id, //
name_table, //
street_name_suffix_table); //
};
const auto candidate_road_name_mismatch = std::all_of(begin(target_road_name_ids), //
end(target_road_name_ids), //
name_mismatch); //
if (candidate_road_name_mismatch)
{
continue;
}
// If the Sliproad `bd` is a link, `bc` and `cd` must not be links.
if (!isValidSliproadLink(sliproad, main_road, candidate_road))
{
continue;
}
if (node_based_graph.GetTarget(candidate_road.eid) == main_road_intersection->node)
{
sliproad.instruction.type = TurnType::Sliproad;
sliproad_found = true;
break;
}
else
{
const auto skip_traffic_light_intersection = intersection_generator(
node_based_graph.GetTarget(sliproad_edge), candidate_road.eid);
if (skip_traffic_light_intersection.isTrafficSignalOrBarrier() &&
node_based_graph.GetTarget(skip_traffic_light_intersection[1].eid) ==
main_road_intersection->node)
{
sliproad.instruction.type = TurnType::Sliproad;
sliproad_found = true;
break;
}
}
}
}
if (next_road.instruction.type == TurnType::Fork)
// Now in case we found a Sliproad and assigned the corresponding type to the road,
// it could be that the intersection from which the Sliproad splits off was a Fork before.
// In those cases the obvious non-Sliproad is now obvious and we discard the Fork turn type.
if (sliproad_found && main_road.instruction.type == TurnType::Fork)
{
const auto &next_data = node_based_graph.GetEdgeData(next_road.eid);
const auto &source_edge_data = node_based_graph.GetEdgeData(source_edge_id);
const auto &main_road_data = node_based_graph.GetEdgeData(main_road.eid);
const auto same_name =
next_data.name_id != EMPTY_NAMEID && source_edge_data.name_id != EMPTY_NAMEID &&
!util::guidance::requiresNameAnnounced(
next_data.name_id, source_edge_data.name_id, name_table, street_name_suffix_table);
const auto same_name = source_edge_data.name_id != EMPTY_NAMEID && //
main_road_data.name_id != EMPTY_NAMEID && //
!util::guidance::requiresNameAnnounced(source_edge_data.name_id,
main_road_data.name_id,
name_table,
street_name_suffix_table); //
if (same_name)
{
if (angularDeviation(next_road.angle, STRAIGHT_ANGLE) < 5)
intersection[obvious_turn_index].instruction.type = TurnType::Suppressed;
if (angularDeviation(main_road.angle, STRAIGHT_ANGLE) < 5)
intersection[*obvious].instruction.type = TurnType::Suppressed;
else
intersection[obvious_turn_index].instruction.type = TurnType::Continue;
intersection[obvious_turn_index].instruction.direction_modifier =
getTurnDirection(intersection[obvious_turn_index].angle);
intersection[*obvious].instruction.type = TurnType::Continue;
intersection[*obvious].instruction.direction_modifier =
getTurnDirection(intersection[*obvious].angle);
}
else if (next_data.name_id != EMPTY_NAMEID)
else if (main_road_data.name_id != EMPTY_NAMEID)
{
intersection[obvious_turn_index].instruction.type = TurnType::NewName;
intersection[obvious_turn_index].instruction.direction_modifier =
getTurnDirection(intersection[obvious_turn_index].angle);
intersection[*obvious].instruction.type = TurnType::NewName;
intersection[*obvious].instruction.direction_modifier =
getTurnDirection(intersection[*obvious].angle);
}
else
{
intersection[obvious_turn_index].instruction.type = TurnType::Suppressed;
intersection[*obvious].instruction.type = TurnType::Suppressed;
}
}
return intersection;
}
// Implementation details
boost::optional<std::size_t> SliproadHandler::getObviousIndexWithSliproads(
const EdgeID from, const Intersection &intersection, const NodeID at) const
{
BOOST_ASSERT(from != SPECIAL_EDGEID);
BOOST_ASSERT(at != SPECIAL_NODEID);
// If a turn is obvious without taking Sliproads into account use this
const auto index = findObviousTurn(from, intersection);
if (index != 0)
{
return boost::make_optional(index);
}
// Otherwise check if the road is forking into two and one of them is a Sliproad;
// then the non-Sliproad is the obvious one.
if (intersection.size() != 3)
{
return boost::none;
}
const auto forking = intersection[1].instruction.type == TurnType::Fork &&
intersection[2].instruction.type == TurnType::Fork;
if (!forking)
{
return boost::none;
}
const auto first = getNextIntersection(at, intersection.getRightmostRoad().eid);
const auto second = getNextIntersection(at, intersection.getLeftmostRoad().eid);
if (!first || !second)
{
return boost::none;
}
if (first->intersection.isDeadEnd() || second->intersection.isDeadEnd())
{
return boost::none;
}
// In case of loops at the end of the road, we will arrive back at the intersection
// itself. If that is the case, the road is obviously not a sliproad.
if (canBeTargetOfSliproad(first->intersection) && at != second->node)
{
return boost::make_optional(std::size_t{2});
}
if (canBeTargetOfSliproad(second->intersection) && at != first->node)
{
return boost::make_optional(std::size_t{1});
}
return boost::none;
}
bool SliproadHandler::nextIntersectionIsTooFarAway(const NodeID start, const EdgeID onto) const
{
BOOST_ASSERT(start != SPECIAL_NODEID);
BOOST_ASSERT(onto != SPECIAL_EDGEID);
const auto &extractor = intersection_generator.GetCoordinateExtractor();
// Base max distance threshold on the current road class we're on
const auto &data = node_based_graph.GetEdgeData(onto);
const auto threshold = scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, // <- scales down
data.road_classification);
DistanceToNextIntersectionAccumulator accumulator{extractor, node_based_graph, threshold};
const SkipTrafficSignalBarrierRoadSelector selector{};
(void)graph_walker.TraverseRoad(start, onto, accumulator, selector);
return accumulator.too_far_away;
}
bool SliproadHandler::isThroughStreet(const EdgeID from, const IntersectionView &intersection) const
{
BOOST_ASSERT(from != SPECIAL_EDGEID);
BOOST_ASSERT(!intersection.empty());
const auto &edge_name_id = node_based_graph.GetEdgeData(from).name_id;
auto first = begin(intersection) + 1; // Skip UTurn road
auto last = end(intersection);
auto same_name = [&](const auto &road) {
const auto &road_name_id = node_based_graph.GetEdgeData(road.eid).name_id;
return edge_name_id != EMPTY_NAMEID && //
road_name_id != EMPTY_NAMEID && //
!util::guidance::requiresNameAnnounced(edge_name_id,
road_name_id,
name_table,
street_name_suffix_table); //
};
return std::find_if(first, last, same_name) != last;
}
bool SliproadHandler::roadContinues(const EdgeID current, const EdgeID next) const
{
const auto &current_data = node_based_graph.GetEdgeData(current);
const auto &next_data = node_based_graph.GetEdgeData(next);
auto same_road_category = current_data.road_classification == next_data.road_classification;
auto same_travel_mode = current_data.travel_mode == next_data.travel_mode;
auto same_name = current_data.name_id != EMPTY_NAMEID && //
next_data.name_id != EMPTY_NAMEID && //
!util::guidance::requiresNameAnnounced(current_data.name_id,
next_data.name_id,
name_table,
street_name_suffix_table); //
const auto continues = same_road_category && same_travel_mode && same_name;
return continues;
}
bool SliproadHandler::isValidSliproadArea(const double max_area,
const NodeID a,
const NodeID b,
const NodeID c) const
{
using namespace util::coordinate_calculation;
const auto first = node_info_list[a];
const auto second = node_info_list[b];
const auto third = node_info_list[c];
const auto length = haversineDistance(first, second);
const auto heigth = haversineDistance(second, third);
const auto area = (length * heigth) / 2.;
// Everything below is data issue - there are some weird situations where
// nodes are really close to each other and / or tagging ist just plain off.
const constexpr auto MIN_SLIPROAD_AREA = 3.;
if (area < MIN_SLIPROAD_AREA || area > max_area)
{
return false;
}
return true;
}
bool SliproadHandler::isValidSliproadLink(const IntersectionViewData &sliproad,
const IntersectionViewData &first,
const IntersectionViewData &second) const
{
// If the Sliproad is not a link we don't care
const auto &sliproad_data = node_based_graph.GetEdgeData(sliproad.eid);
if (!sliproad_data.road_classification.IsLinkClass())
{
return true;
}
// otherwise neither the first road leading to the intersection we shortcut
const auto &first_road_data = node_based_graph.GetEdgeData(first.eid);
if (first_road_data.road_classification.IsLinkClass())
{
return false;
}
// nor the second road coming from the intersection we shotcut must be links
const auto &second_road_data = node_based_graph.GetEdgeData(second.eid);
if (second_road_data.road_classification.IsLinkClass())
{
return false;
}
return true;
}
bool SliproadHandler::canBeTargetOfSliproad(const IntersectionView &intersection)
{
// Example to handle:
// .
// a . . b .
// ` .
// ` .
// c < intersection
// .
//
// One outgoing two incoming
if (intersection.size() != 3)
{
return false;
}
const auto backwards = intersection[0].entry_allowed;
const auto multiple_allowed = intersection[1].entry_allowed && intersection[2].entry_allowed;
if (backwards || multiple_allowed)
{
return false;
}
return true;
}
double SliproadHandler::scaledThresholdByRoadClass(const double max_threshold,
const RoadClassification &classification)
{
double factor = 1.0;
switch (classification.GetPriority())
{
case RoadPriorityClass::MOTORWAY:
factor = 1.0;
break;
case RoadPriorityClass::TRUNK:
factor = 0.8;
break;
case RoadPriorityClass::PRIMARY:
factor = 0.8;
break;
case RoadPriorityClass::SECONDARY:
factor = 0.6;
break;
case RoadPriorityClass::TERTIARY:
factor = 0.5;
break;
case RoadPriorityClass::MAIN_RESIDENTIAL:
factor = 0.4;
break;
case RoadPriorityClass::SIDE_RESIDENTIAL:
factor = 0.3;
break;
case RoadPriorityClass::LINK_ROAD:
factor = 0.3;
break;
case RoadPriorityClass::CONNECTIVITY:
factor = 0.1;
break;
// What
case RoadPriorityClass::BIKE_PATH:
case RoadPriorityClass::FOOT_PATH:
default:
factor = 0.1;
}
const auto scaled = max_threshold * factor;
BOOST_ASSERT(scaled <= max_threshold);
return scaled;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm