improve sliproad / fork handling

This commit is contained in:
Moritz Kobitzsch 2016-07-04 12:19:49 +02:00 committed by Patrick Niklaus
parent 1309dd2a0f
commit 9e323d2d42
No known key found for this signature in database
GPG Key ID: E426891B5F978B1B
17 changed files with 558 additions and 426 deletions

View File

@ -167,3 +167,34 @@ Feature: Slipways and Dedicated Turn Lanes
When I route I should get
| waypoints | route | turns |
| a,o | Schwarzwaldstrasse (L561),Ettlinger Allee,Ettlinger Allee | depart,turn right,arrive |
Scenario: Traffic Lights everywhere
#http://map.project-osrm.org/?z=18&center=48.995336%2C8.383813&loc=48.995467%2C8.384548&loc=48.995115%2C8.382761&hl=en&alt=0
Given the node map
| a | | | k | l | | | j | |
| | | | | | d | b | c | i |
| | | | | | | | | |
| | | | | | | e | g | |
| | | | | | | | | |
| | | | | | | | | |
| | | | | | | | h | |
| | | | | | | | | |
| | | | | | | | f | |
And the nodes
| node | highway |
| b | traffic_signals |
| e | traffic_signals |
| g | traffic_signals |
And the ways
| nodes | highway | name | oneway |
| aklbci | secondary | Ebertstrasse | yes |
| kdeh | secondary_link | | yes |
| jcghf | primary | Brauerstrasse | yes |
When I route I should get
| waypoints | route | turns |
| a,i | Ebertstrasse,Ebertstrasse | depart,arrive |
| a,l | Ebertstrasse,Ebertstrasse | depart,arrive |
| a,f | Ebertstrasse,Brauerstrasse,Brauerstrasse | depart,turn right,arrive |

View File

@ -29,7 +29,8 @@ class IntersectionHandler
const std::vector<QueryNode> &node_info_list,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
virtual ~IntersectionHandler();
virtual ~IntersectionHandler() = default;
// check whether the handler can actually handle the intersection
virtual bool
@ -51,6 +52,9 @@ class IntersectionHandler
// Decide on a basic turn types
TurnType::Enum findBasicTurnType(const EdgeID via_edge, const ConnectedRoad &candidate) const;
// Find the most obvious turn to follow
std::size_t findObviousTurn(const EdgeID via_edge, const Intersection &intersection) const;
// Get the Instruction for an obvious turn
TurnInstruction getInstructionForObvious(const std::size_t number_of_candidates,
const EdgeID via_edge,

View File

@ -10,11 +10,6 @@ namespace extractor
namespace guidance
{
// possible fork
bool isFork(const ConnectedRoad &uturn,
const ConnectedRoad &possible_right_fork,
const ConnectedRoad &possible_left_fork);
// Ending in a T-Intersection
bool isEndOfRoad(const ConnectedRoad &uturn,
const ConnectedRoad &possible_right_turn,

View File

@ -26,7 +26,8 @@ class MotorwayHandler : public IntersectionHandler
const std::vector<QueryNode> &node_info_list,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
~MotorwayHandler() override final;
~MotorwayHandler() override final = default;
// check whether the handler can actually handle the intersection
bool canProcess(const NodeID nid,

View File

@ -44,7 +44,7 @@ class RoundaboutHandler : public IntersectionHandler
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
~RoundaboutHandler() override final;
~RoundaboutHandler() override final = default;
// check whether the handler can actually handle the intersection
bool canProcess(const NodeID from_nid,

View File

@ -0,0 +1,53 @@
#ifndef OSRM_EXTRACTOR_GUIDANCE_SLIPROAD_HANDLER_HPP_
#define OSRM_EXTRACTOR_GUIDANCE_SLIPROAD_HANDLER_HPP_
#include "extractor/guidance/intersection.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/query_node.hpp"
#include "util/name_table.hpp"
#include "util/node_based_graph.hpp"
#include <cstddef>
#include <utility>
#include <vector>
namespace osrm
{
namespace extractor
{
namespace guidance
{
// Intersection handlers deal with all issues related to intersections.
// They assign appropriate turn operations to the TurnOperations.
class SliproadHandler : public IntersectionHandler
{
public:
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);
~SliproadHandler() override final = default;
// check whether the handler can actually handle the intersection
bool canProcess(const NodeID /*nid*/,
const EdgeID /*via_eid*/,
const Intersection & /*intersection*/) const override final;
// process the intersection
Intersection operator()(const NodeID nid,
const EdgeID via_eid,
Intersection intersection) const override final;
private:
const IntersectionGenerator &intersection_generator;
};
} // namespace guidance
} // namespace extractor
} // namespace osrm
#endif /*OSRM_EXTRACTOR_GUIDANCE_SLIPROAD_HANDLER_HPP_*/

View File

@ -428,7 +428,7 @@ inline int getPriority(const FunctionalRoadClass road_class)
// They are used in Fork-Discovery. Possibly should be moved to profiles post v5?
// A fork can happen between road types that are at most 1 priority apart from each other
const constexpr int road_priority[] = {
10, 0, 10, 2, 10, 4, 10, 6, 10, 8, 10, 11, 10, 12, 10, 14};
14, 0, 14, 2, 14, 4, 14, 6, 14, 8, 10, 11, 10, 12, 10, 14};
return road_priority[static_cast<int>(road_class)];
}

View File

@ -6,6 +6,7 @@
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/motorway_handler.hpp"
#include "extractor/guidance/roundabout_handler.hpp"
#include "extractor/guidance/sliproad_handler.hpp"
#include "extractor/guidance/toolkit.hpp"
#include "extractor/guidance/turn_classification.hpp"
#include "extractor/guidance/turn_handler.hpp"
@ -59,13 +60,11 @@ class TurnAnalysis
const RoundaboutHandler roundabout_handler;
const MotorwayHandler motorway_handler;
const TurnHandler turn_handler;
const SliproadHandler sliproad_handler;
// Utility function, setting basic turn types. Prepares for normal turn handling.
Intersection
setTurnTypes(const NodeID from, const EdgeID via_edge, Intersection intersection) const;
Intersection handleSliproads(const NodeID intersection_node_id,
Intersection intersection) const;
}; // class TurnAnalysis
} // namespace guidance

