2016-10-05 06:33:58 -04:00
|
|
|
#include "extractor/guidance/sliproad_handler.hpp"
|
2016-07-04 06:19:49 -04:00
|
|
|
#include "extractor/guidance/constants.hpp"
|
|
|
|
#include "extractor/guidance/intersection_scenario_three_way.hpp"
|
|
|
|
#include "extractor/guidance/toolkit.hpp"
|
|
|
|
|
|
|
|
#include "util/guidance/toolkit.hpp"
|
|
|
|
|
|
|
|
#include <limits>
|
|
|
|
#include <utility>
|
|
|
|
|
|
|
|
#include <boost/assert.hpp>
|
|
|
|
|
|
|
|
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
|
|
|
|
using osrm::util::guidance::getTurnDirection;
|
|
|
|
using osrm::util::guidance::angularDeviation;
|
|
|
|
|
|
|
|
namespace osrm
|
|
|
|
{
|
|
|
|
namespace extractor
|
|
|
|
{
|
|
|
|
namespace guidance
|
|
|
|
{
|
|
|
|
|
|
|
|
SliproadHandler::SliproadHandler(const IntersectionGenerator &intersection_generator,
|
|
|
|
const util::NodeBasedDynamicGraph &node_based_graph,
|
|
|
|
const std::vector<QueryNode> &node_info_list,
|
|
|
|
const util::NameTable &name_table,
|
|
|
|
const SuffixTable &street_name_suffix_table)
|
2016-08-15 06:43:26 -04:00
|
|
|
: IntersectionHandler(node_based_graph,
|
|
|
|
node_info_list,
|
|
|
|
name_table,
|
|
|
|
street_name_suffix_table,
|
|
|
|
intersection_generator)
|
2016-07-04 06:19:49 -04:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
// included for interface reasons only
|
|
|
|
bool SliproadHandler::canProcess(const NodeID /*nid*/,
|
|
|
|
const EdgeID /*via_eid*/,
|
2016-09-12 04:08:01 -04:00
|
|
|
const Intersection &intersection) const
|
2016-07-04 06:19:49 -04:00
|
|
|
{
|
2016-09-12 04:08:01 -04:00
|
|
|
return intersection.size() > 2;
|
2016-07-04 06:19:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
Intersection SliproadHandler::
|
|
|
|
operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) const
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
2016-08-10 07:35:21 -04:00
|
|
|
const auto findNextIntersectionForRoad =
|
2016-09-02 08:06:38 -04:00
|
|
|
[&](const NodeID at_node, const ConnectedRoad &road, NodeID &output_node) {
|
2016-11-03 05:18:27 -04:00
|
|
|
auto intersection = intersection_generator(at_node, road.eid);
|
|
|
|
auto in_edge = road.eid;
|
2016-08-10 07:35:21 -04:00
|
|
|
// skip over traffic lights
|
2016-09-19 06:56:13 -04:00
|
|
|
// 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)
|
2016-08-10 07:35:21 -04:00
|
|
|
{
|
2016-09-19 06:56:13 -04:00
|
|
|
visited_nodes.insert(node);
|
|
|
|
node = node_based_graph.GetTarget(in_edge);
|
2016-09-12 04:08:01 -04:00
|
|
|
if (node == at_node)
|
|
|
|
{
|
|
|
|
// we ended up in a loop without exit
|
|
|
|
output_node = SPECIAL_NODEID;
|
|
|
|
intersection.clear();
|
|
|
|
return intersection;
|
|
|
|
}
|
2016-11-03 05:18:27 -04:00
|
|
|
in_edge = intersection[1].eid;
|
2016-09-02 08:06:38 -04:00
|
|
|
output_node = node_based_graph.GetTarget(in_edge);
|
2016-08-10 07:35:21 -04:00
|
|
|
intersection = intersection_generator(node, in_edge);
|
|
|
|
}
|
2016-09-19 06:56:13 -04:00
|
|
|
if (intersection.size() <= 2)
|
|
|
|
{
|
|
|
|
output_node = SPECIAL_NODEID;
|
|
|
|
intersection.clear();
|
|
|
|
}
|
2016-08-10 07:35:21 -04:00
|
|
|
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;
|
2016-11-03 05:18:27 -04:00
|
|
|
else if (intersection.size() == 3 && intersection[1].instruction.type == TurnType::Fork)
|
2016-08-10 07:35:21 -04:00
|
|
|
{
|
|
|
|
// 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.
|
2016-09-15 02:25:17 -04:00
|
|
|
NodeID intersection_node_one = SPECIAL_NODEID, intersection_node_two = SPECIAL_NODEID;
|
2016-09-02 08:06:38 -04:00
|
|
|
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);
|
2016-09-12 04:08:01 -04:00
|
|
|
// in case of broken roads, we return
|
|
|
|
if (intersection_following_index_one.empty() ||
|
|
|
|
intersection_following_index_two.empty())
|
|
|
|
return 0;
|
2016-09-02 08:06:38 -04:00
|
|
|
|
|
|
|
// 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.
|
2016-08-10 07:35:21 -04:00
|
|
|
|
|
|
|
// a sliproad has to enter a road without choice
|
|
|
|
const auto couldBeSliproad = [](const Intersection &intersection) {
|
|
|
|
if (intersection.size() != 3)
|
|
|
|
return false;
|
|
|
|
if ((intersection[1].entry_allowed && intersection[2].entry_allowed) ||
|
|
|
|
intersection[0].entry_allowed)
|
|
|
|
return false;
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2016-09-02 08:06:38 -04:00
|
|
|
if (couldBeSliproad(intersection_following_index_one) &&
|
|
|
|
intersection_node_id != intersection_node_two)
|
2016-08-10 07:35:21 -04:00
|
|
|
return 2;
|
2016-09-02 08:06:38 -04:00
|
|
|
else if (couldBeSliproad(intersection_following_index_two) &&
|
|
|
|
intersection_node_id != intersection_node_one)
|
2016-08-10 07:35:21 -04:00
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}();
|
|
|
|
|
|
|
|
if (obvious_turn_index == 0)
|
|
|
|
return intersection;
|
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
const auto &next_road = intersection[obvious_turn_index];
|
|
|
|
|
|
|
|
const auto linkTest = [this, next_road](const ConnectedRoad &road) {
|
2016-11-03 05:18:27 -04:00
|
|
|
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) >
|
2016-07-04 06:19:49 -04:00
|
|
|
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) {
|
2016-11-03 05:18:27 -04:00
|
|
|
const auto road_edge_data = node_based_graph.GetEdgeData(road.eid);
|
2016-07-04 06:19:49 -04:00
|
|
|
// Test to see if the source edge and the one we're looking at are the same road
|
2016-09-22 10:42:38 -04:00
|
|
|
|
|
|
|
const auto same_name = !util::guidance::requiresNameAnnounced(
|
|
|
|
name_table.GetNameForID(road_edge_data.name_id),
|
|
|
|
name_table.GetRefForID(road_edge_data.name_id),
|
|
|
|
name_table.GetNameForID(source_edge_data.name_id),
|
|
|
|
name_table.GetRefForID(source_edge_data.name_id),
|
|
|
|
street_name_suffix_table);
|
|
|
|
|
2016-07-26 09:00:58 -04:00
|
|
|
return road_edge_data.road_classification == source_edge_data.road_classification &&
|
2016-09-22 10:42:38 -04:00
|
|
|
road_edge_data.name_id != EMPTY_NAMEID && source_edge_data.name_id != EMPTY_NAMEID &&
|
|
|
|
same_name && road.entry_allowed;
|
2016-07-04 06:19:49 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
if (!check_valid(next_road))
|
|
|
|
return intersection;
|
|
|
|
|
|
|
|
// Threshold check, if the intersection is too far away, don't bother continuing
|
2016-10-20 06:15:36 -04:00
|
|
|
const auto coordinate_extractor = intersection_generator.GetCoordinateExtractor();
|
|
|
|
const auto next_road_length = util::coordinate_calculation::getLength(
|
|
|
|
coordinate_extractor.GetForwardCoordinatesAlongRoad(
|
2016-11-03 05:18:27 -04:00
|
|
|
node_based_graph.GetTarget(source_edge_id), next_road.eid),
|
2016-10-20 06:15:36 -04:00
|
|
|
&util::coordinate_calculation::haversineDistance);
|
|
|
|
if (next_road_length > MAX_SLIPROAD_THRESHOLD)
|
2016-07-04 06:19:49 -04:00
|
|
|
{
|
|
|
|
return intersection;
|
|
|
|
}
|
2016-11-03 05:18:27 -04:00
|
|
|
auto next_intersection_node = node_based_graph.GetTarget(next_road.eid);
|
2016-07-04 06:19:49 -04:00
|
|
|
|
2016-08-10 07:35:21 -04:00
|
|
|
const auto next_road_next_intersection =
|
2016-09-02 08:06:38 -04:00
|
|
|
findNextIntersectionForRoad(intersection_node_id, next_road, next_intersection_node);
|
|
|
|
|
2016-09-19 06:56:13 -04:00
|
|
|
if (next_road_next_intersection.empty())
|
|
|
|
return intersection;
|
|
|
|
|
2016-09-02 08:06:38 -04:00
|
|
|
// 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;
|
2016-07-04 06:19:49 -04:00
|
|
|
|
|
|
|
std::unordered_set<NameID> target_road_names;
|
|
|
|
|
|
|
|
for (const auto &road : next_road_next_intersection)
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
const auto &target_data = node_based_graph.GetEdgeData(road.eid);
|
2016-07-04 06:19:49 -04:00
|
|
|
target_road_names.insert(target_data.name_id);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto &road : intersection)
|
|
|
|
{
|
|
|
|
if (linkTest(road))
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
EdgeID candidate_in = road.eid;
|
2016-07-04 06:19:49 -04:00
|
|
|
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);
|
2016-11-03 05:18:27 -04:00
|
|
|
candidate_in = intersection[1].eid;
|
2016-07-04 06:19:49 -04:00
|
|
|
intersection = intersection_generator(node, candidate_in);
|
|
|
|
}
|
|
|
|
return intersection;
|
|
|
|
}(intersection_node_id);
|
|
|
|
|
2016-11-03 05:18:27 -04:00
|
|
|
const auto link_data = node_based_graph.GetEdgeData(road.eid);
|
2016-09-13 07:17:18 -04:00
|
|
|
// Check if the road continues here
|
|
|
|
const bool is_through_street =
|
2016-10-13 03:59:10 -04:00
|
|
|
!target_intersection.empty() && link_data.name_id != EMPTY_NAMEID &&
|
2016-09-13 07:17:18 -04:00
|
|
|
target_intersection.end() !=
|
2016-09-19 06:56:13 -04:00
|
|
|
std::find_if(target_intersection.begin() + 1,
|
|
|
|
target_intersection.end(),
|
|
|
|
[this, &link_data](const ConnectedRoad &road) {
|
2016-09-22 10:42:38 -04:00
|
|
|
const auto &road_edge_data =
|
|
|
|
node_based_graph.GetEdgeData(road.eid);
|
|
|
|
|
2016-10-13 03:59:10 -04:00
|
|
|
const auto same_name =
|
|
|
|
road_edge_data.name_id != EMPTY_NAMEID &&
|
|
|
|
!util::guidance::requiresNameAnnounced(
|
|
|
|
name_table.GetNameForID(road_edge_data.name_id),
|
|
|
|
name_table.GetRefForID(road_edge_data.name_id),
|
|
|
|
name_table.GetNameForID(link_data.name_id),
|
|
|
|
name_table.GetRefForID(link_data.name_id),
|
|
|
|
street_name_suffix_table);
|
2016-09-22 10:42:38 -04:00
|
|
|
|
|
|
|
return same_name;
|
2016-09-19 06:56:13 -04:00
|
|
|
});
|
2016-09-13 07:17:18 -04:00
|
|
|
|
|
|
|
// if the sliproad candidate is a through street, we cannot handle it as a sliproad
|
|
|
|
if (is_through_street)
|
|
|
|
continue;
|
|
|
|
|
2016-07-04 06:19:49 -04:00
|
|
|
for (const auto &candidate_road : target_intersection)
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.eid);
|
2016-07-04 06:19:49 -04:00
|
|
|
if (target_road_names.count(candidate_data.name_id) > 0)
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
if (node_based_graph.GetTarget(candidate_road.eid) == next_intersection_node)
|
2016-07-04 06:19:49 -04:00
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
road.instruction.type = TurnType::Sliproad;
|
2016-07-04 06:19:49 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-07-26 09:00:58 -04:00
|
|
|
const auto skip_traffic_light_intersection = intersection_generator(
|
2016-11-03 05:18:27 -04:00
|
|
|
node_based_graph.GetTarget(candidate_in), candidate_road.eid);
|
2016-07-04 06:19:49 -04:00
|
|
|
if (skip_traffic_light_intersection.size() == 2 &&
|
2016-11-03 05:18:27 -04:00
|
|
|
node_based_graph.GetTarget(skip_traffic_light_intersection[1].eid) ==
|
2016-07-04 06:19:49 -04:00
|
|
|
next_intersection_node)
|
|
|
|
{
|
|
|
|
|
2016-11-03 05:18:27 -04:00
|
|
|
road.instruction.type = TurnType::Sliproad;
|
2016-07-04 06:19:49 -04:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-03 05:18:27 -04:00
|
|
|
if (next_road.instruction.type == TurnType::Fork)
|
2016-07-04 06:19:49 -04:00
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
const auto &next_data = node_based_graph.GetEdgeData(next_road.eid);
|
2016-09-22 10:42:38 -04:00
|
|
|
|
2016-10-13 03:59:10 -04:00
|
|
|
const auto same_name = next_data.name_id != EMPTY_NAMEID &&
|
|
|
|
source_edge_data.name_id != EMPTY_NAMEID &&
|
|
|
|
!util::guidance::requiresNameAnnounced(
|
|
|
|
name_table.GetNameForID(next_data.name_id),
|
|
|
|
name_table.GetRefForID(next_data.name_id),
|
|
|
|
name_table.GetNameForID(source_edge_data.name_id),
|
|
|
|
name_table.GetRefForID(source_edge_data.name_id),
|
|
|
|
street_name_suffix_table);
|
2016-09-22 10:42:38 -04:00
|
|
|
|
|
|
|
if (same_name)
|
2016-07-04 06:19:49 -04:00
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
if (angularDeviation(next_road.angle, STRAIGHT_ANGLE) < 5)
|
|
|
|
intersection[obvious_turn_index].instruction.type = TurnType::Suppressed;
|
2016-07-04 06:19:49 -04:00
|
|
|
else
|
2016-11-03 05:18:27 -04:00
|
|
|
intersection[obvious_turn_index].instruction.type = TurnType::Continue;
|
|
|
|
intersection[obvious_turn_index].instruction.direction_modifier =
|
|
|
|
getTurnDirection(intersection[obvious_turn_index].angle);
|
2016-07-04 06:19:49 -04:00
|
|
|
}
|
|
|
|
else if (next_data.name_id != EMPTY_NAMEID)
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
intersection[obvious_turn_index].instruction.type = TurnType::NewName;
|
|
|
|
intersection[obvious_turn_index].instruction.direction_modifier =
|
|
|
|
getTurnDirection(intersection[obvious_turn_index].angle);
|
2016-07-04 06:19:49 -04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2016-11-03 05:18:27 -04:00
|
|
|
intersection[obvious_turn_index].instruction.type = TurnType::Suppressed;
|
2016-07-04 06:19:49 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return intersection;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace guidance
|
|
|
|
} // namespace extractor
|
|
|
|
} // namespace osrm
|