View File

@ -28,7 +28,8 @@ class TurnHandler : public IntersectionHandler
const std::vector<QueryNode> &node_info_list,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table);
~TurnHandler() override final;
~TurnHandler() override final = default;
// check whether the handler can actually handle the intersection
bool canProcess(const NodeID nid,
@ -41,6 +42,9 @@ class TurnHandler : public IntersectionHandler
Intersection intersection) const override final;
private:
bool isObviousOfTwo(const EdgeID via_edge,
const ConnectedRoad &road,
const ConnectedRoad &other) const;
// Dead end.
Intersection handleOneWayTurn(Intersection intersection) const;
@ -57,8 +61,7 @@ class TurnHandler : public IntersectionHandler
handleDistinctConflict(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad &right) const;
// Classification
std::size_t findObviousTurn(const EdgeID via_edge, const Intersection &intersection) const;
std::pair<std::size_t, std::size_t> findFork(const Intersection &intersection) const;
std::pair<std::size_t, std::size_t> findFork(const EdgeID via_edge, const Intersection &intersection) const;
Intersection assignLeftTurns(const EdgeID via_edge,
Intersection intersection,

View File

@ -719,6 +719,16 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
// Handle sliproads from motorways in urban areas, save from modifying depart, since
// TurnType::Sliproad != TurnType::NoTurn
if (one_back_step.maneuver.instruction.type == TurnType::Sliproad)
{
if (current_step.maneuver.instruction.type == TurnType::Suppressed &&
compatible(one_back_step, current_step))
{
// Traffic light on the sliproad
steps[one_back_index] =
elongate(std::move(steps[one_back_index]), steps[step_index]);
invalidateStep(steps[step_index]);
}
else
{
// Handle possible u-turns between highways that look like slip-roads
if (steps[getPreviousIndex(one_back_index)].name_id == steps[step_index].name_id &&
@ -750,6 +760,7 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
invalidateStep(steps[step_index]);
}
}
}
// Due to empty segments, we can get name-changes from A->A
// These have to be handled in post-processing
else if (isCollapsableInstruction(current_step.maneuver.instruction) &&

View File

@ -1,5 +1,5 @@
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/intersection_handler.hpp"
#include "extractor/guidance/toolkit.hpp"
#include "util/guidance/toolkit.hpp"
@ -34,8 +34,6 @@ IntersectionHandler::IntersectionHandler(const util::NodeBasedDynamicGraph &node
{
}
IntersectionHandler::~IntersectionHandler() = default;
std::size_t IntersectionHandler::countValid(const Intersection &intersection) const
{
return std::count_if(intersection.begin(), intersection.end(), [](const ConnectedRoad &road) {
@ -322,6 +320,115 @@ bool IntersectionHandler::isThroughStreet(const std::size_t index,
return false;
}
std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
const Intersection &intersection) const
{
// no obvious road
if (intersection.size() == 1)
return 0;
// a single non u-turn is obvious
if (intersection.size() == 2)
return 1;
// at least three roads
std::size_t best = 0;
double best_deviation = 180;
std::size_t best_continue = 0;
double best_continue_deviation = 180;
const EdgeData &in_data = node_based_graph.GetEdgeData(via_edge);
for (std::size_t i = 1; i < intersection.size(); ++i)
{
const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
if (intersection[i].entry_allowed && deviation < best_deviation)
{
best_deviation = deviation;
best = i;
}
const auto out_data = node_based_graph.GetEdgeData(intersection[i].turn.eid);
auto continue_class = node_based_graph.GetEdgeData(intersection[best_continue].turn.eid)
.road_classification.road_class;
if (intersection[i].entry_allowed && out_data.name_id == in_data.name_id &&
(best_continue == 0 || continue_class > out_data.road_classification.road_class ||
(deviation < best_continue_deviation &&
out_data.road_classification.road_class == continue_class)))
{
best_continue_deviation = deviation;
best_continue = i;
}
}
if (best == 0)
return 0;
if (best_deviation >= 2 * NARROW_TURN_ANGLE)
return 0;
// has no obvious continued road
if (best_continue == 0 || best_continue_deviation >= 2 * NARROW_TURN_ANGLE ||
(node_based_graph.GetEdgeData(intersection[best_continue].turn.eid)
.road_classification.road_class ==
node_based_graph.GetEdgeData(intersection[best].turn.eid)
.road_classification.road_class &&
std::abs(best_continue_deviation) > 1 && best_deviation / best_continue_deviation < 0.75))
{
// Find left/right deviation
const double left_deviation = angularDeviation(
intersection[(best + 1) % intersection.size()].turn.angle, STRAIGHT_ANGLE);
const double right_deviation =
angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE);
if (best_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
std::min(left_deviation, right_deviation) > FUZZY_ANGLE_DIFFERENCE)
return best;
// other narrow turns?
if (angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE) <=
FUZZY_ANGLE_DIFFERENCE)
return 0;
if (angularDeviation(intersection[(best + 1) % intersection.size()].turn.angle,
STRAIGHT_ANGLE) <= FUZZY_ANGLE_DIFFERENCE)
return 0;
// Well distinct turn that is nearly straight
if ((left_deviation / best_deviation >= DISTINCTION_RATIO ||
(left_deviation > best_deviation &&
!intersection[(best + 1) % intersection.size()].entry_allowed)) &&
(right_deviation / best_deviation >= DISTINCTION_RATIO ||
(right_deviation > best_deviation && !intersection[best - 1].entry_allowed)))
{
return best;
}
}
else
{
const double deviation =
angularDeviation(intersection[best_continue].turn.angle, STRAIGHT_ANGLE);
const auto &continue_data =
node_based_graph.GetEdgeData(intersection[best_continue].turn.eid);
if (std::abs(deviation) < 1)
return best_continue;
// check if any other similar best continues exist
for (std::size_t i = 1; i < intersection.size(); ++i)
{
if (i == best_continue || !intersection[i].entry_allowed)
continue;
if (angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE) / deviation < 1.1 &&
continue_data.road_classification.road_class ==
node_based_graph.GetEdgeData(intersection[i].turn.eid)
.road_classification.road_class)
return 0;
}
return best_continue; // no obvious turn
}
return 0;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -13,14 +13,6 @@ namespace extractor
namespace guidance
{
bool isFork(const ConnectedRoad &,
const ConnectedRoad &possible_right_fork,
const ConnectedRoad &possible_left_fork)
{
return angularDeviation(possible_right_fork.turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE &&
angularDeviation(possible_left_fork.turn.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE;
}
bool isEndOfRoad(const ConnectedRoad &,
const ConnectedRoad &possible_right_turn,
const ConnectedRoad &possible_left_turn)

View File

@ -47,8 +47,6 @@ MotorwayHandler::MotorwayHandler(const util::NodeBasedDynamicGraph &node_based_g
{
}
MotorwayHandler::~MotorwayHandler() = default;
bool MotorwayHandler::canProcess(const NodeID,
const EdgeID via_eid,
const Intersection &intersection) const

View File

@ -31,8 +31,6 @@ RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_bas
{
}
RoundaboutHandler::~RoundaboutHandler() = default;
bool RoundaboutHandler::canProcess(const NodeID from_nid,
const EdgeID via_eid,
const Intersection &intersection) const

View File

@ -0,0 +1,192 @@
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/intersection_scenario_three_way.hpp"
#include "extractor/guidance/sliproad_handler.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)
: IntersectionHandler(node_based_graph, node_info_list, name_table, street_name_suffix_table),
intersection_generator(intersection_generator)
{
}
// included for interface reasons only
bool SliproadHandler::canProcess(const NodeID /*nid*/,
const EdgeID /*via_eid*/,
const Intersection & /*intersection*/) const
{
return true;
}
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;
const auto obvious_turn_index = findObviousTurn(source_edge_id, intersection);
const auto &next_road = intersection[obvious_turn_index];
const auto linkTest = [this, next_road](const ConnectedRoad &road) {
return !node_based_graph.GetEdgeData(road.turn.eid).roundabout && road.entry_allowed &&
angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <= 2 * NARROW_TURN_ANGLE &&
!hasRoundaboutType(road.turn.instruction) &&
angularDeviation(next_road.turn.angle, road.turn.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);
const bool hasNext = obvious_turn_index != 0;
if (!hasNext)
return intersection;
// 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.turn.eid);
// Test to see if the source edge and the one we're looking at are the same road
return road_edge_data.road_classification.road_class ==
source_edge_data.road_classification.road_class &&
road_edge_data.name_id != EMPTY_NAMEID &&
road_edge_data.name_id == source_edge_data.name_id && 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 &next_road_data = node_based_graph.GetEdgeData(next_road.turn.eid);
if (next_road_data.distance > MAX_SLIPROAD_THRESHOLD)
{
return intersection;
}
auto next_intersection_node = node_based_graph.GetTarget(next_road.turn.eid);
const auto next_road_next_intersection = [&]() {
auto intersection = intersection_generator(intersection_node_id, next_road.turn.eid);
auto in_edge = next_road.turn.eid;
// skip over traffic lights
if (intersection.size() == 2)
{
const auto node = node_based_graph.GetTarget(in_edge);
in_edge = intersection[1].turn.eid;
next_intersection_node = node_based_graph.GetTarget(in_edge);
intersection = intersection_generator(node, in_edge);
}
return intersection;
}();
std::unordered_set<NameID> target_road_names;
for (const auto &road : next_road_next_intersection)
{
const auto &target_data = node_based_graph.GetEdgeData(road.turn.eid);
target_road_names.insert(target_data.name_id);
}
for (auto &road : intersection)
{
if (linkTest(road))
{
EdgeID candidate_in = road.turn.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].turn.eid;
intersection = intersection_generator(node, candidate_in);
}
return intersection;
}(intersection_node_id);
for (const auto &candidate_road : target_intersection)
{
const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.turn.eid);
if (target_road_names.count(candidate_data.name_id) > 0)
{
if (node_based_graph.GetTarget(candidate_road.turn.eid) ==
next_intersection_node)
{
road.turn.instruction.type = TurnType::Sliproad;
break;
}
else
{
const auto skip_traffic_light_intersection =
intersection_generator(node_based_graph.GetTarget(candidate_in), candidate_road.turn.eid);
if (skip_traffic_light_intersection.size() == 2 &&
node_based_graph.GetTarget(
skip_traffic_light_intersection[1].turn.eid) ==
next_intersection_node)
{
road.turn.instruction.type = TurnType::Sliproad;
break;
}
}
}
}
}
}
if (next_road.turn.instruction.type == TurnType::Fork)
{
const auto &next_data = node_based_graph.GetEdgeData(next_road.turn.eid);
if (next_data.name_id == source_edge_data.name_id)
{
if (angularDeviation(next_road.turn.angle, STRAIGHT_ANGLE) < 5)
intersection[obvious_turn_index].turn.instruction.type = TurnType::Suppressed;
else
intersection[obvious_turn_index].turn.instruction.type = TurnType::Continue;
intersection[obvious_turn_index].turn.instruction.direction_modifier =
getTurnDirection(intersection[obvious_turn_index].turn.angle);
}
else if (next_data.name_id != EMPTY_NAMEID)
{
intersection[obvious_turn_index].turn.instruction.type = TurnType::NewName;
intersection[obvious_turn_index].turn.instruction.direction_modifier =
getTurnDirection(intersection[obvious_turn_index].turn.angle);
}
else
{
intersection[obvious_turn_index].turn.instruction.type = TurnType::Suppressed;
}
}
return intersection;
}
} // namespace guidance
} // namespace extractor
} // namespace osrm

View File

@ -49,7 +49,12 @@ TurnAnalysis::TurnAnalysis(const util::NodeBasedDynamicGraph &node_based_graph,
name_table,
street_name_suffix_table),
motorway_handler(node_based_graph, node_info_list, name_table, street_name_suffix_table),
turn_handler(node_based_graph, node_info_list, name_table, street_name_suffix_table)
turn_handler(node_based_graph, node_info_list, name_table, street_name_suffix_table),
sliproad_handler(intersection_generator,
node_based_graph,
node_info_list,
name_table,
street_name_suffix_table)
{
}
@ -78,7 +83,7 @@ Intersection TurnAnalysis::assignTurnTypes(const NodeID from_nid,
}
}
// Handle sliproads
intersection = handleSliproads(via_eid, std::move(intersection));
intersection = sliproad_handler(from_nid, via_eid, std::move(intersection));
// Turn On Ramps Into Off Ramps, if we come from a motorway-like road
if (isMotorwayClass(node_based_graph.GetEdgeData(via_eid).road_classification.road_class))
@ -127,135 +132,6 @@ TurnAnalysis::setTurnTypes(const NodeID from_nid, const EdgeID, Intersection int
return intersection;
}
// "Sliproads" occur when we've got a link between two roads (MOTORWAY_LINK, etc), but
// the two roads are *also* directly connected shortly afterwards.
// In these cases, we tag the turn-type as "sliproad", and then in post-processing
// we emit a "turn", instead of "take the ramp"+"merge"
Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id,
Intersection intersection) const
{
auto intersection_node_id = node_based_graph.GetTarget(source_edge_id);
const auto linkTest = [this](const ConnectedRoad &road) {
return !node_based_graph.GetEdgeData(road.turn.eid).roundabout && road.entry_allowed &&
angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <= 2 * NARROW_TURN_ANGLE &&
!hasRoundaboutType(road.turn.instruction);
};
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);
// Find the continuation of the intersection we're on
auto next_road = std::find_if(
intersection.begin(),
intersection.end(),
[this, source_edge_data](const ConnectedRoad &road) {
const auto road_edge_data = node_based_graph.GetEdgeData(road.turn.eid);
// Test to see if the source edge and the one we're looking at are the same road
return road_edge_data.road_classification.road_class ==
source_edge_data.road_classification.road_class &&
road_edge_data.name_id != EMPTY_NAMEID &&
road_edge_data.name_id == source_edge_data.name_id && road.entry_allowed &&
angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE;
});
const bool hasNext = next_road != intersection.end();
if (!hasNext)
{
return intersection;
}
// Threshold check, if the intersection is too far away, don't bother continuing
const auto &next_road_data = node_based_graph.GetEdgeData(next_road->turn.eid);
if (next_road_data.distance > MAX_SLIPROAD_THRESHOLD)
{
return intersection;
}
auto next_intersection_node = node_based_graph.GetTarget(next_road->turn.eid);
const auto next_road_next_intersection = [&]() {
auto intersection = intersection_generator(intersection_node_id, next_road->turn.eid);
auto in_edge = next_road->turn.eid;
//skip over traffic lights
if(intersection.size() == 2)
{
const auto node = node_based_graph.GetTarget(in_edge);
in_edge = intersection[1].turn.eid;
next_intersection_node = node_based_graph.GetTarget(in_edge);
intersection = intersection_generator(node, in_edge);
}
return intersection;
}();
std::unordered_set<NameID> target_road_names;
for (const auto &road : next_road_next_intersection)
{
const auto &target_data = node_based_graph.GetEdgeData(road.turn.eid);
target_road_names.insert(target_data.name_id);
}
for (auto &road : intersection)
{
if (linkTest(road))
{
const auto target_intersection = [&](NodeID node, EdgeID eid) {
auto intersection = intersection_generator(node, eid);
//skip over traffic lights
if(intersection.size() == 2)
{
node = node_based_graph.GetTarget(eid);
eid = intersection[1].turn.eid;
intersection = intersection_generator(node, eid);
}
return intersection;
}(intersection_node_id, road.turn.eid);
for (const auto &candidate_road : target_intersection)
{
const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.turn.eid);
if (target_road_names.count(candidate_data.name_id) > 0 &&
node_based_graph.GetTarget(candidate_road.turn.eid) == next_intersection_node)
{
road.turn.instruction.type = TurnType::Sliproad;
break;
}
}
}
}
if (next_road->turn.instruction.type == TurnType::Fork)
{
const auto &next_data = node_based_graph.GetEdgeData(next_road->turn.eid);
if (next_data.name_id == source_edge_data.name_id)
{
if (angularDeviation(next_road->turn.angle, STRAIGHT_ANGLE) < 5)
next_road->turn.instruction.type = TurnType::Suppressed;
else
next_road->turn.instruction.type = TurnType::Continue;
next_road->turn.instruction.direction_modifier =
getTurnDirection(next_road->turn.angle);
}
else if (next_data.name_id != EMPTY_NAMEID)
{
next_road->turn.instruction.type = TurnType::NewName;
next_road->turn.instruction.direction_modifier =
getTurnDirection(next_road->turn.angle);
}
else
{
next_road->turn.instruction.type = TurnType::Suppressed;
}
}
return intersection;
}
const IntersectionGenerator &TurnAnalysis::getGenerator() const { return intersection_generator; }
} // namespace guidance

View File

@ -29,32 +29,30 @@ TurnHandler::TurnHandler(const util::NodeBasedDynamicGraph &node_based_graph,
{
}
TurnHandler::~TurnHandler() = default;
bool TurnHandler::canProcess(const NodeID, const EdgeID, const Intersection &) const
{
return true;
}
Intersection TurnHandler::
operator()(const NodeID, const EdgeID via_eid, Intersection intersection) const
operator()(const NodeID, const EdgeID via_edge, Intersection intersection) const
{
if (intersection.size() == 1)
return handleOneWayTurn(std::move(intersection));
if (intersection[0].entry_allowed)
{
intersection[0].turn.instruction = {findBasicTurnType(via_eid, intersection[0]),
intersection[0].turn.instruction = {findBasicTurnType(via_edge, intersection[0]),
DirectionModifier::UTurn};
}
if (intersection.size() == 2)
return handleTwoWayTurn(via_eid, std::move(intersection));
return handleTwoWayTurn(via_edge, std::move(intersection));
if (intersection.size() == 3)
return handleThreeWayTurn(via_eid, std::move(intersection));
return handleThreeWayTurn(via_edge, std::move(intersection));
return handleComplexTurn(via_eid, std::move(intersection));
return handleComplexTurn(via_edge, std::move(intersection));
}
Intersection TurnHandler::handleOneWayTurn(Intersection intersection) const
@ -72,14 +70,11 @@ Intersection TurnHandler::handleTwoWayTurn(const EdgeID via_edge, Intersection i
return intersection;
}
Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const
bool TurnHandler::isObviousOfTwo(const EdgeID via_edge,
const ConnectedRoad &road,
const ConnectedRoad &other) const
{
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
const auto &first_data = node_based_graph.GetEdgeData(intersection[1].turn.eid);
const auto &second_data = node_based_graph.GetEdgeData(intersection[2].turn.eid);
BOOST_ASSERT(intersection[0].turn.angle < 0.001);
const auto isObviousOfTwo = [this, in_data](const ConnectedRoad road,
const ConnectedRoad other) {
const auto first_class =
node_based_graph.GetEdgeData(road.turn.eid).road_classification.road_class;
@ -96,16 +91,15 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
return true;
const bool other_is_obvious_by_road_class =
(!isRampClass(second_class) &&
(2 * getPriority(second_class) < getPriority(first_class)) &&
(!isRampClass(second_class) && (2 * getPriority(second_class) < getPriority(first_class)) &&
in_data.road_classification.road_class == second_class) ||
(!isLowPriorityRoadClass(second_class) && isLowPriorityRoadClass(first_class));
if (other_is_obvious_by_road_class)
return false;
const bool turn_is_perfectly_straight = angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <
std::numeric_limits<double>::epsilon();
const bool turn_is_perfectly_straight =
angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < std::numeric_limits<double>::epsilon();
if (turn_is_perfectly_straight && in_data.name_id != EMPTY_NAMEID &&
in_data.name_id == node_based_graph.GetEdgeData(road.turn.eid).name_id)
@ -120,8 +114,14 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
FUZZY_ANGLE_DIFFERENCE;
return is_much_narrower_than_other;
};
};
Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection intersection) const
{
const auto &in_data = node_based_graph.GetEdgeData(via_edge);
const auto &first_data = node_based_graph.GetEdgeData(intersection[1].turn.eid);
const auto &second_data = node_based_graph.GetEdgeData(intersection[2].turn.eid);
BOOST_ASSERT(intersection[0].turn.angle < 0.001);
/* Two nearly straight turns -> FORK
OOOOOOO
/
@ -129,54 +129,10 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
\
OOOOOOO
*/
if (isFork(intersection[0], intersection[1], intersection[2]))
{
if (intersection[1].entry_allowed && intersection[2].entry_allowed)
{
const auto left_class = node_based_graph.GetEdgeData(intersection[2].turn.eid)
.road_classification.road_class;
const auto right_class = node_based_graph.GetEdgeData(intersection[1].turn.eid)
.road_classification.road_class;
if (isObviousOfTwo(intersection[1], intersection[2]) &&
(second_data.name_id != in_data.name_id ||
first_data.name_id == second_data.name_id))
{
intersection[1].turn.instruction =
getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]);
intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
DirectionModifier::SlightLeft};
}
else if (isObviousOfTwo(intersection[2], intersection[1]) &&
(first_data.name_id != in_data.name_id ||
first_data.name_id == second_data.name_id))
{
intersection[2].turn.instruction =
getInstructionForObvious(intersection.size(), via_edge, false, intersection[2]);
intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
DirectionModifier::SlightRight};
}
else if (canBeSeenAsFork(left_class, right_class))
{
const auto fork_range = findFork(via_edge, intersection);
if (fork_range.first == 1 && fork_range.second == 2)
assignFork(via_edge, intersection[2], intersection[1]);
}
else
{
intersection[1].turn.instruction = {findBasicTurnType(via_edge, intersection[1]),
DirectionModifier::SlightRight};
intersection[2].turn.instruction = {findBasicTurnType(via_edge, intersection[2]),
DirectionModifier::SlightLeft};
}
}
else
{
if (intersection[1].entry_allowed)
intersection[1].turn.instruction =
getInstructionForObvious(intersection.size(), via_edge, false, intersection[1]);
if (intersection[2].entry_allowed)
intersection[2].turn.instruction =
getInstructionForObvious(intersection.size(), via_edge, false, intersection[2]);
}
}
/* T Intersection
OOOOOOO T OOOOOOOO
@ -185,8 +141,8 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
I
*/
else if (isEndOfRoad(intersection[0], intersection[1], intersection[2]) &&
!isObviousOfTwo(intersection[1], intersection[2]) &&
!isObviousOfTwo(intersection[2], intersection[1]))
!isObviousOfTwo(via_edge, intersection[1], intersection[2]) &&
!isObviousOfTwo(via_edge, intersection[2], intersection[1]))
{
if (intersection[1].entry_allowed)
{
@ -206,7 +162,7 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
}
else
{
if (isObviousOfTwo(intersection[1], intersection[2]) &&
if (isObviousOfTwo(via_edge, intersection[1], intersection[2]) &&
(in_data.name_id != second_data.name_id || first_data.name_id == second_data.name_id))
{
intersection[1].turn.instruction = getInstructionForObvious(
@ -218,7 +174,7 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
getTurnDirection(intersection[1].turn.angle)};
}
if (isObviousOfTwo(intersection[2], intersection[1]) &&
if (isObviousOfTwo(via_edge, intersection[2], intersection[1]) &&
(in_data.name_id != first_data.name_id || first_data.name_id == second_data.name_id))
{
intersection[2].turn.instruction = getInstructionForObvious(
@ -236,7 +192,7 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection intersection) const
{
const std::size_t obvious_index = findObviousTurn(via_edge, intersection);
const auto fork_range = findFork(intersection);
const auto fork_range = findFork(via_edge, intersection);
std::size_t straightmost_turn = 0;
double straightmost_deviation = 180;
for (std::size_t i = 0; i < intersection.size(); ++i)
@ -358,119 +314,6 @@ Intersection TurnHandler::handleComplexTurn(const EdgeID via_edge, Intersection
return intersection;
}
std::size_t TurnHandler::findObviousTurn(const EdgeID via_edge,
const Intersection &intersection) const
{
// no obvious road
if (intersection.size() == 1)
return 0;
// a single non u-turn is obvious
if (intersection.size() == 2)
return 1;
// at least three roads
std::size_t best = 0;
double best_deviation = 180;
std::size_t best_continue = 0;
double best_continue_deviation = 180;
const EdgeData &in_data = node_based_graph.GetEdgeData(via_edge);
const auto in_class = in_data.road_classification.road_class;
for (std::size_t i = 1; i < intersection.size(); ++i)
{
const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
if (intersection[i].entry_allowed && deviation < best_deviation)
{
best_deviation = deviation;
best = i;
}
const auto out_data = node_based_graph.GetEdgeData(intersection[i].turn.eid);
auto continue_class = node_based_graph.GetEdgeData(intersection[best_continue].turn.eid)
.road_classification.road_class;
if (intersection[i].entry_allowed && out_data.name_id == in_data.name_id &&
(best_continue == 0 || (continue_class > out_data.road_classification.road_class &&
in_class != continue_class) ||
(deviation < best_continue_deviation &&
(out_data.road_classification.road_class == continue_class ||
in_class == out_data.road_classification.road_class))))
{
best_continue_deviation = deviation;
best_continue = i;
}
}
if (best == 0)
return 0;
if (best_deviation >= 2 * NARROW_TURN_ANGLE)
return 0;
// has no obvious continued road
if (best_continue == 0 || best_continue_deviation >= 2 * NARROW_TURN_ANGLE ||
(node_based_graph.GetEdgeData(intersection[best_continue].turn.eid)
.road_classification.road_class ==
node_based_graph.GetEdgeData(intersection[best].turn.eid)
.road_classification.road_class &&
std::abs(best_continue_deviation) > 1 && best_deviation / best_continue_deviation < 0.75))
{
// Find left/right deviation
const double left_deviation = angularDeviation(
intersection[(best + 1) % intersection.size()].turn.angle, STRAIGHT_ANGLE);
const double right_deviation =
angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE);
if (best_deviation < MAXIMAL_ALLOWED_NO_TURN_DEVIATION &&
std::min(left_deviation, right_deviation) > FUZZY_ANGLE_DIFFERENCE)
return best;
// other narrow turns?
if (angularDeviation(intersection[best - 1].turn.angle, STRAIGHT_ANGLE) <=
FUZZY_ANGLE_DIFFERENCE)
return 0;
if (angularDeviation(intersection[(best + 1) % intersection.size()].turn.angle,
STRAIGHT_ANGLE) <= FUZZY_ANGLE_DIFFERENCE)
return 0;
// Well distinct turn that is nearly straight
if ((left_deviation / best_deviation >= DISTINCTION_RATIO ||
(left_deviation > best_deviation &&
!intersection[(best + 1) % intersection.size()].entry_allowed)) &&
(right_deviation / best_deviation >= DISTINCTION_RATIO ||
(right_deviation > best_deviation && !intersection[best - 1].entry_allowed)))
{
return best;
}
}
else
{
const double deviation =
angularDeviation(intersection[best_continue].turn.angle, STRAIGHT_ANGLE);
const auto &continue_data =
node_based_graph.GetEdgeData(intersection[best_continue].turn.eid);
if (std::abs(deviation) < 1)
return best_continue;
// check if any other similar best continues exist
for (std::size_t i = 1; i < intersection.size(); ++i)
{
if (i == best_continue || !intersection[i].entry_allowed)
continue;
if (angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE) / deviation < 1.1 &&
continue_data.road_classification.road_class ==
node_based_graph.GetEdgeData(intersection[i].turn.eid)
.road_classification.road_class)
return 0;
}
return best_continue; // no obvious turn
}
return 0;
}
// Assignment of left turns hands of to right turns.
// To do so, we mirror every road segment and reverse the order.
// After the mirror and reversal / we assign right turns and
@ -631,7 +474,8 @@ Intersection TurnHandler::assignRightTurns(const EdgeID via_edge,
return intersection;
}
std::pair<std::size_t, std::size_t> TurnHandler::findFork(const Intersection &intersection) const
std::pair<std::size_t, std::size_t> TurnHandler::findFork(const EdgeID via_edge,
const Intersection &intersection) const
{
std::size_t best = 0;
@ -650,39 +494,67 @@ std::pair<std::size_t, std::size_t> TurnHandler::findFork(const Intersection &in
if (best_deviation <= NARROW_TURN_ANGLE)
{
std::size_t left = best, right = best;
if (intersection[best].turn.angle >= 180)
{
// due to best > 1, we can safely decrement right
--right;
if (angularDeviation(intersection[right].turn.angle, STRAIGHT_ANGLE) >
NARROW_TURN_ANGLE)
return std::make_pair(std::size_t{0}, std::size_t{0});
}
else
{
++left;
if (left >= intersection.size() ||
angularDeviation(intersection[left].turn.angle, STRAIGHT_ANGLE) > NARROW_TURN_ANGLE)
return std::make_pair(std::size_t{0}, std::size_t{0});
}
while (left + 1 < intersection.size() &&
angularDeviation(intersection[left].turn.angle, intersection[left + 1].turn.angle) <
NARROW_TURN_ANGLE &&
angularDeviation(intersection[left].turn.angle, STRAIGHT_ANGLE) <= GROUP_ANGLE)
(angularDeviation(intersection[left + 1].turn.angle, STRAIGHT_ANGLE) <=
NARROW_TURN_ANGLE ||
(angularDeviation(intersection[left].turn.angle,
intersection[left + 1].turn.angle) <= NARROW_TURN_ANGLE &&
angularDeviation(intersection[left].turn.angle, STRAIGHT_ANGLE) <= GROUP_ANGLE)))
++left;
while (right > 1 &&
angularDeviation(intersection[right].turn.angle,
intersection[right - 1].turn.angle) < NARROW_TURN_ANGLE &&
angularDeviation(intersection[right - 1].turn.angle, STRAIGHT_ANGLE) <= GROUP_ANGLE)
while (
right > 1 &&
(angularDeviation(intersection[right - 1].turn.angle, STRAIGHT_ANGLE) <=
NARROW_TURN_ANGLE ||
(angularDeviation(intersection[right].turn.angle, intersection[right - 1].turn.angle) <
NARROW_TURN_ANGLE &&
angularDeviation(intersection[right - 1].turn.angle, STRAIGHT_ANGLE) <= GROUP_ANGLE)))
--right;
// TODO check whether 2*NARROW_TURN is too large
if (0 < right && right < left &&
if (left == right)
return std::make_pair(std::size_t{0}, std::size_t{0});
const bool valid_indices = 0 < right && right < left;
const bool separated_at_left_side =
angularDeviation(intersection[left].turn.angle,
intersection[(left + 1) % intersection.size()].turn.angle) >=
GROUP_ANGLE &&
GROUP_ANGLE;
const bool separated_at_right_side =
right > 0 &&
angularDeviation(intersection[right].turn.angle, intersection[right - 1].turn.angle) >=
GROUP_ANGLE)
GROUP_ANGLE;
const bool not_more_than_three = (left - right) <= 2;
const bool has_obvious = [&]() {
if (left - right == 1)
{
return isObviousOfTwo(via_edge, intersection[left], intersection[right]) ||
isObviousOfTwo(via_edge, intersection[right], intersection[left]);
}
else if (left - right == 2)
{
return isObviousOfTwo(via_edge, intersection[right + 1], intersection[right]) ||
isObviousOfTwo(via_edge, intersection[right], intersection[right + 1]) ||
isObviousOfTwo(via_edge, intersection[left], intersection[right + 1]) ||
isObviousOfTwo(via_edge, intersection[right + 1], intersection[left]);
}
return false;
}();
const bool has_compatible_classes = [&]() {
const bool ramp_class =
isLinkClass(node_based_graph.GetEdgeData(intersection[right].turn.eid)
.road_classification.road_class);
for (std::size_t index = right + 1; index <= left; ++index)
if (ramp_class !=
isLinkClass(node_based_graph.GetEdgeData(intersection[index].turn.eid)
.road_classification.road_class))
return false;
return true;
}();
// TODO check whether 2*NARROW_TURN is too large
if (valid_indices && separated_at_left_side && separated_at_right_side &&
not_more_than_three && !has_obvious && has_compatible_classes)
return std::make_pair(right, left);
}
return std::make_pair(std::size_t{0}, std::size_t{0});