Turn Angles in OSRM were computed using a lookahead of 10 meters.
This PR adds more advanced coordinate extraction, analysing the road to detect offsets due to OSM way modelling. In addition it improves the handling of bearings. Right now OSM reports bearings simply based on the very first coordinate along a way. With this PR, we store the bearings for a turn correctly, making the bearings for turns correct.
This commit is contained in:
@@ -598,10 +598,10 @@ EdgeID Contractor::LoadEdgeExpandedGraph(
|
||||
const auto current_segment =
|
||||
&(m_geometry_list[forward_begin + leaf_object.fwd_segment_position]);
|
||||
|
||||
u = &(internal_to_external_node_map
|
||||
[m_geometry_list[forward_begin +
|
||||
leaf_object.fwd_segment_position]
|
||||
.node_id]);
|
||||
u = &(
|
||||
internal_to_external_node_map[m_geometry_list[forward_begin +
|
||||
leaf_object.fwd_segment_position]
|
||||
.node_id]);
|
||||
v = &(internal_to_external_node_map
|
||||
[m_geometry_list[forward_begin + leaf_object.fwd_segment_position + 1]
|
||||
.node_id]);
|
||||
@@ -619,8 +619,8 @@ EdgeID Contractor::LoadEdgeExpandedGraph(
|
||||
current_segment->forward_weight,
|
||||
log_edge_updates_factor);
|
||||
|
||||
m_geometry_list[forward_begin + 1 + leaf_object.fwd_segment_position].forward_weight =
|
||||
new_segment_weight;
|
||||
m_geometry_list[forward_begin + 1 + leaf_object.fwd_segment_position]
|
||||
.forward_weight = new_segment_weight;
|
||||
m_geometry_datasource[forward_begin + 1 + leaf_object.fwd_segment_position] =
|
||||
forward_speed_iter->speed_source.source;
|
||||
|
||||
@@ -642,8 +642,8 @@ EdgeID Contractor::LoadEdgeExpandedGraph(
|
||||
segment_speed_filenames,
|
||||
current_segment->reverse_weight,
|
||||
log_edge_updates_factor);
|
||||
m_geometry_list[forward_begin + leaf_object.fwd_segment_position].reverse_weight =
|
||||
new_segment_weight;
|
||||
m_geometry_list[forward_begin + leaf_object.fwd_segment_position]
|
||||
.reverse_weight = new_segment_weight;
|
||||
m_geometry_datasource[forward_begin + leaf_object.fwd_segment_position] =
|
||||
reverse_speed_iter->speed_source.source;
|
||||
|
||||
|
||||
@@ -13,22 +13,6 @@ namespace guidance
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
std::pair<short, short> getIntermediateBearings(const LegGeometry &leg_geometry,
|
||||
const std::size_t segment_index)
|
||||
{
|
||||
auto turn_index = leg_geometry.BackIndex(segment_index);
|
||||
BOOST_ASSERT(turn_index > 0);
|
||||
BOOST_ASSERT(turn_index + 1 < leg_geometry.locations.size());
|
||||
|
||||
// TODO chose a bigger look-a-head to smooth complex geometry
|
||||
const auto pre_turn_coordinate = leg_geometry.locations[turn_index - 1];
|
||||
const auto turn_coordinate = leg_geometry.locations[turn_index];
|
||||
const auto post_turn_coordinate = leg_geometry.locations[turn_index + 1];
|
||||
|
||||
return std::make_pair<short, short>(
|
||||
std::round(util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate)),
|
||||
std::round(util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate)));
|
||||
}
|
||||
|
||||
std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "engine/guidance/post_processing.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "engine/guidance/post_processing.hpp"
|
||||
|
||||
#include "engine/guidance/assemble_steps.hpp"
|
||||
#include "engine/guidance/lane_processing.hpp"
|
||||
@@ -42,8 +42,10 @@ const constexpr double MAX_COLLAPSE_DISTANCE = 30;
|
||||
// check if at least one of the turns is actually a maneuver
|
||||
inline bool hasManeuver(const RouteStep &first, const RouteStep &second)
|
||||
{
|
||||
return first.maneuver.instruction.type != TurnType::Suppressed ||
|
||||
second.maneuver.instruction.type != TurnType::Suppressed;
|
||||
return (first.maneuver.instruction.type != TurnType::Suppressed ||
|
||||
second.maneuver.instruction.type != TurnType::Suppressed) &&
|
||||
(first.maneuver.instruction.type != TurnType::NoTurn &&
|
||||
second.maneuver.instruction.type != TurnType::NoTurn);
|
||||
}
|
||||
|
||||
// forward all signage/name data from one step to another.
|
||||
@@ -67,6 +69,7 @@ inline bool choiceless(const RouteStep &step, const RouteStep &previous)
|
||||
1 >= std::count(step.intersections.front().entry.begin(),
|
||||
step.intersections.front().entry.end(),
|
||||
true);
|
||||
|
||||
return is_without_choice;
|
||||
}
|
||||
|
||||
@@ -330,8 +333,9 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
entry_intersection.bearings[entry_intersection.in]),
|
||||
exit_bearing);
|
||||
|
||||
auto bearings = propagation_step.intersections.front().bearings;
|
||||
propagation_step.maneuver.instruction.direction_modifier =
|
||||
::osrm::util::guidance::getTurnDirection(angle);
|
||||
util::guidance::getTurnDirection(angle);
|
||||
}
|
||||
|
||||
forwardStepSignage(propagation_step, destination_copy);
|
||||
@@ -347,6 +351,142 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
}
|
||||
}
|
||||
|
||||
bool bearingsAreReversed(const double bearing_in, const double bearing_out)
|
||||
{
|
||||
// Nearly perfectly reversed angles have a difference close to 180 degrees (straight)
|
||||
const double left_turn_angle = [&]() {
|
||||
if (0 <= bearing_out && bearing_out <= bearing_in)
|
||||
return bearing_in - bearing_out;
|
||||
return bearing_in + 360 - bearing_out;
|
||||
}();
|
||||
return angularDeviation(left_turn_angle, 180) <= 35;
|
||||
}
|
||||
|
||||
bool isLinkroad(const RouteStep &step)
|
||||
{
|
||||
const constexpr double MAX_LINK_ROAD_LENGTH = 60.0;
|
||||
return step.distance <= MAX_LINK_ROAD_LENGTH && step.name_id == EMPTY_NAMEID;
|
||||
}
|
||||
|
||||
bool isUTurn(const RouteStep &in_step, const RouteStep &out_step, const RouteStep &pre_in_step)
|
||||
{
|
||||
const bool is_possible_candidate = in_step.distance <= MAX_COLLAPSE_DISTANCE ||
|
||||
choiceless(out_step, in_step) ||
|
||||
(isLinkroad(in_step) && out_step.name_id != EMPTY_NAMEID &&
|
||||
pre_in_step.name_id == out_step.name_id);
|
||||
const bool takes_u_turn = bearingsAreReversed(
|
||||
util::bearing::reverseBearing(
|
||||
in_step.intersections.front().bearings[in_step.intersections.front().in]),
|
||||
out_step.intersections.front().bearings[out_step.intersections.front().out]);
|
||||
|
||||
return is_possible_candidate && takes_u_turn && compatible(in_step, out_step);
|
||||
}
|
||||
|
||||
double findTotalTurnAngle(const RouteStep &entry_step, const RouteStep &exit_step)
|
||||
{
|
||||
const auto exit_intersection = exit_step.intersections.front();
|
||||
const auto exit_step_exit_bearing = exit_intersection.bearings[exit_intersection.out];
|
||||
const auto exit_step_entry_bearing =
|
||||
util::bearing::reverseBearing(exit_intersection.bearings[exit_intersection.in]);
|
||||
|
||||
const auto entry_intersection = entry_step.intersections.front();
|
||||
const auto entry_step_entry_bearing =
|
||||
util::bearing::reverseBearing(entry_intersection.bearings[entry_intersection.in]);
|
||||
const auto entry_step_exit_bearing = entry_intersection.bearings[entry_intersection.out];
|
||||
|
||||
const auto exit_angle = turn_angle(exit_step_entry_bearing, exit_step_exit_bearing);
|
||||
const auto entry_angle = turn_angle(entry_step_entry_bearing, entry_step_exit_bearing);
|
||||
|
||||
const double total_angle = turn_angle(entry_step_entry_bearing, exit_step_exit_bearing);
|
||||
// We allow for minor deviations from a straight line
|
||||
if (((entry_step.distance < MAX_COLLAPSE_DISTANCE && exit_step.intersections.size() == 1) ||
|
||||
(entry_angle <= 185 && exit_angle <= 185) || (entry_angle >= 175 && exit_angle >= 175)) &&
|
||||
angularDeviation(total_angle, 180) > 20)
|
||||
{
|
||||
// both angles are in the same direction, the total turn gets increased
|
||||
//
|
||||
// a ---- b
|
||||
// \
|
||||
// c
|
||||
// |
|
||||
// d
|
||||
//
|
||||
// Will be considered just like
|
||||
// a -----b
|
||||
// |
|
||||
// c
|
||||
// |
|
||||
// d
|
||||
return total_angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
// to prevent ignoring angles like
|
||||
// a -- b
|
||||
// |
|
||||
// c -- d
|
||||
// We don't combine both turn angles here but keep the very first turn angle.
|
||||
// We choose the first one, since we consider the first maneuver in a merge range the
|
||||
// important one
|
||||
return entry_angle;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t getPreviousIndex(std::size_t index, const std::vector<RouteStep> &steps)
|
||||
{
|
||||
BOOST_ASSERT(index > 0);
|
||||
BOOST_ASSERT(index < steps.size());
|
||||
--index;
|
||||
while (index > 0 && steps[index].maneuver.instruction.type == TurnType::NoTurn)
|
||||
--index;
|
||||
|
||||
return index;
|
||||
};
|
||||
|
||||
void collapseUTurn(std::vector<RouteStep> &steps,
|
||||
const std::size_t two_back_index,
|
||||
const std::size_t one_back_index,
|
||||
const std::size_t step_index)
|
||||
{
|
||||
BOOST_ASSERT(two_back_index < steps.size());
|
||||
BOOST_ASSERT(step_index < steps.size());
|
||||
BOOST_ASSERT(one_back_index < steps.size());
|
||||
const auto ¤t_step = steps[step_index];
|
||||
|
||||
// the simple case is a u-turn that changes directly into the in-name again
|
||||
const bool direct_u_turn = !isNoticeableNameChange(steps[two_back_index], current_step);
|
||||
|
||||
// however, we might also deal with a dual-collapse scenario in which we have to
|
||||
// additionall collapse a name-change as welll
|
||||
const auto next_step_index = step_index + 1;
|
||||
const bool continues_with_name_change =
|
||||
(next_step_index < steps.size()) &&
|
||||
((steps[next_step_index].maneuver.instruction.type == TurnType::UseLane &&
|
||||
steps[next_step_index].maneuver.instruction.direction_modifier ==
|
||||
DirectionModifier::Straight) ||
|
||||
isCollapsableInstruction(steps[next_step_index].maneuver.instruction));
|
||||
const bool u_turn_with_name_change =
|
||||
continues_with_name_change && steps[next_step_index].name_id != EMPTY_NAMEID &&
|
||||
!isNoticeableNameChange(steps[two_back_index], steps[next_step_index]);
|
||||
|
||||
if (direct_u_turn || u_turn_with_name_change)
|
||||
{
|
||||
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
|
||||
invalidateStep(steps[step_index]);
|
||||
if (u_turn_with_name_change)
|
||||
{
|
||||
steps[one_back_index] =
|
||||
elongate(std::move(steps[one_back_index]), steps[next_step_index]);
|
||||
invalidateStep(steps[next_step_index]); // will be skipped due to the
|
||||
// continue statement at the
|
||||
// beginning of this function
|
||||
}
|
||||
forwardStepSignage(steps[one_back_index], steps[two_back_index]);
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
|
||||
steps[one_back_index].maneuver.instruction.direction_modifier = DirectionModifier::UTurn;
|
||||
}
|
||||
}
|
||||
|
||||
void collapseTurnAt(std::vector<RouteStep> &steps,
|
||||
const std::size_t two_back_index,
|
||||
const std::size_t one_back_index,
|
||||
@@ -357,45 +497,81 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
||||
const auto ¤t_step = steps[step_index];
|
||||
const auto &one_back_step = steps[one_back_index];
|
||||
|
||||
// FIXME: this function assumes driving on the right hand side of the streat
|
||||
const auto bearingsAreReversed = [](const double bearing_in, const double bearing_out) {
|
||||
// Nearly perfectly reversed angles have a difference close to 180 degrees (straight)
|
||||
const double left_turn_angle = [&]() {
|
||||
if (0 <= bearing_out && bearing_out <= bearing_in)
|
||||
return bearing_in - bearing_out;
|
||||
return bearing_in + 360 - bearing_out;
|
||||
}();
|
||||
return angularDeviation(left_turn_angle, 180) <= 35;
|
||||
};
|
||||
|
||||
// This function assumes driving on the right hand side of the streat
|
||||
BOOST_ASSERT(!one_back_step.intersections.empty() && !current_step.intersections.empty());
|
||||
|
||||
if (!hasManeuver(one_back_step, current_step))
|
||||
return;
|
||||
|
||||
// Very Short New Name
|
||||
if (((collapsable(one_back_step, current_step) ||
|
||||
(isCollapsableInstruction(one_back_step.maneuver.instruction) &&
|
||||
choiceless(current_step, one_back_step))) &&
|
||||
!(one_back_step.maneuver.instruction.type == TurnType::Merge)))
|
||||
// the check against merge is a workaround for motorways
|
||||
// A maneuver is preceded by a name change if the instruction just before can be collapsed
|
||||
// normally or the instruction itself is collapsable and does not actually present a choice
|
||||
const auto maneuverPrecededByNameChange = [](const RouteStep &turning_point,
|
||||
const RouteStep &possible_name_change_location,
|
||||
const RouteStep &preceeding_step) {
|
||||
// the check against merge is a workaround for motorways
|
||||
if (possible_name_change_location.maneuver.instruction.type == TurnType::Merge ||
|
||||
!compatible(possible_name_change_location, preceeding_step))
|
||||
return false;
|
||||
|
||||
return collapsable(possible_name_change_location, turning_point) ||
|
||||
(isCollapsableInstruction(possible_name_change_location.maneuver.instruction) &&
|
||||
choiceless(possible_name_change_location, preceeding_step));
|
||||
};
|
||||
|
||||
// check if the actual turn we wan't to announce is delayed. This situation describes a turn
|
||||
// that is expressed by two turns,
|
||||
const auto isDelayedTurn = [](const RouteStep &openining_turn,
|
||||
const RouteStep &finishing_turn) {
|
||||
// only possible if both are compatible
|
||||
if (!compatible(openining_turn, finishing_turn))
|
||||
return false;
|
||||
else
|
||||
{
|
||||
const auto is_short_and_collapsable =
|
||||
openining_turn.distance <= MAX_COLLAPSE_DISTANCE &&
|
||||
isCollapsableInstruction(finishing_turn.maneuver.instruction);
|
||||
|
||||
const auto without_choice = choiceless(finishing_turn, openining_turn);
|
||||
|
||||
const auto is_not_too_long_and_choiceless =
|
||||
openining_turn.distance <= 2 * MAX_COLLAPSE_DISTANCE && without_choice;
|
||||
|
||||
// for ramps we allow longer stretches, since they are often on some major brides/large
|
||||
// roads. A combined distance of of 4 intersections would be to long for a normal
|
||||
// collapse. In case of a ramp though, we also account for situations that have the ramp
|
||||
// tagged late
|
||||
const auto is_delayed_turn_onto_a_ramp =
|
||||
openining_turn.distance <= 4 * MAX_COLLAPSE_DISTANCE && without_choice &&
|
||||
util::guidance::hasRampType(finishing_turn.maneuver.instruction);
|
||||
return !util::guidance::hasRampType(openining_turn.maneuver.instruction) &&
|
||||
(is_short_and_collapsable || is_not_too_long_and_choiceless ||
|
||||
isLinkroad(openining_turn) || is_delayed_turn_onto_a_ramp);
|
||||
}
|
||||
};
|
||||
|
||||
// Handle possible u-turns
|
||||
if (isUTurn(one_back_step, current_step, steps[two_back_index]))
|
||||
collapseUTurn(steps, two_back_index, one_back_index, step_index);
|
||||
// Very Short New Name that will be suppressed. Turn location remains at current_step
|
||||
else if (maneuverPrecededByNameChange(current_step, one_back_step, steps[two_back_index]))
|
||||
{
|
||||
BOOST_ASSERT(two_back_index < steps.size());
|
||||
if (compatible(one_back_step, steps[two_back_index]))
|
||||
BOOST_ASSERT(!one_back_step.intersections.empty());
|
||||
if (TurnType::Merge == current_step.maneuver.instruction.type)
|
||||
{
|
||||
steps[step_index].maneuver.instruction.direction_modifier =
|
||||
util::guidance::mirrorDirectionModifier(
|
||||
steps[step_index].maneuver.instruction.direction_modifier);
|
||||
steps[step_index].maneuver.instruction.type = TurnType::Turn;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(!one_back_step.intersections.empty());
|
||||
if (TurnType::Continue == current_step.maneuver.instruction.type ||
|
||||
(TurnType::Suppressed == current_step.maneuver.instruction.type &&
|
||||
current_step.maneuver.instruction.direction_modifier !=
|
||||
DirectionModifier::Straight))
|
||||
steps[step_index].maneuver.instruction.type = TurnType::Turn;
|
||||
else if (TurnType::Merge == current_step.maneuver.instruction.type)
|
||||
{
|
||||
steps[step_index].maneuver.instruction.direction_modifier =
|
||||
util::guidance::mirrorDirectionModifier(
|
||||
steps[step_index].maneuver.instruction.direction_modifier);
|
||||
steps[step_index].maneuver.instruction.type = TurnType::Turn;
|
||||
}
|
||||
|
||||
else if (TurnType::NewName == current_step.maneuver.instruction.type &&
|
||||
current_step.maneuver.instruction.direction_modifier !=
|
||||
DirectionModifier::Straight &&
|
||||
@@ -407,111 +583,100 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
|
||||
one_back_step.intersections.front().bearings.size() > 2)
|
||||
steps[step_index].maneuver.instruction.type = TurnType::Turn;
|
||||
|
||||
steps[two_back_index] = elongate(std::move(steps[two_back_index]), one_back_step);
|
||||
// If the previous instruction asked to continue, the name change will have to
|
||||
// be changed into a turn
|
||||
invalidateStep(steps[one_back_index]);
|
||||
const auto total_angle = findTotalTurnAngle(steps[one_back_index], current_step);
|
||||
steps[step_index].maneuver.instruction.direction_modifier =
|
||||
getTurnDirection(total_angle);
|
||||
}
|
||||
|
||||
steps[two_back_index] = elongate(std::move(steps[two_back_index]), one_back_step);
|
||||
// If the previous instruction asked to continue, the name change will have to
|
||||
// be changed into a turn
|
||||
invalidateStep(steps[one_back_index]);
|
||||
}
|
||||
// very short segment after turn
|
||||
else if (one_back_step.distance <= MAX_COLLAPSE_DISTANCE &&
|
||||
isCollapsableInstruction(current_step.maneuver.instruction))
|
||||
// very short segment after turn, turn location remains at one_back_step
|
||||
else if (isDelayedTurn(one_back_step, current_step))
|
||||
{
|
||||
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
|
||||
// TODO check for lanes (https://github.com/Project-OSRM/osrm-backend/issues/2553)
|
||||
if (compatible(one_back_step, current_step))
|
||||
if (TurnType::Continue == one_back_step.maneuver.instruction.type &&
|
||||
isNoticeableNameChange(steps[two_back_index], current_step))
|
||||
{
|
||||
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
|
||||
|
||||
if ((TurnType::Continue == one_back_step.maneuver.instruction.type ||
|
||||
TurnType::Suppressed == one_back_step.maneuver.instruction.type) &&
|
||||
isNoticeableNameChange(steps[two_back_index], current_step))
|
||||
{
|
||||
|
||||
if (current_step.maneuver.instruction.type == TurnType::OnRamp ||
|
||||
current_step.maneuver.instruction.type == TurnType::OffRamp)
|
||||
steps[one_back_index].maneuver.instruction.type =
|
||||
current_step.maneuver.instruction.type;
|
||||
else
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
|
||||
}
|
||||
else if (TurnType::Turn == one_back_step.maneuver.instruction.type &&
|
||||
!isNoticeableNameChange(steps[two_back_index], current_step))
|
||||
{
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
|
||||
}
|
||||
else if (TurnType::Turn == one_back_step.maneuver.instruction.type &&
|
||||
!isNoticeableNameChange(steps[two_back_index], current_step))
|
||||
{
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
|
||||
|
||||
const auto getBearing = [](bool in, const RouteStep &step) {
|
||||
const auto index =
|
||||
in ? step.intersections.front().in : step.intersections.front().out;
|
||||
return step.intersections.front().bearings[index];
|
||||
};
|
||||
const auto getBearing = [](bool in, const RouteStep &step) {
|
||||
const auto index =
|
||||
in ? step.intersections.front().in : step.intersections.front().out;
|
||||
return step.intersections.front().bearings[index];
|
||||
};
|
||||
|
||||
// If we Merge onto the same street, we end up with a u-turn in some cases
|
||||
if (bearingsAreReversed(
|
||||
util::bearing::reverseBearing(getBearing(true, one_back_step)),
|
||||
getBearing(false, current_step)))
|
||||
{
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
|
||||
steps[one_back_index].maneuver.instruction.direction_modifier =
|
||||
DirectionModifier::UTurn;
|
||||
}
|
||||
}
|
||||
else if (TurnType::Merge == one_back_step.maneuver.instruction.type &&
|
||||
current_step.maneuver.instruction.type !=
|
||||
TurnType::Suppressed) // This suppressed is a check for highways. We might
|
||||
// need a highway-suppressed to get the turn onto a
|
||||
// highway...
|
||||
// If we Merge onto the same street, we end up with a u-turn in some cases
|
||||
if (bearingsAreReversed(util::bearing::reverseBearing(getBearing(true, one_back_step)),
|
||||
getBearing(false, current_step)))
|
||||
{
|
||||
steps[one_back_index].maneuver.instruction.direction_modifier =
|
||||
util::guidance::mirrorDirectionModifier(
|
||||
steps[one_back_index].maneuver.instruction.direction_modifier);
|
||||
DirectionModifier::UTurn;
|
||||
}
|
||||
forwardStepSignage(steps[one_back_index], current_step);
|
||||
invalidateStep(steps[step_index]);
|
||||
}
|
||||
}
|
||||
// Potential U-Turn
|
||||
else if ((one_back_step.distance <= MAX_COLLAPSE_DISTANCE ||
|
||||
choiceless(current_step, one_back_step)) &&
|
||||
bearingsAreReversed(util::bearing::reverseBearing(
|
||||
one_back_step.intersections.front()
|
||||
.bearings[one_back_step.intersections.front().in]),
|
||||
current_step.intersections.front()
|
||||
.bearings[current_step.intersections.front().out]) &&
|
||||
compatible(one_back_step, current_step))
|
||||
{
|
||||
BOOST_ASSERT(two_back_index < steps.size());
|
||||
// the simple case is a u-turn that changes directly into the in-name again
|
||||
const bool direct_u_turn = !isNoticeableNameChange(steps[two_back_index], current_step);
|
||||
else if (TurnType::NewName == one_back_step.maneuver.instruction.type ||
|
||||
(TurnType::NewName == current_step.maneuver.instruction.type &&
|
||||
steps[one_back_index].maneuver.instruction.type == TurnType::Suppressed))
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
|
||||
|
||||
// however, we might also deal with a dual-collapse scenario in which we have to
|
||||
// additionall collapse a name-change as welll
|
||||
const auto next_step_index = step_index + 1;
|
||||
const bool continues_with_name_change =
|
||||
(next_step_index < steps.size()) &&
|
||||
((steps[next_step_index].maneuver.instruction.type == TurnType::UseLane &&
|
||||
steps[next_step_index].maneuver.instruction.direction_modifier ==
|
||||
DirectionModifier::Straight) ||
|
||||
isCollapsableInstruction(steps[next_step_index].maneuver.instruction));
|
||||
const bool u_turn_with_name_change =
|
||||
continues_with_name_change && steps[next_step_index].name_id != EMPTY_NAMEID &&
|
||||
!isNoticeableNameChange(steps[two_back_index], steps[next_step_index]);
|
||||
|
||||
if (direct_u_turn || u_turn_with_name_change)
|
||||
if (TurnType::Merge == one_back_step.maneuver.instruction.type &&
|
||||
current_step.maneuver.instruction.type !=
|
||||
TurnType::Suppressed) // This suppressed is a check for highways. We might
|
||||
// need a highway-suppressed to get the turn onto a
|
||||
// highway...
|
||||
{
|
||||
steps[one_back_index] = elongate(std::move(steps[one_back_index]), steps[step_index]);
|
||||
invalidateStep(steps[step_index]);
|
||||
if (u_turn_with_name_change &&
|
||||
compatible(steps[one_back_index], steps[next_step_index]))
|
||||
{
|
||||
steps[one_back_index] =
|
||||
elongate(std::move(steps[one_back_index]), steps[next_step_index]);
|
||||
invalidateStep(steps[next_step_index]); // will be skipped due to the
|
||||
// continue statement at the
|
||||
// beginning of this function
|
||||
|
||||
forwardStepSignage(steps[one_back_index], steps[two_back_index]);
|
||||
}
|
||||
if (direct_u_turn)
|
||||
forwardStepSignage(steps[one_back_index], steps[two_back_index]);
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
|
||||
steps[one_back_index].maneuver.instruction.direction_modifier =
|
||||
DirectionModifier::UTurn;
|
||||
util::guidance::mirrorDirectionModifier(
|
||||
steps[one_back_index].maneuver.instruction.direction_modifier);
|
||||
}
|
||||
// on non merge-types, we check for a combined turn angle
|
||||
else if (TurnType::Merge != one_back_step.maneuver.instruction.type)
|
||||
{
|
||||
const auto combined_angle = findTotalTurnAngle(one_back_step, current_step);
|
||||
steps[one_back_index].maneuver.instruction.direction_modifier =
|
||||
getTurnDirection(combined_angle);
|
||||
}
|
||||
|
||||
steps[one_back_index].name = current_step.name;
|
||||
steps[one_back_index].name_id = current_step.name_id;
|
||||
invalidateStep(steps[step_index]);
|
||||
}
|
||||
else if (TurnType::Suppressed == current_step.maneuver.instruction.type &&
|
||||
!isNoticeableNameChange(one_back_step, current_step))
|
||||
{
|
||||
steps[one_back_index] = elongate(std::move(steps[one_back_index]), current_step);
|
||||
const auto angle = findTotalTurnAngle(one_back_step, current_step);
|
||||
steps[one_back_index].maneuver.instruction.direction_modifier =
|
||||
util::guidance::getTurnDirection(angle);
|
||||
|
||||
invalidateStep(steps[step_index]);
|
||||
}
|
||||
else if (TurnType::Turn == one_back_step.maneuver.instruction.type &&
|
||||
TurnType::OnRamp == current_step.maneuver.instruction.type)
|
||||
{
|
||||
// turning onto a ramp makes the first turn into a ramp
|
||||
steps[one_back_index] = elongate(std::move(steps[one_back_index]), current_step);
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::OnRamp;
|
||||
const auto angle = findTotalTurnAngle(one_back_step, current_step);
|
||||
steps[one_back_index].maneuver.instruction.direction_modifier =
|
||||
util::guidance::getTurnDirection(angle);
|
||||
|
||||
forwardStepSignage(steps[one_back_index], current_step);
|
||||
invalidateStep(steps[step_index]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -559,10 +724,33 @@ bool isStaggeredIntersection(const RouteStep &previous, const RouteStep ¤t
|
||||
|
||||
} // namespace
|
||||
|
||||
// A check whether two instructions can be treated as one. This is only the case for very short
|
||||
// maneuvers that can, in some form, be seen as one. Lookahead of one step.
|
||||
bool collapsable(const RouteStep &step, const RouteStep &next)
|
||||
{
|
||||
const auto is_short_step = step.distance < MAX_COLLAPSE_DISTANCE;
|
||||
const auto instruction_can_be_collapsed = isCollapsableInstruction(step.maneuver.instruction);
|
||||
|
||||
const auto is_use_lane = step.maneuver.instruction.type == TurnType::UseLane;
|
||||
const auto lanes_dont_change =
|
||||
step.intersections.front().lanes == next.intersections.front().lanes;
|
||||
|
||||
if (is_short_step && instruction_can_be_collapsed)
|
||||
return true;
|
||||
|
||||
// Prevent collapsing away important lane change steps
|
||||
if (is_short_step && is_use_lane && lanes_dont_change)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// elongate a step by another. the data is added either at the front, or the back
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
RouteStep elongate(RouteStep step, const RouteStep &by_step)
|
||||
{
|
||||
BOOST_ASSERT(step.mode == by_step.mode);
|
||||
|
||||
step.duration += by_step.duration;
|
||||
step.distance += by_step.distance;
|
||||
BOOST_ASSERT(step.mode == by_step.mode);
|
||||
@@ -597,27 +785,6 @@ RouteStep elongate(RouteStep step, const RouteStep &by_step)
|
||||
// Post processing can invalidate some instructions. For example StayOnRoundabout
|
||||
// is turned into exit counts. These instructions are removed by the following function
|
||||
|
||||
// A check whether two instructions can be treated as one. This is only the case for very short
|
||||
// maneuvers that can, in some form, be seen as one. Lookahead of one step.
|
||||
bool collapsable(const RouteStep &step, const RouteStep &next)
|
||||
{
|
||||
const auto is_short_step = step.distance < MAX_COLLAPSE_DISTANCE;
|
||||
const auto instruction_can_be_collapsed = isCollapsableInstruction(step.maneuver.instruction);
|
||||
|
||||
const auto is_use_lane = step.maneuver.instruction.type == TurnType::UseLane;
|
||||
const auto lanes_dont_change =
|
||||
step.intersections.front().lanes == next.intersections.front().lanes;
|
||||
|
||||
if (is_short_step && instruction_can_be_collapsed)
|
||||
return true;
|
||||
|
||||
// Prevent collapsing away important lane change steps
|
||||
if (is_short_step && is_use_lane && lanes_dont_change)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
|
||||
{
|
||||
// finally clean up the post-processed instructions.
|
||||
@@ -733,17 +900,6 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
if (steps.size() <= 2)
|
||||
return steps;
|
||||
|
||||
// Get the previous non-invalid instruction
|
||||
const auto getPreviousIndex = [&steps](std::size_t index) {
|
||||
BOOST_ASSERT(index > 0);
|
||||
BOOST_ASSERT(index < steps.size());
|
||||
--index;
|
||||
while (index > 0 && steps[index].maneuver.instruction.type == TurnType::NoTurn)
|
||||
--index;
|
||||
|
||||
return index;
|
||||
};
|
||||
|
||||
const auto getPreviousNameIndex = [&steps](std::size_t index) {
|
||||
BOOST_ASSERT(index > 0);
|
||||
BOOST_ASSERT(index < steps.size());
|
||||
@@ -774,9 +930,7 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
{
|
||||
const auto ¤t_step = steps[step_index];
|
||||
const auto next_step_index = step_index + 1;
|
||||
if (current_step.maneuver.instruction.type == TurnType::NoTurn)
|
||||
continue;
|
||||
const auto one_back_index = getPreviousIndex(step_index);
|
||||
const auto one_back_index = getPreviousIndex(step_index, steps);
|
||||
BOOST_ASSERT(one_back_index < steps.size());
|
||||
|
||||
const auto &one_back_step = steps[one_back_index];
|
||||
@@ -808,13 +962,24 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
else
|
||||
{
|
||||
// Handle possible u-turns between highways that look like slip-roads
|
||||
if (steps[getPreviousIndex(one_back_index, steps)].name_id ==
|
||||
steps[step_index].name_id &&
|
||||
steps[step_index].name_id != EMPTY_NAMEID)
|
||||
{
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
|
||||
}
|
||||
if (compatible(one_back_step, current_step))
|
||||
{
|
||||
// Turn Types in the response depend on whether we find the same road name
|
||||
// (sliproad indcating a u-turn) or if we are turning onto a different road, in
|
||||
// which case we use a turn.
|
||||
if (!isNoticeableNameChange(steps[getPreviousIndex(one_back_index)],
|
||||
steps[step_index]))
|
||||
if (!isNoticeableNameChange(steps[getPreviousIndex(one_back_index, steps)],
|
||||
current_step) &&
|
||||
current_step.name_id != EMPTY_NAMEID)
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Continue;
|
||||
else
|
||||
steps[one_back_index].maneuver.instruction.type = TurnType::Turn;
|
||||
@@ -830,16 +995,9 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
steps[one_back_index].intersections.front().lane_description =
|
||||
current_step.intersections.front().lane_description;
|
||||
|
||||
const auto exit_intersection = steps[step_index].intersections.front();
|
||||
const auto exit_bearing = exit_intersection.bearings[exit_intersection.out];
|
||||
|
||||
const auto entry_intersection = steps[one_back_index].intersections.front();
|
||||
const auto entry_bearing = entry_intersection.bearings[entry_intersection.in];
|
||||
|
||||
const double angle =
|
||||
turn_angle(util::bearing::reverseBearing(entry_bearing), exit_bearing);
|
||||
const auto angle = findTotalTurnAngle(one_back_step, current_step);
|
||||
steps[one_back_index].maneuver.instruction.direction_modifier =
|
||||
::osrm::util::guidance::getTurnDirection(angle);
|
||||
util::guidance::getTurnDirection(angle);
|
||||
invalidateStep(steps[step_index]);
|
||||
}
|
||||
else
|
||||
@@ -875,7 +1033,7 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
isCollapsableInstruction(one_back_step.maneuver.instruction)) ||
|
||||
isStaggeredIntersection(one_back_step, current_step)))
|
||||
{
|
||||
const auto two_back_index = getPreviousIndex(one_back_index);
|
||||
const auto two_back_index = getPreviousIndex(one_back_index, steps);
|
||||
BOOST_ASSERT(two_back_index < steps.size());
|
||||
// valid, since one_back is collapsable or a turn and therefore not depart:
|
||||
if (!isNoticeableNameChange(steps[two_back_index], current_step))
|
||||
@@ -930,27 +1088,44 @@ std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps)
|
||||
{
|
||||
// check for one of the multiple collapse scenarios and, if possible, collapse the
|
||||
// turn
|
||||
const auto two_back_index = getPreviousIndex(one_back_index);
|
||||
const auto two_back_index = getPreviousIndex(one_back_index, steps);
|
||||
BOOST_ASSERT(two_back_index < steps.size());
|
||||
collapseTurnAt(steps, two_back_index, one_back_index, step_index);
|
||||
}
|
||||
}
|
||||
else if (one_back_index > 0 && (one_back_step.distance <= MAX_COLLAPSE_DISTANCE ||
|
||||
choiceless(current_step, one_back_step)))
|
||||
else if (one_back_index > 0 &&
|
||||
(one_back_step.distance <= MAX_COLLAPSE_DISTANCE ||
|
||||
choiceless(current_step, one_back_step) || isLinkroad(one_back_step)))
|
||||
{
|
||||
// check for one of the multiple collapse scenarios and, if possible, collapse the turn
|
||||
const auto two_back_index = getPreviousIndex(one_back_index);
|
||||
const auto two_back_index = getPreviousIndex(one_back_index, steps);
|
||||
BOOST_ASSERT(two_back_index < steps.size());
|
||||
// all turns that are handled lower down are also compatible
|
||||
collapseTurnAt(steps, two_back_index, one_back_index, step_index);
|
||||
}
|
||||
|
||||
if (steps[step_index].maneuver.instruction.type == TurnType::Turn)
|
||||
{
|
||||
const auto u_turn_one_back_index = getPreviousIndex(step_index, steps);
|
||||
if (u_turn_one_back_index > 0)
|
||||
{
|
||||
const auto u_turn_two_back_index = getPreviousIndex(u_turn_one_back_index, steps);
|
||||
if (isUTurn(steps[u_turn_one_back_index],
|
||||
steps[step_index],
|
||||
steps[u_turn_two_back_index]))
|
||||
{
|
||||
collapseUTurn(steps, u_turn_two_back_index, u_turn_one_back_index, step_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// handle final sliproad
|
||||
if (steps.size() >= 3 &&
|
||||
steps[getPreviousIndex(steps.size() - 1)].maneuver.instruction.type == TurnType::Sliproad)
|
||||
steps[getPreviousIndex(steps.size() - 1, steps)].maneuver.instruction.type ==
|
||||
TurnType::Sliproad)
|
||||
{
|
||||
steps[getPreviousIndex(steps.size() - 1)].maneuver.instruction.type = TurnType::Turn;
|
||||
steps[getPreviousIndex(steps.size() - 1, steps)].maneuver.instruction.type = TurnType::Turn;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(steps.front().intersections.size() >= 1);
|
||||
@@ -1260,6 +1435,7 @@ std::vector<RouteStep> buildIntersections(std::vector<RouteStep> steps)
|
||||
steps[last_valid_instruction] =
|
||||
elongate(std::move(steps[last_valid_instruction]), step);
|
||||
step.maneuver.instruction = TurnInstruction::NO_TURN();
|
||||
invalidateStep(steps[step_index]);
|
||||
}
|
||||
else if (!isSilent(instruction))
|
||||
{
|
||||
@@ -1296,16 +1472,6 @@ std::vector<RouteStep> collapseUseLane(std::vector<RouteStep> steps)
|
||||
return (mask & tag) != extractor::guidance::TurnLaneType::empty;
|
||||
};
|
||||
|
||||
const auto getPreviousIndex = [&steps](std::size_t index) {
|
||||
BOOST_ASSERT(index > 0);
|
||||
BOOST_ASSERT(index < steps.size());
|
||||
--index;
|
||||
while (index > 0 && steps[index].maneuver.instruction.type == TurnType::NoTurn)
|
||||
--index;
|
||||
|
||||
return index;
|
||||
};
|
||||
|
||||
const auto canCollapseUseLane = [containsTag](const RouteStep &step) {
|
||||
// the lane description is given left to right, lanes are counted from the right.
|
||||
// Therefore we access the lane description using the reverse iterator
|
||||
@@ -1330,9 +1496,8 @@ std::vector<RouteStep> collapseUseLane(std::vector<RouteStep> steps)
|
||||
const auto &step = steps[step_index];
|
||||
if (step.maneuver.instruction.type == TurnType::UseLane && canCollapseUseLane(step))
|
||||
{
|
||||
const auto previous = getPreviousIndex(step_index);
|
||||
const auto previous = getPreviousIndex(step_index, steps);
|
||||
steps[previous] = elongate(std::move(steps[previous]), steps[step_index]);
|
||||
// elongate(steps[step_index-1], steps[step_index]);
|
||||
invalidateStep(steps[step_index]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -467,7 +467,8 @@ Status TilePlugin::HandleRequest(const std::shared_ptr<datafacade::BaseDataFacad
|
||||
for (const auto &source_ebn : edge_based_node_info)
|
||||
{
|
||||
// Grab a copy of the geometry leading up to the intersection.
|
||||
first_geometry = facade->GetUncompressedForwardGeometry(source_ebn.second.packed_geometry_id);
|
||||
first_geometry =
|
||||
facade->GetUncompressedForwardGeometry(source_ebn.second.packed_geometry_id);
|
||||
|
||||
// We earlier saved the source and target intersection nodes for every road section.
|
||||
// We can use the target node to find all road sections that lead away from
|
||||
@@ -534,7 +535,8 @@ Status TilePlugin::HandleRequest(const std::shared_ptr<datafacade::BaseDataFacad
|
||||
edge_based_node_info.at(target_ebn).packed_geometry_id);
|
||||
|
||||
// Now, calculate the sum of the weight of all the segments.
|
||||
forward_weight_vector = facade->GetUncompressedForwardWeights(source_ebn.second.packed_geometry_id);
|
||||
forward_weight_vector =
|
||||
facade->GetUncompressedForwardWeights(source_ebn.second.packed_geometry_id);
|
||||
const auto sum_node_weight = std::accumulate(
|
||||
forward_weight_vector.begin(), forward_weight_vector.end(), EdgeWeight{0});
|
||||
|
||||
|
||||
@@ -164,7 +164,8 @@ void CompressedEdgeContainer::CompressEdge(const EdgeID edge_id_1,
|
||||
BOOST_ASSERT(edge_bucket_id1 == GetPositionForID(edge_id_1));
|
||||
BOOST_ASSERT(edge_bucket_id1 < m_compressed_oneway_geometries.size());
|
||||
|
||||
std::vector<OnewayCompressedEdge> &edge_bucket_list1 = m_compressed_oneway_geometries[edge_bucket_id1];
|
||||
std::vector<OnewayCompressedEdge> &edge_bucket_list1 =
|
||||
m_compressed_oneway_geometries[edge_bucket_id1];
|
||||
|
||||
// note we don't save the start coordinate: it is implicitly given by edge 1
|
||||
// weight1 is the distance to the (currently) last coordinate in the bucket
|
||||
@@ -235,7 +236,8 @@ void CompressedEdgeContainer::AddUncompressedEdge(const EdgeID edge_id,
|
||||
BOOST_ASSERT(edge_bucket_id == GetPositionForID(edge_id));
|
||||
BOOST_ASSERT(edge_bucket_id < m_compressed_oneway_geometries.size());
|
||||
|
||||
std::vector<OnewayCompressedEdge> &edge_bucket_list = m_compressed_oneway_geometries[edge_bucket_id];
|
||||
std::vector<OnewayCompressedEdge> &edge_bucket_list =
|
||||
m_compressed_oneway_geometries[edge_bucket_id];
|
||||
|
||||
// note we don't save the start coordinate: it is implicitly given by edge_id
|
||||
// weight is the distance to the (currently) last coordinate in the bucket
|
||||
@@ -264,7 +266,8 @@ unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID
|
||||
|
||||
std::vector<CompressedEdge> zipped_edge_bucket;
|
||||
const auto &first_node = reverse_bucket.back();
|
||||
zipped_edge_bucket.emplace_back(CompressedEdge{first_node.node_id, INVALID_EDGE_WEIGHT, first_node.weight});
|
||||
zipped_edge_bucket.emplace_back(
|
||||
CompressedEdge{first_node.node_id, INVALID_EDGE_WEIGHT, first_node.weight});
|
||||
|
||||
for (std::size_t i = 0; i < forward_bucket.size() - 1; ++i)
|
||||
{
|
||||
@@ -273,11 +276,13 @@ unsigned CompressedEdgeContainer::ZipEdges(const EdgeID f_edge_id, const EdgeID
|
||||
|
||||
BOOST_ASSERT(fwd_node.node_id == rev_node.node_id);
|
||||
|
||||
zipped_edge_bucket.emplace_back(CompressedEdge{fwd_node.node_id, fwd_node.weight, rev_node.weight});
|
||||
zipped_edge_bucket.emplace_back(
|
||||
CompressedEdge{fwd_node.node_id, fwd_node.weight, rev_node.weight});
|
||||
}
|
||||
|
||||
const auto &last_node = forward_bucket.back();
|
||||
zipped_edge_bucket.emplace_back(CompressedEdge{last_node.node_id, last_node.weight, INVALID_EDGE_WEIGHT});
|
||||
zipped_edge_bucket.emplace_back(
|
||||
CompressedEdge{last_node.node_id, last_node.weight, INVALID_EDGE_WEIGHT});
|
||||
m_compressed_geometries.emplace_back(std::move(zipped_edge_bucket));
|
||||
|
||||
return zipped_geometry_id;
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
#include "extractor/edge_based_graph_factory.hpp"
|
||||
#include "extractor/edge_based_edge.hpp"
|
||||
#include "util/bearing.hpp"
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/coordinate_calculation.hpp"
|
||||
#include "util/exception.hpp"
|
||||
#include "util/guidance/turn_bearing.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/percent.hpp"
|
||||
#include "util/simple_logger.hpp"
|
||||
@@ -377,18 +379,14 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
auto intersection = turn_analysis.getIntersection(node_u, edge_from_u);
|
||||
intersection =
|
||||
turn_analysis.assignTurnTypes(node_u, edge_from_u, std::move(intersection));
|
||||
|
||||
intersection =
|
||||
turn_lane_handler.assignTurnLanes(node_u, edge_from_u, std::move(intersection));
|
||||
|
||||
const auto possible_turns = turn_analysis.transformIntersectionIntoTurns(intersection);
|
||||
|
||||
// the entry class depends on the turn, so we have to classify the interesction for
|
||||
// every edge
|
||||
const auto turn_classification = classifyIntersection(node_v,
|
||||
intersection,
|
||||
*m_node_based_graph,
|
||||
m_compressed_edge_container,
|
||||
m_node_info_list);
|
||||
const auto turn_classification = classifyIntersection(intersection);
|
||||
|
||||
const auto entry_class_id = [&](const util::guidance::EntryClass entry_class) {
|
||||
if (0 == entry_class_hash.count(entry_class))
|
||||
@@ -436,6 +434,7 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
|
||||
const int32_t turn_penalty =
|
||||
scripting_environment.GetTurnPenalty(180. - turn.angle);
|
||||
|
||||
const auto turn_instruction = turn.instruction;
|
||||
|
||||
if (turn_instruction.direction_modifier == guidance::DirectionModifier::UTurn)
|
||||
@@ -443,27 +442,43 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
distance += profile_properties.u_turn_penalty;
|
||||
}
|
||||
|
||||
distance += turn_penalty;
|
||||
// don't add turn penalty if it is not an actual turn. This heuristic is necessary
|
||||
// since OSRM cannot handle looping roads/parallel roads
|
||||
if (turn_instruction.type != guidance::TurnType::NoTurn)
|
||||
distance += turn_penalty;
|
||||
|
||||
const bool is_encoded_forwards = m_compressed_edge_container.HasZippedEntryForForwardID(edge_from_u);
|
||||
const bool is_encoded_backwards = m_compressed_edge_container.HasZippedEntryForReverseID(edge_from_u);
|
||||
const bool is_encoded_forwards =
|
||||
m_compressed_edge_container.HasZippedEntryForForwardID(edge_from_u);
|
||||
const bool is_encoded_backwards =
|
||||
m_compressed_edge_container.HasZippedEntryForReverseID(edge_from_u);
|
||||
BOOST_ASSERT(is_encoded_forwards || is_encoded_backwards);
|
||||
if (is_encoded_forwards) {
|
||||
if (is_encoded_forwards)
|
||||
{
|
||||
original_edge_data_vector.emplace_back(
|
||||
GeometryID{m_compressed_edge_container.GetZippedPositionForForwardID(edge_from_u), true},
|
||||
GeometryID{
|
||||
m_compressed_edge_container.GetZippedPositionForForwardID(edge_from_u),
|
||||
true},
|
||||
edge_data1.name_id,
|
||||
turn.lane_data_id,
|
||||
turn_instruction,
|
||||
entry_class_id,
|
||||
edge_data1.travel_mode);
|
||||
} else if (is_encoded_backwards) {
|
||||
edge_data1.travel_mode,
|
||||
util::guidance::TurnBearing(intersection[0].turn.bearing),
|
||||
util::guidance::TurnBearing(turn.bearing));
|
||||
}
|
||||
else if (is_encoded_backwards)
|
||||
{
|
||||
original_edge_data_vector.emplace_back(
|
||||
GeometryID{m_compressed_edge_container.GetZippedPositionForReverseID(edge_from_u), false},
|
||||
GeometryID{
|
||||
m_compressed_edge_container.GetZippedPositionForReverseID(edge_from_u),
|
||||
false},
|
||||
edge_data1.name_id,
|
||||
turn.lane_data_id,
|
||||
turn_instruction,
|
||||
entry_class_id,
|
||||
edge_data1.travel_mode);
|
||||
edge_data1.travel_mode,
|
||||
util::guidance::TurnBearing(intersection[0].turn.bearing),
|
||||
util::guidance::TurnBearing(turn.bearing));
|
||||
}
|
||||
|
||||
++original_edges_counter;
|
||||
|
||||
@@ -0,0 +1,816 @@
|
||||
#include "extractor/guidance/coordinate_extractor.hpp"
|
||||
#include "extractor/guidance/constants.hpp"
|
||||
#include "extractor/guidance/toolkit.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include <boost/range/algorithm/transform.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace extractor
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
// to use the corrected coordinate, we require it to be at least a bit further down the
|
||||
// road than the offset coordinate. We postulate a minimum Distance of 2 Meters
|
||||
const constexpr double DESIRED_COORDINATE_DIFFERENCE = 2.0;
|
||||
// the default distance we lookahead on a road. This distance prevents small mapping
|
||||
// errors to impact the turn angles.
|
||||
const constexpr double LOOKAHEAD_DISTANCE_WITHOUT_LANES = 10.0;
|
||||
// The standard with of a interstate highway is 3.7 meters. Local roads have
|
||||
// smaller widths, ranging from 2.5 to 3.25 meters. As a compromise, we use
|
||||
// the 3.25 here for our angle calculations
|
||||
const constexpr double ASSUMED_LANE_WIDTH = 3.25;
|
||||
const constexpr double FAR_LOOKAHEAD_DISTANCE = 30.0;
|
||||
|
||||
// The count of lanes assumed when no lanes are present. Since most roads will have lanes for both
|
||||
// directions or a lane count specified, we use 2. Overestimating only makes our calculations safer,
|
||||
// so we are fine for 1-lane ways. larger than 2 lanes should usually be specified in the data.
|
||||
const constexpr std::uint16_t ASSUMED_LANE_COUNT = 2;
|
||||
}
|
||||
|
||||
CoordinateExtractor::CoordinateExtractor(
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<extractor::QueryNode> &node_coordinates)
|
||||
: node_based_graph(node_based_graph), compressed_geometries(compressed_geometries),
|
||||
node_coordinates(node_coordinates)
|
||||
{
|
||||
}
|
||||
|
||||
util::Coordinate
|
||||
CoordinateExtractor::GetCoordinateAlongRoad(const NodeID intersection_node,
|
||||
const EdgeID turn_edge,
|
||||
const bool traversed_in_reverse,
|
||||
const NodeID to_node,
|
||||
const std::uint8_t intersection_lanes) const
|
||||
{
|
||||
const auto considered_lanes =
|
||||
(intersection_lanes == 0) ? ASSUMED_LANE_COUNT : intersection_lanes;
|
||||
|
||||
// we first extract all coordinates from the road
|
||||
auto coordinates =
|
||||
GetCoordinatesAlongRoad(intersection_node, turn_edge, traversed_in_reverse, to_node);
|
||||
|
||||
/* if we are looking at a straight line, we don't care where exactly the coordinate
|
||||
* is. Simply return the final coordinate. Turn angles/turn vectors are the same no matter which
|
||||
* coordinate we look at.
|
||||
*/
|
||||
if (coordinates.size() <= 2)
|
||||
return coordinates.back();
|
||||
|
||||
// fallback, mostly necessary for dead ends
|
||||
if (intersection_node == to_node)
|
||||
return TrimCoordinatesToLength(coordinates, 5).back();
|
||||
|
||||
const auto lookahead_distance =
|
||||
FAR_LOOKAHEAD_DISTANCE + considered_lanes * ASSUMED_LANE_WIDTH * 0.5;
|
||||
|
||||
// reduce coordinates to the ones we care about
|
||||
coordinates = TrimCoordinatesToLength(std::move(coordinates), lookahead_distance);
|
||||
|
||||
// If this reduction leaves us with only two coordinates, the turns/angles are represented in a
|
||||
// valid way. Only curved roads and other difficult scenarios will require multiple coordinates.
|
||||
if (coordinates.size() == 2)
|
||||
return coordinates.back();
|
||||
|
||||
const auto &turn_edge_data = node_based_graph.GetEdgeData(turn_edge);
|
||||
const util::Coordinate turn_coordinate =
|
||||
node_coordinates[traversed_in_reverse ? to_node : intersection_node];
|
||||
|
||||
// Low priority roads are usually modelled very strangely. The roads are so small, though, that
|
||||
// our basic heuristic looking at the road should be fine.
|
||||
if (turn_edge_data.road_classification.IsLowPriorityRoadClass())
|
||||
{
|
||||
// Look ahead a tiny bit. Low priority road classes can be modelled fairly distinct in the
|
||||
// very first part of the road
|
||||
coordinates = TrimCoordinatesToLength(std::move(coordinates), 10);
|
||||
if (coordinates.size() > 2 &&
|
||||
util::coordinate_calculation::haversineDistance(turn_coordinate, coordinates[1]) <
|
||||
ASSUMED_LANE_WIDTH)
|
||||
return GetCorrectedCoordinate(turn_coordinate, coordinates[1], coordinates.back());
|
||||
else
|
||||
return coordinates.back();
|
||||
}
|
||||
|
||||
/*
|
||||
* The coordinates along the road are in different distances from the source. If only very few
|
||||
* coordinates are close to the intersection, It might just be we simply looked to far down the
|
||||
* road. We can decide to weight coordinates differently based on their distance from the
|
||||
* intersection.
|
||||
* In addition, changes very close to an intersection indicate graphical representation of the
|
||||
* intersection over perceived turn angles.
|
||||
*
|
||||
* a -
|
||||
* \
|
||||
* -------------------- b
|
||||
*
|
||||
* Here the initial angle close to a might simply be due to OSM-Ways being located in the middle
|
||||
* of the actual roads. If a road splits in two, the ways for the separate direction can be
|
||||
* modeled very far apart with a steep angle at the split, even though the roads actually don't
|
||||
* take a turn. The distance between the coordinates can be an indicator for these small changes
|
||||
*/
|
||||
const auto segment_distances = [&coordinates]() {
|
||||
std::vector<double> segment_distances;
|
||||
segment_distances.reserve(coordinates.size());
|
||||
// sentinel
|
||||
auto last_coordinate = coordinates.front();
|
||||
boost::range::transform(coordinates,
|
||||
std::back_inserter(segment_distances),
|
||||
[&last_coordinate](const util::Coordinate current_coordinate) {
|
||||
const auto distance =
|
||||
util::coordinate_calculation::haversineDistance(
|
||||
last_coordinate, current_coordinate);
|
||||
last_coordinate = current_coordinate;
|
||||
return distance;
|
||||
});
|
||||
return segment_distances;
|
||||
}();
|
||||
|
||||
/* if the very first coordinate along the road is reasonably far away from the road, we assume
|
||||
* the coordinate to correctly represent the turn. This could probably be improved using
|
||||
* information on the very first turn angle (requires knowledge about previous road) and the
|
||||
* respective lane widths.
|
||||
*/
|
||||
const bool first_coordinate_is_far_away = [&segment_distances, considered_lanes]() {
|
||||
const auto required_distance =
|
||||
considered_lanes * 0.5 * ASSUMED_LANE_WIDTH + LOOKAHEAD_DISTANCE_WITHOUT_LANES;
|
||||
return segment_distances[1] > required_distance;
|
||||
}();
|
||||
|
||||
if (first_coordinate_is_far_away)
|
||||
{
|
||||
return coordinates[1];
|
||||
}
|
||||
|
||||
const double max_deviation_from_straight = GetMaxDeviation(
|
||||
coordinates.begin(), coordinates.end(), coordinates.front(), coordinates.back());
|
||||
|
||||
// if the deviation from a straight line is small, we can savely use the coordinate. We use half
|
||||
// a lane as heuristic to determine if the road is straight enough.
|
||||
if (max_deviation_from_straight < 0.5 * ASSUMED_LANE_WIDTH)
|
||||
{
|
||||
return coordinates.back();
|
||||
}
|
||||
|
||||
/*
|
||||
* if a road turns barely in the beginning, it is similar to the first coordinate being
|
||||
* sufficiently far ahead.
|
||||
* possible negative:
|
||||
* http://www.openstreetmap.org/search?query=52.514503%2013.32252#map=19/52.51450/13.32252
|
||||
*/
|
||||
const auto straight_distance_and_index = [&]() {
|
||||
auto straight_distance = segment_distances[1];
|
||||
|
||||
std::size_t index;
|
||||
for (index = 2; index < coordinates.size(); ++index)
|
||||
{
|
||||
// check the deviation from a straight line
|
||||
if (GetMaxDeviation(coordinates.begin(),
|
||||
coordinates.begin() + index,
|
||||
coordinates.front(),
|
||||
*(coordinates.begin() + index)) < 0.25 * ASSUMED_LANE_WIDTH)
|
||||
straight_distance += segment_distances[index];
|
||||
else
|
||||
break;
|
||||
}
|
||||
return std::make_pair(index - 1, straight_distance);
|
||||
}();
|
||||
const auto straight_distance = straight_distance_and_index.second;
|
||||
const auto straight_index = straight_distance_and_index.first;
|
||||
|
||||
const bool starts_of_without_turn = [&]() {
|
||||
return straight_distance >=
|
||||
considered_lanes * 0.5 * ASSUMED_LANE_WIDTH + LOOKAHEAD_DISTANCE_WITHOUT_LANES;
|
||||
}();
|
||||
if (starts_of_without_turn)
|
||||
{
|
||||
// skip over repeated coordinates
|
||||
return TrimCoordinatesToLength(std::move(coordinates), 5).back();
|
||||
}
|
||||
|
||||
// compute the regression vector based on the sum of least squares
|
||||
const auto regression_line = RegressionLine(coordinates);
|
||||
|
||||
/*
|
||||
* If we can find a line that represents the full set of coordinates within a certain range in
|
||||
* relation to ASSUMED_LANE_WIDTH, we use the regression line to express the turn angle.
|
||||
* This yields a transformation similar to:
|
||||
*
|
||||
* c d d
|
||||
* b -> c
|
||||
* b
|
||||
* a a
|
||||
*/
|
||||
const double max_deviation_from_regression = GetMaxDeviation(
|
||||
coordinates.begin(), coordinates.end(), regression_line.first, regression_line.second);
|
||||
|
||||
if (max_deviation_from_regression < 0.35 * ASSUMED_LANE_WIDTH)
|
||||
{
|
||||
// We use the locations on the regression line to offset the regression line onto the
|
||||
// intersection.
|
||||
const auto coord_between_front =
|
||||
util::coordinate_calculation::projectPointOnSegment(
|
||||
regression_line.first, regression_line.second, coordinates.front())
|
||||
.second;
|
||||
const auto coord_between_back =
|
||||
util::coordinate_calculation::projectPointOnSegment(
|
||||
regression_line.first, regression_line.second, coordinates.back())
|
||||
.second;
|
||||
return GetCorrectedCoordinate(turn_coordinate, coord_between_front, coord_between_back);
|
||||
}
|
||||
|
||||
const auto total_distance =
|
||||
std::accumulate(segment_distances.begin(), segment_distances.end(), 0.);
|
||||
|
||||
if (IsDirectOffset(coordinates,
|
||||
straight_index,
|
||||
straight_distance,
|
||||
total_distance,
|
||||
segment_distances,
|
||||
considered_lanes))
|
||||
{
|
||||
// could be too agressive? Depend on lanes to check how far we want to go out?
|
||||
// compare
|
||||
// http://www.openstreetmap.org/search?query=52.411243%2013.363575#map=19/52.41124/13.36357
|
||||
const auto offset_index = std::max<decltype(straight_index)>(1, straight_index);
|
||||
return GetCorrectedCoordinate(
|
||||
turn_coordinate, coordinates[offset_index], coordinates[offset_index + 1]);
|
||||
}
|
||||
|
||||
if (IsCurve(coordinates,
|
||||
segment_distances,
|
||||
total_distance,
|
||||
considered_lanes * 0.5 * ASSUMED_LANE_WIDTH,
|
||||
turn_edge_data))
|
||||
{
|
||||
/*
|
||||
* In curves we now have to distinguish between larger curves and tiny curves modelling the
|
||||
* actual turn in the beginnig.
|
||||
*
|
||||
* We distinguish between turns that simply model the initial way of getting onto the
|
||||
* destination lanes and the ones that performa a larger turn.
|
||||
*/
|
||||
const double offset = 0.5 * considered_lanes * ASSUMED_LANE_WIDTH;
|
||||
coordinates = TrimCoordinatesToLength(std::move(coordinates), offset);
|
||||
const auto vector_head = coordinates.back();
|
||||
coordinates = TrimCoordinatesToLength(std::move(coordinates), offset);
|
||||
BOOST_ASSERT(coordinates.size() >= 2);
|
||||
return GetCorrectedCoordinate(turn_coordinate, coordinates.back(), vector_head);
|
||||
}
|
||||
|
||||
{
|
||||
// skip over the first coordinates, in specific the assumed lane count. We add a small
|
||||
// safety factor, to not overshoot on the regression
|
||||
const auto trimmed_coordinates = TrimCoordinatesByLengthFront(
|
||||
coordinates, 0.8 * (considered_lanes * ASSUMED_LANE_WIDTH));
|
||||
if (trimmed_coordinates.size() >= 2)
|
||||
{
|
||||
// get the regression line
|
||||
const auto regression_line_trimmed = RegressionLine(trimmed_coordinates);
|
||||
|
||||
// and compute the maximum deviation from it
|
||||
const auto max_deviation_from_trimmed_regression =
|
||||
GetMaxDeviation(trimmed_coordinates.begin(),
|
||||
trimmed_coordinates.end(),
|
||||
regression_line_trimmed.first,
|
||||
regression_line_trimmed.second);
|
||||
|
||||
if (max_deviation_from_trimmed_regression < 0.5 * ASSUMED_LANE_WIDTH)
|
||||
return GetCorrectedCoordinate(
|
||||
turn_coordinate, regression_line_trimmed.first, regression_line_trimmed.second);
|
||||
}
|
||||
}
|
||||
|
||||
// We use the locations on the regression line to offset the regression line onto the
|
||||
// intersection.
|
||||
return TrimCoordinatesToLength(coordinates, LOOKAHEAD_DISTANCE_WITHOUT_LANES).back();
|
||||
}
|
||||
|
||||
std::vector<util::Coordinate>
|
||||
CoordinateExtractor::GetCoordinatesAlongRoad(const NodeID intersection_node,
|
||||
const EdgeID turn_edge,
|
||||
const bool traversed_in_reverse,
|
||||
const NodeID to_node) const
|
||||
{
|
||||
if (!compressed_geometries.HasEntryForID(turn_edge))
|
||||
{
|
||||
if (traversed_in_reverse)
|
||||
return {{node_coordinates[to_node]}, {node_coordinates[intersection_node]}};
|
||||
else
|
||||
return {{node_coordinates[intersection_node]}, {node_coordinates[to_node]}};
|
||||
}
|
||||
else
|
||||
{
|
||||
// extracts the geometry in coordinates from the compressed edge container
|
||||
std::vector<util::Coordinate> result;
|
||||
const auto &geometry = compressed_geometries.GetBucketReference(turn_edge);
|
||||
result.reserve(geometry.size() + 2);
|
||||
|
||||
// the compressed edges contain node ids, we transfer them to coordinates accessing the
|
||||
// node_coordinates array
|
||||
const auto compressedGeometryToCoordinate =
|
||||
[this](const CompressedEdgeContainer::OnewayCompressedEdge &compressed_edge)
|
||||
-> util::Coordinate { return node_coordinates[compressed_edge.node_id]; };
|
||||
|
||||
// add the coordinates to the result in either normal or reversed order, based on
|
||||
// traversed_in_reverse
|
||||
if (traversed_in_reverse)
|
||||
{
|
||||
std::transform(geometry.rbegin(),
|
||||
geometry.rend(),
|
||||
std::back_inserter(result),
|
||||
compressedGeometryToCoordinate);
|
||||
result.push_back(node_coordinates[intersection_node]);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.push_back(node_coordinates[intersection_node]);
|
||||
std::transform(geometry.begin(),
|
||||
geometry.end(),
|
||||
std::back_inserter(result),
|
||||
compressedGeometryToCoordinate);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
double
|
||||
CoordinateExtractor::GetMaxDeviation(std::vector<util::Coordinate>::const_iterator range_begin,
|
||||
const std::vector<util::Coordinate>::const_iterator &range_end,
|
||||
const util::Coordinate straight_begin,
|
||||
const util::Coordinate straight_end) const
|
||||
{
|
||||
// compute the deviation of a single coordinate from a straight line
|
||||
auto get_single_deviation = [&](const util::Coordinate coordinate) {
|
||||
// find the projected coordinate
|
||||
auto coord_between = util::coordinate_calculation::projectPointOnSegment(
|
||||
straight_begin, straight_end, coordinate)
|
||||
.second;
|
||||
// and calculate the distance between the intermediate coordinate and the coordinate
|
||||
// on the osrm-way
|
||||
return util::coordinate_calculation::haversineDistance(coord_between, coordinate);
|
||||
};
|
||||
|
||||
// note: we don't accumulate here but rather compute the maximum. The functor passed here is not
|
||||
// summing up anything.
|
||||
return std::accumulate(
|
||||
range_begin, range_end, 0.0, [&](const double current, const util::Coordinate coordinate) {
|
||||
return std::max(current, get_single_deviation(coordinate));
|
||||
});
|
||||
};
|
||||
|
||||
bool CoordinateExtractor::IsCurve(const std::vector<util::Coordinate> &coordinates,
|
||||
const std::vector<double> &segment_distances,
|
||||
const double segment_length,
|
||||
const double considered_lane_width,
|
||||
const util::NodeBasedEdgeData &edge_data) const
|
||||
{
|
||||
BOOST_ASSERT(coordinates.size() > 2);
|
||||
|
||||
// by default, we treat roundabout as curves
|
||||
if (edge_data.roundabout)
|
||||
return true;
|
||||
|
||||
// TODO we might have to fix this to better compensate for errors due to repeated coordinates
|
||||
const bool takes_an_actual_turn = [&coordinates]() {
|
||||
const auto begin_bearing =
|
||||
util::coordinate_calculation::bearing(coordinates[0], coordinates[1]);
|
||||
const auto end_bearing = util::coordinate_calculation::bearing(
|
||||
coordinates[coordinates.size() - 2], coordinates[coordinates.size() - 1]);
|
||||
|
||||
const auto total_angle = angularDeviation(begin_bearing, end_bearing);
|
||||
return total_angle > 0.5 * NARROW_TURN_ANGLE;
|
||||
}();
|
||||
|
||||
if (!takes_an_actual_turn)
|
||||
return false;
|
||||
|
||||
const auto get_deviation = [](const util::Coordinate line_start,
|
||||
const util::Coordinate line_end,
|
||||
const util::Coordinate point) {
|
||||
// find the projected coordinate
|
||||
auto coord_between =
|
||||
util::coordinate_calculation::projectPointOnSegment(line_start, line_end, point).second;
|
||||
// and calculate the distance between the intermediate coordinate and the coordinate
|
||||
return util::coordinate_calculation::haversineDistance(coord_between, point);
|
||||
};
|
||||
|
||||
// a curve needs to be on one side of the coordinate array
|
||||
const bool all_same_side = [&]() {
|
||||
if (coordinates.size() <= 3)
|
||||
return true;
|
||||
|
||||
const bool ccw = util::coordinate_calculation::isCCW(
|
||||
coordinates.front(), coordinates.back(), coordinates[1]);
|
||||
|
||||
return std::all_of(
|
||||
coordinates.begin() + 2, coordinates.end() - 1, [&](const util::Coordinate coordinate) {
|
||||
const bool compare_ccw = util::coordinate_calculation::isCCW(
|
||||
coordinates.front(), coordinates.back(), coordinate);
|
||||
return ccw == compare_ccw;
|
||||
});
|
||||
}();
|
||||
|
||||
if (!all_same_side)
|
||||
return false;
|
||||
|
||||
// check if the deviation is a sequence that increases up to a maximum deviation and decreses
|
||||
// after, following what we would expect from a modelled curve
|
||||
bool has_up_down_deviation = false;
|
||||
std::size_t maximum_deviation_index = 0;
|
||||
double maximum_deviation = 0;
|
||||
|
||||
std::tie(has_up_down_deviation, maximum_deviation_index, maximum_deviation) =
|
||||
[&coordinates, get_deviation]() -> std::tuple<bool, std::size_t, double> {
|
||||
const auto increasing = [&](const util::Coordinate lhs, const util::Coordinate rhs) {
|
||||
return get_deviation(coordinates.front(), coordinates.back(), lhs) <=
|
||||
get_deviation(coordinates.front(), coordinates.back(), rhs);
|
||||
};
|
||||
|
||||
const auto decreasing = [&](const util::Coordinate lhs, const util::Coordinate rhs) {
|
||||
return get_deviation(coordinates.front(), coordinates.back(), lhs) >=
|
||||
get_deviation(coordinates.front(), coordinates.back(), rhs);
|
||||
};
|
||||
|
||||
if (coordinates.size() < 3)
|
||||
return std::make_tuple(true, 0, 0.);
|
||||
|
||||
if (coordinates.size() == 3)
|
||||
return std::make_tuple(
|
||||
true, 1, get_deviation(coordinates.front(), coordinates.back(), coordinates[1]));
|
||||
|
||||
const auto maximum_itr =
|
||||
std::is_sorted_until(coordinates.begin() + 1, coordinates.end(), increasing);
|
||||
|
||||
if (maximum_itr == coordinates.end())
|
||||
return std::make_tuple(true, coordinates.size() - 1, 0.);
|
||||
else if (std::is_sorted(maximum_itr, coordinates.end(), decreasing))
|
||||
return std::make_tuple(
|
||||
true,
|
||||
std::distance(coordinates.begin(), maximum_itr),
|
||||
get_deviation(coordinates.front(), coordinates.back(), *maximum_itr));
|
||||
else
|
||||
return std::make_tuple(false, 0, 0.);
|
||||
}();
|
||||
|
||||
// a curve has increasing deviation from its front/back vertices to a certain point and after it
|
||||
// only decreases
|
||||
if (!has_up_down_deviation)
|
||||
return false;
|
||||
|
||||
// if the maximum deviation is at a quarter of the total curve, we are probably looking at a
|
||||
// normal turn
|
||||
const auto distance_to_max_deviation = std::accumulate(
|
||||
segment_distances.begin(), segment_distances.begin() + maximum_deviation_index, 0.);
|
||||
|
||||
if ((distance_to_max_deviation <= 0.35 * segment_length ||
|
||||
maximum_deviation < std::max(0.3 * considered_lane_width, 0.5 * ASSUMED_LANE_WIDTH)) &&
|
||||
segment_length > 10)
|
||||
return false;
|
||||
|
||||
BOOST_ASSERT(coordinates.size() >= 3);
|
||||
// Compute all turn angles along the road
|
||||
const auto turn_angles = [coordinates]() {
|
||||
std::vector<double> turn_angles;
|
||||
turn_angles.reserve(coordinates.size() - 2);
|
||||
for (std::size_t index = 0; index + 2 < coordinates.size(); ++index)
|
||||
{
|
||||
turn_angles.push_back(util::coordinate_calculation::computeAngle(
|
||||
coordinates[index], coordinates[index + 1], coordinates[index + 2]));
|
||||
}
|
||||
return turn_angles;
|
||||
}();
|
||||
|
||||
const bool curve_is_valid =
|
||||
[&turn_angles, &segment_distances, &segment_length, &considered_lane_width]() {
|
||||
// internal state for our lamdae
|
||||
bool last_was_straight = false;
|
||||
// a turn angle represents two segments between three coordinates. We initialize the
|
||||
// distance with the very first segment length (in-segment) of the first turn-angle
|
||||
double straight_distance = std::max(0., segment_distances[1] - considered_lane_width);
|
||||
auto distance_itr = segment_distances.begin() + 1;
|
||||
|
||||
// every call to the lamda requires a call to the distances. They need to be aligned
|
||||
BOOST_ASSERT(segment_distances.size() == turn_angles.size() + 2);
|
||||
|
||||
const auto detect_invalid_curve = [&](const double previous_angle,
|
||||
const double current_angle) {
|
||||
const auto both_actually_turn =
|
||||
(angularDeviation(previous_angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE) &&
|
||||
(angularDeviation(current_angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE);
|
||||
// they cannot be straight, since they differ at least by FUZZY_ANGLE_DIFFERENCE
|
||||
const auto turn_direction_switches =
|
||||
(previous_angle > STRAIGHT_ANGLE) == (current_angle < STRAIGHT_ANGLE);
|
||||
|
||||
// a turn that switches direction mid-curve is not a valid curve
|
||||
if (both_actually_turn && turn_direction_switches)
|
||||
return true;
|
||||
|
||||
const bool is_straight = angularDeviation(current_angle, STRAIGHT_ANGLE) < 5;
|
||||
++distance_itr;
|
||||
if (is_straight)
|
||||
{
|
||||
// since the angle is straight, we augment it by the second part of the segment
|
||||
straight_distance += *distance_itr;
|
||||
if (last_was_straight && straight_distance > 0.3 * segment_length)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
} // if a segment on its own is long enough, thats fair game as well
|
||||
else if (straight_distance > 0.3 * segment_length)
|
||||
return true;
|
||||
else
|
||||
{
|
||||
// we reset the last distance, starting with the next in-segment again
|
||||
straight_distance = *distance_itr;
|
||||
}
|
||||
last_was_straight = is_straight;
|
||||
return false;
|
||||
};
|
||||
|
||||
const auto end_of_straight_segment =
|
||||
std::adjacent_find(turn_angles.begin(), turn_angles.end(), detect_invalid_curve);
|
||||
|
||||
// No curve should have a very long straight segment
|
||||
return end_of_straight_segment == turn_angles.end();
|
||||
}();
|
||||
|
||||
return (segment_length > 2 * considered_lane_width && curve_is_valid);
|
||||
}
|
||||
|
||||
bool CoordinateExtractor::IsDirectOffset(const std::vector<util::Coordinate> &coordinates,
|
||||
const std::size_t straight_index,
|
||||
const double straight_distance,
|
||||
const double segment_length,
|
||||
const std::vector<double> &segment_distances,
|
||||
const std::uint8_t considered_lanes) const
|
||||
{
|
||||
// check if a given length is with half a lane of the assumed lane offset
|
||||
const auto IsCloseToLaneDistance = [considered_lanes](const double width) {
|
||||
// a road usually is connected to the middle of the lanes. So the lane-offset has to
|
||||
// consider half to road
|
||||
const auto lane_offset = 0.5 * considered_lanes * ASSUMED_LANE_WIDTH;
|
||||
return std::abs(width - lane_offset) < 0.5 * ASSUMED_LANE_WIDTH;
|
||||
};
|
||||
|
||||
// Check whether the very first coordinate is simply an offset. This is the case if the initial
|
||||
// vertex is close to the turn and the remaining coordinates are nearly straight.
|
||||
const auto offset_index = std::max<decltype(straight_index)>(1, straight_index);
|
||||
|
||||
// we need at least a single coordinate
|
||||
if (offset_index + 1 >= coordinates.size())
|
||||
return false;
|
||||
|
||||
// the straight part has to be around the lane distance
|
||||
if (!IsCloseToLaneDistance(segment_distances[offset_index]))
|
||||
return false;
|
||||
|
||||
// the segment itself cannot be short
|
||||
if (segment_length < 0.8 * FAR_LOOKAHEAD_DISTANCE)
|
||||
return false;
|
||||
|
||||
// if the remaining segment is short, we don't consider it an offset
|
||||
if ((segment_length - std::max(straight_distance, segment_distances[1])) > 0.1 * segment_length)
|
||||
return false;
|
||||
|
||||
// finally, we cannot be far off from a straight line for the remaining coordinates
|
||||
return 0.5 * ASSUMED_LANE_WIDTH > GetMaxDeviation(coordinates.begin() + offset_index,
|
||||
coordinates.end(),
|
||||
coordinates[offset_index],
|
||||
coordinates.back());
|
||||
}
|
||||
|
||||
std::vector<util::Coordinate>
|
||||
CoordinateExtractor::TrimCoordinatesToLength(std::vector<util::Coordinate> coordinates,
|
||||
const double desired_length) const
|
||||
{
|
||||
BOOST_ASSERT(coordinates.size() >= 2);
|
||||
double distance_to_current_coordinate = 0;
|
||||
|
||||
for (std::size_t coordinate_index = 1; coordinate_index < coordinates.size();
|
||||
++coordinate_index)
|
||||
{
|
||||
const auto distance_to_next_coordinate =
|
||||
distance_to_current_coordinate +
|
||||
util::coordinate_calculation::haversineDistance(coordinates[coordinate_index - 1],
|
||||
coordinates[coordinate_index]);
|
||||
|
||||
// if we reached the number of coordinates, we can stop here
|
||||
if (distance_to_next_coordinate >= desired_length)
|
||||
{
|
||||
coordinates.resize(coordinate_index + 1);
|
||||
coordinates.back() = util::coordinate_calculation::interpolateLinear(
|
||||
ComputeInterpolationFactor(
|
||||
desired_length, distance_to_current_coordinate, distance_to_next_coordinate),
|
||||
coordinates[coordinate_index - 1],
|
||||
coordinates[coordinate_index]);
|
||||
break;
|
||||
}
|
||||
|
||||
// remember the accumulated distance
|
||||
distance_to_current_coordinate = distance_to_next_coordinate;
|
||||
}
|
||||
if (coordinates.size() > 2 &&
|
||||
util::coordinate_calculation::haversineDistance(coordinates[0], coordinates[1]) <= 1)
|
||||
coordinates.erase(coordinates.begin() + 1);
|
||||
|
||||
BOOST_ASSERT(coordinates.size());
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
util::Coordinate
|
||||
CoordinateExtractor::GetCorrectedCoordinate(const util::Coordinate fixpoint,
|
||||
const util::Coordinate vector_base,
|
||||
const util::Coordinate vector_head) const
|
||||
{
|
||||
// if the coordinates are close together, we were not able to look far ahead, so
|
||||
// we can use the end-coordinate
|
||||
if (util::coordinate_calculation::haversineDistance(vector_base, vector_head) <
|
||||
DESIRED_COORDINATE_DIFFERENCE)
|
||||
return vector_head;
|
||||
else
|
||||
{
|
||||
/* to correct for the initial offset, we move the lookahead coordinate close
|
||||
* to the original road. We do so by subtracting the difference between the
|
||||
* turn coordinate and the offset coordinate from the lookahead coordinge:
|
||||
*
|
||||
* a ------ b ------ c
|
||||
* |
|
||||
* d
|
||||
* \
|
||||
* \
|
||||
* e
|
||||
*
|
||||
* is converted to:
|
||||
*
|
||||
* a ------ b ------ c
|
||||
* \
|
||||
* \
|
||||
* e
|
||||
*
|
||||
* for turn node `b`, vector_base `d` and vector_head `e`
|
||||
*/
|
||||
const auto offset_percentage = 90;
|
||||
const auto corrected_lon =
|
||||
vector_head.lon -
|
||||
util::FixedLongitude{offset_percentage *
|
||||
static_cast<int>(vector_base.lon - fixpoint.lon) / 100};
|
||||
const auto corrected_lat =
|
||||
vector_head.lat -
|
||||
util::FixedLatitude{offset_percentage *
|
||||
static_cast<int>(vector_base.lat - fixpoint.lat) / 100};
|
||||
|
||||
return util::Coordinate(corrected_lon, corrected_lat);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<util::Coordinate>
|
||||
CoordinateExtractor::SampleCoordinates(const std::vector<util::Coordinate> &coordinates,
|
||||
const double max_sample_length,
|
||||
const double rate) const
|
||||
{
|
||||
BOOST_ASSERT(rate > 0 && coordinates.size() >= 2);
|
||||
|
||||
// the return value
|
||||
std::vector<util::Coordinate> sampled_coordinates;
|
||||
sampled_coordinates.reserve(ceil(max_sample_length / rate) + 2);
|
||||
|
||||
// the very first coordinate is always part of the sample
|
||||
sampled_coordinates.push_back(coordinates.front());
|
||||
|
||||
double carry_length = 0., total_length = 0.;
|
||||
// interpolate coordinates as long as we are not past the desired length
|
||||
const auto add_samples_until_length_limit = [&](const util::Coordinate previous_coordinate,
|
||||
const util::Coordinate current_coordinate) {
|
||||
// pretend to have found an element and stop the sampling
|
||||
if (total_length > max_sample_length)
|
||||
return true;
|
||||
|
||||
const auto distance_between = util::coordinate_calculation::haversineDistance(
|
||||
previous_coordinate, current_coordinate);
|
||||
|
||||
if (carry_length + distance_between >= rate)
|
||||
{
|
||||
// within the current segment, there is at least a single coordinate that we want to
|
||||
// sample. We extract all coordinates that are on our sampling intervals and update our
|
||||
// local sampling item to reflect the travelled distance
|
||||
const auto base_sampling = rate - carry_length;
|
||||
|
||||
// the number of samples in the interval is equal to the length of the interval (+ the
|
||||
// already traversed part from the previous segment) divided by the sampling rate
|
||||
BOOST_ASSERT(max_sample_length > total_length);
|
||||
const std::size_t num_samples = std::floor(
|
||||
(std::min(max_sample_length - total_length, distance_between) + carry_length) /
|
||||
rate);
|
||||
|
||||
for (std::size_t sample_value = 0; sample_value < num_samples; ++sample_value)
|
||||
{
|
||||
const auto interpolation_factor = ComputeInterpolationFactor(
|
||||
base_sampling + sample_value * rate, 0, distance_between);
|
||||
auto sampled_coordinate = util::coordinate_calculation::interpolateLinear(
|
||||
interpolation_factor, previous_coordinate, current_coordinate);
|
||||
sampled_coordinates.emplace_back(sampled_coordinate);
|
||||
}
|
||||
|
||||
// current length needs to reflect how much is missing to the next sample. Here we can
|
||||
// ignore max sample range, because if we reached it, the loop is done anyhow
|
||||
carry_length = (distance_between + carry_length) - (num_samples * rate);
|
||||
}
|
||||
else
|
||||
{
|
||||
// do the necessary bookkeeping and continue
|
||||
carry_length += distance_between;
|
||||
}
|
||||
// the total length travelled is always updated by the full distance
|
||||
total_length += distance_between;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// misuse of adjacent_find. Loop over coordinates, until a total sample length is reached
|
||||
std::adjacent_find(coordinates.begin(), coordinates.end(), add_samples_until_length_limit);
|
||||
|
||||
return sampled_coordinates;
|
||||
}
|
||||
|
||||
double CoordinateExtractor::ComputeInterpolationFactor(const double desired_distance,
|
||||
const double distance_to_first,
|
||||
const double distance_to_second) const
|
||||
{
|
||||
BOOST_ASSERT(distance_to_first < desired_distance);
|
||||
double segment_length = distance_to_second - distance_to_first;
|
||||
BOOST_ASSERT(segment_length > 0);
|
||||
BOOST_ASSERT(distance_to_second >= desired_distance);
|
||||
double missing_distance = desired_distance - distance_to_first;
|
||||
return std::max(0., std::min(missing_distance / segment_length, 1.0));
|
||||
}
|
||||
|
||||
std::vector<util::Coordinate>
|
||||
CoordinateExtractor::TrimCoordinatesByLengthFront(std::vector<util::Coordinate> coordinates,
|
||||
const double desired_length) const
|
||||
{
|
||||
double distance_to_index = 0;
|
||||
std::size_t index = 0;
|
||||
for (std::size_t next_index = 1; next_index < coordinates.size(); ++next_index)
|
||||
{
|
||||
const double next_distance =
|
||||
distance_to_index + util::coordinate_calculation::haversineDistance(
|
||||
coordinates[index], coordinates[next_index]);
|
||||
if (next_distance >= desired_length)
|
||||
{
|
||||
const auto factor =
|
||||
ComputeInterpolationFactor(desired_length, distance_to_index, next_distance);
|
||||
auto interpolated_coordinate = util::coordinate_calculation::interpolateLinear(
|
||||
factor, coordinates[index], coordinates[next_index]);
|
||||
if (index > 0)
|
||||
coordinates.erase(coordinates.begin(), coordinates.begin() + index);
|
||||
coordinates.front() = interpolated_coordinate;
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
distance_to_index = next_distance;
|
||||
index = next_index;
|
||||
}
|
||||
|
||||
// the coordinates in total are too short in length for the desired length
|
||||
// this part is only reached when we don't return from within the above loop
|
||||
coordinates.clear();
|
||||
return coordinates;
|
||||
}
|
||||
|
||||
std::pair<util::Coordinate, util::Coordinate>
|
||||
CoordinateExtractor::RegressionLine(const std::vector<util::Coordinate> &coordinates) const
|
||||
{
|
||||
// create a sample of all coordinates to improve the quality of our regression vector
|
||||
// (less dependent on modelling of the data in OSM)
|
||||
const auto sampled_coordinates = SampleCoordinates(coordinates, FAR_LOOKAHEAD_DISTANCE, 1);
|
||||
|
||||
// compute the regression vector based on the sum of least squares
|
||||
const auto regression_line = leastSquareRegression(sampled_coordinates);
|
||||
const auto coord_between_front =
|
||||
util::coordinate_calculation::projectPointOnSegment(
|
||||
regression_line.first, regression_line.second, coordinates.front())
|
||||
.second;
|
||||
const auto coord_between_back =
|
||||
util::coordinate_calculation::projectPointOnSegment(
|
||||
regression_line.first, regression_line.second, coordinates.back())
|
||||
.second;
|
||||
|
||||
return {coord_between_front, coord_between_back};
|
||||
}
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace extractor
|
||||
} // namespace osrm
|
||||
@@ -21,6 +21,8 @@ std::string toString(const ConnectedRoad &road)
|
||||
result += std::to_string(road.entry_allowed);
|
||||
result += " angle: ";
|
||||
result += std::to_string(road.turn.angle);
|
||||
result += " bearing: ";
|
||||
result += std::to_string(road.turn.bearing);
|
||||
result += " instruction: ";
|
||||
result += std::to_string(static_cast<std::int32_t>(road.turn.instruction.type)) + " " +
|
||||
std::to_string(static_cast<std::int32_t>(road.turn.instruction.direction_modifier)) +
|
||||
|
||||
@@ -26,7 +26,7 @@ IntersectionGenerator::IntersectionGenerator(
|
||||
const CompressedEdgeContainer &compressed_edge_container)
|
||||
: node_based_graph(node_based_graph), restriction_map(restriction_map),
|
||||
barrier_nodes(barrier_nodes), node_info_list(node_info_list),
|
||||
compressed_edge_container(compressed_edge_container)
|
||||
coordinate_extractor(node_based_graph, compressed_edge_container, node_info_list)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -75,16 +75,21 @@ Intersection IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
|
||||
|
||||
bool has_uturn_edge = false;
|
||||
bool uturn_could_be_valid = false;
|
||||
const util::Coordinate turn_coordinate = node_info_list[turn_node];
|
||||
|
||||
const auto intersection_lanes = getLaneCountAtIntersection(turn_node, node_based_graph);
|
||||
|
||||
for (const EdgeID onto_edge : node_based_graph.GetAdjacentEdgeRange(turn_node))
|
||||
{
|
||||
BOOST_ASSERT(onto_edge != SPECIAL_EDGEID);
|
||||
const NodeID to_node = node_based_graph.GetTarget(onto_edge);
|
||||
const auto &onto_data = node_based_graph.GetEdgeData(onto_edge);
|
||||
|
||||
bool turn_is_valid =
|
||||
// reverse edges are never valid turns because the resulting turn would look like this:
|
||||
// from_node --via_edge--> turn_node <--onto_edge-- to_node
|
||||
// however we need this for capture intersection shape for incoming one-ways
|
||||
!node_based_graph.GetEdgeData(onto_edge).reversed &&
|
||||
!onto_data.reversed &&
|
||||
// we are not turning over a barrier
|
||||
(!is_barrier_node || from_node == to_node) &&
|
||||
// We are at an only_-restriction but not at the right turn.
|
||||
@@ -93,8 +98,17 @@ Intersection IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
|
||||
!restriction_map.CheckIfTurnIsRestricted(from_node, turn_node, to_node);
|
||||
|
||||
auto angle = 0.;
|
||||
double bearing = 0.;
|
||||
|
||||
// The first coordinate (the origin) can depend on the number of lanes turning onto,
|
||||
// just as the target coordinate can. Here we compute the corrected coordinate for the
|
||||
// incoming edge.
|
||||
const auto first_coordinate = coordinate_extractor.GetCoordinateAlongRoad(
|
||||
from_node, via_eid, INVERT, turn_node, intersection_lanes);
|
||||
|
||||
if (from_node == to_node)
|
||||
{
|
||||
bearing = util::coordinate_calculation::bearing(turn_coordinate, first_coordinate);
|
||||
uturn_could_be_valid = turn_is_valid;
|
||||
if (turn_is_valid && !is_barrier_node)
|
||||
{
|
||||
@@ -121,33 +135,48 @@ Intersection IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
|
||||
}
|
||||
else
|
||||
{
|
||||
// unpack first node of second segment if packed
|
||||
const auto first_coordinate = getRepresentativeCoordinate(
|
||||
from_node, turn_node, via_eid, INVERT, compressed_edge_container, node_info_list);
|
||||
const auto third_coordinate = getRepresentativeCoordinate(
|
||||
turn_node, to_node, onto_edge, !INVERT, compressed_edge_container, node_info_list);
|
||||
// the default distance we lookahead on a road. This distance prevents small mapping
|
||||
// errors to impact the turn angles.
|
||||
const auto third_coordinate = coordinate_extractor.GetCoordinateAlongRoad(
|
||||
turn_node, onto_edge, !INVERT, to_node, intersection_lanes);
|
||||
|
||||
angle = util::coordinate_calculation::computeAngle(
|
||||
first_coordinate, node_info_list[turn_node], third_coordinate);
|
||||
first_coordinate, turn_coordinate, third_coordinate);
|
||||
|
||||
bearing = util::coordinate_calculation::bearing(turn_coordinate, third_coordinate);
|
||||
|
||||
if (std::abs(angle) < std::numeric_limits<double>::epsilon())
|
||||
has_uturn_edge = true;
|
||||
}
|
||||
|
||||
intersection.push_back(
|
||||
ConnectedRoad(TurnOperation{onto_edge,
|
||||
angle,
|
||||
bearing,
|
||||
{TurnType::Invalid, DirectionModifier::UTurn},
|
||||
INVALID_LANE_DATAID},
|
||||
turn_is_valid));
|
||||
}
|
||||
|
||||
// We hit the case of a street leading into nothing-ness. Since the code here assumes that this
|
||||
// We hit the case of a street leading into nothing-ness. Since the code here assumes
|
||||
// that this
|
||||
// will never happen we add an artificial invalid uturn in this case.
|
||||
if (!has_uturn_edge)
|
||||
{
|
||||
intersection.push_back(
|
||||
{TurnOperation{
|
||||
via_eid, 0., {TurnType::Invalid, DirectionModifier::UTurn}, INVALID_LANE_DATAID},
|
||||
false});
|
||||
const auto first_coordinate = coordinate_extractor.GetCoordinateAlongRoad(
|
||||
from_node,
|
||||
via_eid,
|
||||
INVERT,
|
||||
turn_node,
|
||||
node_based_graph.GetEdgeData(via_eid).road_classification.GetNumberOfLanes());
|
||||
const double bearing =
|
||||
util::coordinate_calculation::bearing(turn_coordinate, first_coordinate);
|
||||
|
||||
intersection.push_back({TurnOperation{via_eid,
|
||||
0.,
|
||||
bearing,
|
||||
{TurnType::Invalid, DirectionModifier::UTurn},
|
||||
INVALID_LANE_DATAID},
|
||||
false});
|
||||
}
|
||||
|
||||
const auto ByAngle = [](const ConnectedRoad &first, const ConnectedRoad second) {
|
||||
@@ -162,7 +191,8 @@ Intersection IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
|
||||
boost::count_if(intersection, [](const ConnectedRoad &road) { return road.entry_allowed; });
|
||||
if (0 == valid_count && uturn_could_be_valid)
|
||||
{
|
||||
// after intersections sorting by angles, find the u-turn with (from_node == to_node)
|
||||
// after intersections sorting by angles, find the u-turn with (from_node ==
|
||||
// to_node)
|
||||
// that was inserted together with setting uturn_could_be_valid flag
|
||||
std::size_t self_u_turn = 0;
|
||||
while (self_u_turn < intersection.size() &&
|
||||
@@ -175,7 +205,6 @@ Intersection IntersectionGenerator::GetConnectedRoads(const NodeID from_node,
|
||||
BOOST_ASSERT(from_node == node_based_graph.GetTarget(intersection[self_u_turn].turn.eid));
|
||||
intersection[self_u_turn].entry_allowed = true;
|
||||
}
|
||||
|
||||
return intersection;
|
||||
}
|
||||
|
||||
@@ -219,15 +248,21 @@ bool IntersectionGenerator::CanMerge(const NodeID node_at_intersection,
|
||||
const auto angle_between = angularDeviation(intersection[first_index].turn.angle,
|
||||
intersection[second_index].turn.angle);
|
||||
|
||||
const auto coordinate_at_in_edge =
|
||||
getRepresentativeCoordinate(node_at_intersection,
|
||||
node_based_graph.GetTarget(intersection[0].turn.eid),
|
||||
intersection[0].turn.eid,
|
||||
false,
|
||||
compressed_edge_container,
|
||||
node_info_list);
|
||||
const auto intersection_lanes =
|
||||
getLaneCountAtIntersection(node_at_intersection, node_based_graph);
|
||||
|
||||
const auto coordinate_at_in_edge = coordinate_extractor.GetCoordinateAlongRoad(
|
||||
node_at_intersection,
|
||||
intersection[0].turn.eid,
|
||||
!INVERT,
|
||||
node_based_graph.GetTarget(intersection[0].turn.eid),
|
||||
intersection_lanes);
|
||||
|
||||
const auto coordinate_at_intersection = node_info_list[node_at_intersection];
|
||||
|
||||
if (angle_between >= 120)
|
||||
return false;
|
||||
|
||||
const auto isValidYArm = [this,
|
||||
intersection,
|
||||
coordinate_at_in_edge,
|
||||
@@ -253,20 +288,19 @@ bool IntersectionGenerator::CanMerge(const NodeID node_at_intersection,
|
||||
coordinate_at_in_edge, coordinate_at_intersection, coordinate_at_target);
|
||||
const auto other_turn_angle = util::coordinate_calculation::computeAngle(
|
||||
coordinate_at_in_edge, coordinate_at_intersection, coordinate_at_other_target);
|
||||
const double distance_to_target = util::coordinate_calculation::haversineDistance(
|
||||
coordinate_at_intersection, coordinate_at_target);
|
||||
|
||||
const constexpr double MAX_COLLAPSE_DISTANCE = 30;
|
||||
if (distance_to_target < MAX_COLLAPSE_DISTANCE)
|
||||
return false;
|
||||
|
||||
const bool becomes_narrower =
|
||||
angularDeviation(turn_angle, other_turn_angle) < NARROW_TURN_ANGLE &&
|
||||
angularDeviation(turn_angle, other_turn_angle) <
|
||||
angularDeviation(turn_angle, other_turn_angle) <=
|
||||
angularDeviation(intersection[index].turn.angle,
|
||||
intersection[other_index].turn.angle);
|
||||
|
||||
return becomes_narrower;
|
||||
const bool has_same_deviation =
|
||||
std::abs(angularDeviation(intersection[index].turn.angle, STRAIGHT_ANGLE) -
|
||||
angularDeviation(intersection[other_index].turn.angle, STRAIGHT_ANGLE)) <
|
||||
MAXIMAL_ALLOWED_NO_TURN_DEVIATION;
|
||||
|
||||
return becomes_narrower || has_same_deviation;
|
||||
};
|
||||
|
||||
const bool is_y_arm_first = isValidYArm(first_index, second_index);
|
||||
@@ -345,33 +379,37 @@ Intersection IntersectionGenerator::MergeSegregatedRoads(const NodeID intersecti
|
||||
return (index + intersection.size() - 1) % intersection.size();
|
||||
};
|
||||
|
||||
const auto merge = [](const ConnectedRoad &first,
|
||||
const ConnectedRoad &second) -> ConnectedRoad {
|
||||
if (!first.entry_allowed)
|
||||
{
|
||||
ConnectedRoad result = second;
|
||||
result.turn.angle = (first.turn.angle + second.turn.angle) / 2;
|
||||
if (first.turn.angle - second.turn.angle > 180)
|
||||
result.turn.angle += 180;
|
||||
if (result.turn.angle > 360)
|
||||
result.turn.angle -= 360;
|
||||
// we only merge small angles. If the difference between both is large, we are looking at a
|
||||
// bearing leading north. Such a bearing cannot be handled via the basic average. In this
|
||||
// case we actually need to shift the bearing by half the difference.
|
||||
const auto aroundZero = [](const double first, const double second) {
|
||||
return (std::max(first, second) - std::min(first, second)) >= 180;
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
// find the angle between two other angles
|
||||
const auto combineAngles = [aroundZero](const double first, const double second) {
|
||||
if (!aroundZero(first, second))
|
||||
return .5 * (first + second);
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(!second.entry_allowed);
|
||||
ConnectedRoad result = first;
|
||||
result.turn.angle = (first.turn.angle + second.turn.angle) / 2;
|
||||
|
||||
if (first.turn.angle - second.turn.angle > 180)
|
||||
result.turn.angle += 180;
|
||||
if (result.turn.angle > 360)
|
||||
result.turn.angle -= 360;
|
||||
|
||||
return result;
|
||||
const auto offset = angularDeviation(first, second);
|
||||
auto new_angle = std::max(first, second) + .5 * offset;
|
||||
if (new_angle > 360)
|
||||
return new_angle - 360;
|
||||
return new_angle;
|
||||
}
|
||||
};
|
||||
|
||||
const auto merge = [combineAngles](const ConnectedRoad &first,
|
||||
const ConnectedRoad &second) -> ConnectedRoad {
|
||||
ConnectedRoad result = first.entry_allowed ? first : second;
|
||||
result.turn.angle = combineAngles(first.turn.angle, second.turn.angle);
|
||||
result.turn.bearing = combineAngles(first.turn.bearing, second.turn.bearing);
|
||||
BOOST_ASSERT(0 <= result.turn.angle && result.turn.angle <= 360.0);
|
||||
BOOST_ASSERT(0 <= result.turn.bearing && result.turn.bearing <= 360.0);
|
||||
return result;
|
||||
};
|
||||
|
||||
if (intersection.size() <= 1)
|
||||
return intersection;
|
||||
|
||||
@@ -426,10 +464,10 @@ Intersection IntersectionGenerator::MergeSegregatedRoads(const NodeID intersecti
|
||||
for (std::size_t i = 1; i + 1 < intersection.size(); ++i)
|
||||
intersection[i].turn.angle += correction_factor;
|
||||
|
||||
// FIXME if we have a left-sided country, we need to switch this off and enable it below
|
||||
// FIXME if we have a left-sided country, we need to switch this off and enable it
|
||||
// below
|
||||
intersection[0] = merge(intersection.front(), intersection.back());
|
||||
intersection[0].turn.angle = 0;
|
||||
|
||||
intersection.pop_back();
|
||||
}
|
||||
else if (CanMerge(intersection_node, intersection, 0, 1))
|
||||
@@ -511,14 +549,6 @@ Intersection IntersectionGenerator::AdjustForJoiningRoads(const NodeID node_at_i
|
||||
for (std::size_t index = 1; index < intersection.size(); ++index)
|
||||
{
|
||||
auto &road = intersection[index];
|
||||
// to find out about the above situation, we need to look at the next intersection (at d in
|
||||
// the example). If the initial road can be merged to the left/right, we are about to adjust
|
||||
// the angle.
|
||||
const auto next_intersection_along_road =
|
||||
GetConnectedRoads(node_at_intersection, road.turn.eid);
|
||||
if (next_intersection_along_road.size() <= 1)
|
||||
continue;
|
||||
|
||||
const auto node_at_next_intersection = node_based_graph.GetTarget(road.turn.eid);
|
||||
const util::Coordinate coordinate_at_next_intersection =
|
||||
node_info_list[node_at_next_intersection];
|
||||
@@ -535,6 +565,16 @@ Intersection IntersectionGenerator::AdjustForJoiningRoads(const NodeID node_at_i
|
||||
return angle;
|
||||
};
|
||||
|
||||
const auto range = node_based_graph.GetAdjacentEdgeRange(node_at_next_intersection);
|
||||
if (range.size() <= 1)
|
||||
continue;
|
||||
|
||||
// to find out about the above situation, we need to look at the next intersection (at d in
|
||||
// the example). If the initial road can be merged to the left/right, we are about to adjust
|
||||
// the angle.
|
||||
const auto next_intersection_along_road =
|
||||
GetConnectedRoads(node_at_intersection, road.turn.eid);
|
||||
|
||||
// check if the u-turn edge at the next intersection could be merged to the left/right. If
|
||||
// this is the case and the road is not far away (see previous distance check), if
|
||||
// influences the perceived angle.
|
||||
@@ -545,6 +585,7 @@ Intersection IntersectionGenerator::AdjustForJoiningRoads(const NodeID node_at_i
|
||||
// at the target intersection, we merge to the right, so we need to shift the current
|
||||
// angle to the left
|
||||
road.turn.angle = adjustAngle(road.turn.angle, offset);
|
||||
road.turn.bearing = adjustAngle(road.turn.bearing, offset);
|
||||
}
|
||||
else if (CanMerge(node_at_next_intersection,
|
||||
next_intersection_along_road,
|
||||
@@ -559,6 +600,7 @@ Intersection IntersectionGenerator::AdjustForJoiningRoads(const NodeID node_at_i
|
||||
// at the target intersection, we merge to the left, so we need to shift the current
|
||||
// angle to the right
|
||||
road.turn.angle = adjustAngle(road.turn.angle, -offset);
|
||||
road.turn.bearing = adjustAngle(road.turn.bearing, -offset);
|
||||
}
|
||||
}
|
||||
return intersection;
|
||||
|
||||
@@ -395,18 +395,6 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
const EdgeData &in_data = node_based_graph.GetEdgeData(via_edge);
|
||||
const auto in_classification = in_data.road_classification;
|
||||
|
||||
const auto obvious_by_road_class = [](const RoadClassification in_classification,
|
||||
const RoadClassification obvious_candidate,
|
||||
const RoadClassification compare_candidate) {
|
||||
const bool has_high_priority =
|
||||
PRIORITY_DISTINCTION_FACTOR * obvious_candidate.GetPriority() <
|
||||
compare_candidate.GetPriority();
|
||||
const bool continues_on_same_class = in_classification == obvious_candidate;
|
||||
return (has_high_priority && continues_on_same_class) ||
|
||||
(!obvious_candidate.IsLowPriorityRoadClass() &&
|
||||
compare_candidate.IsLowPriorityRoadClass());
|
||||
};
|
||||
|
||||
for (std::size_t i = 1; i < intersection.size(); ++i)
|
||||
{
|
||||
const double deviation = angularDeviation(intersection[i].turn.angle, STRAIGHT_ANGLE);
|
||||
@@ -434,15 +422,19 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
node_based_graph.GetEdgeData(intersection[best_continue].turn.eid).road_classification;
|
||||
|
||||
// don't prefer low priority classes
|
||||
if (out_data.road_classification.IsLowPriorityRoadClass() &&
|
||||
if (best != 0 && out_data.road_classification.IsLowPriorityRoadClass() &&
|
||||
!current_best_class.IsLowPriorityRoadClass())
|
||||
continue;
|
||||
|
||||
const bool is_better_choice_by_priority = obvious_by_road_class(
|
||||
in_data.road_classification, out_data.road_classification, current_best_class);
|
||||
const bool is_better_choice_by_priority =
|
||||
best == 0 || obviousByRoadClass(in_data.road_classification,
|
||||
out_data.road_classification,
|
||||
current_best_class);
|
||||
|
||||
const bool other_is_better_choice_by_priority = obvious_by_road_class(
|
||||
in_data.road_classification, current_best_class, out_data.road_classification);
|
||||
const bool other_is_better_choice_by_priority =
|
||||
best != 0 && obviousByRoadClass(in_data.road_classification,
|
||||
current_best_class,
|
||||
out_data.road_classification);
|
||||
|
||||
if ((!other_is_better_choice_by_priority && deviation < best_deviation) ||
|
||||
is_better_choice_by_priority)
|
||||
@@ -503,11 +495,53 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
|
||||
// has no obvious continued road
|
||||
const auto &best_data = node_based_graph.GetEdgeData(intersection[best].turn.eid);
|
||||
if (best_continue == 0 || (!all_continues_are_narrow &&
|
||||
(num_continue_names.first >= 2 && intersection.size() >= 4)) ||
|
||||
(num_continue_names.second >= 2 && best_continue_deviation >= 2 * NARROW_TURN_ANGLE) ||
|
||||
(best_deviation != best_continue_deviation && best_deviation < FUZZY_ANGLE_DIFFERENCE &&
|
||||
!best_data.road_classification.IsRampClass()))
|
||||
|
||||
const auto check_non_continue = [&]() {
|
||||
// no continue road exists
|
||||
if (best_continue == 0)
|
||||
return true;
|
||||
|
||||
// we have multiple continues and not all are narrow (treat all the same)
|
||||
if (!all_continues_are_narrow &&
|
||||
(num_continue_names.first >= 2 && intersection.size() >= 4))
|
||||
return true;
|
||||
|
||||
// if the best continue is not narrow and we also have at least 2 possible choices, the
|
||||
// intersection size does not matter anymore
|
||||
if (num_continue_names.second >= 2 && best_continue_deviation >= 2 * NARROW_TURN_ANGLE)
|
||||
return true;
|
||||
|
||||
// continue data now most certainly exists
|
||||
const auto &continue_data =
|
||||
node_based_graph.GetEdgeData(intersection[best_continue].turn.eid);
|
||||
|
||||
if (obviousByRoadClass(in_data.road_classification,
|
||||
continue_data.road_classification,
|
||||
best_data.road_classification))
|
||||
return false;
|
||||
|
||||
if (obviousByRoadClass(in_data.road_classification,
|
||||
best_data.road_classification,
|
||||
continue_data.road_classification))
|
||||
return true;
|
||||
|
||||
// the best deviation is very straight and not a ramp
|
||||
if (best_deviation < best_continue_deviation && best_deviation < FUZZY_ANGLE_DIFFERENCE &&
|
||||
!best_data.road_classification.IsRampClass())
|
||||
return true;
|
||||
|
||||
// the continue road is of a lower priority, while the road continues on the same priority
|
||||
// with a better angle
|
||||
if (best_deviation < best_continue_deviation &&
|
||||
in_data.road_classification == best_data.road_classification &&
|
||||
continue_data.road_classification.GetPriority() >
|
||||
best_data.road_classification.GetPriority())
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (check_non_continue)
|
||||
{
|
||||
// Find left/right deviation
|
||||
// skipping over service roads
|
||||
@@ -517,9 +551,9 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
return index_candidate;
|
||||
const auto &candidate_data =
|
||||
node_based_graph.GetEdgeData(intersection[index_candidate].turn.eid);
|
||||
if (obvious_by_road_class(in_data.road_classification,
|
||||
best_data.road_classification,
|
||||
candidate_data.road_classification))
|
||||
if (obviousByRoadClass(in_data.road_classification,
|
||||
best_data.road_classification,
|
||||
candidate_data.road_classification))
|
||||
return (index_candidate + 1) % intersection.size();
|
||||
else
|
||||
return index_candidate;
|
||||
@@ -532,9 +566,9 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
return index_candidate;
|
||||
const auto candidate_data =
|
||||
node_based_graph.GetEdgeData(intersection[index_candidate].turn.eid);
|
||||
if (obvious_by_road_class(in_data.road_classification,
|
||||
best_data.road_classification,
|
||||
candidate_data.road_classification))
|
||||
if (obviousByRoadClass(in_data.road_classification,
|
||||
best_data.road_classification,
|
||||
candidate_data.road_classification))
|
||||
return index_candidate - 1;
|
||||
else
|
||||
return index_candidate;
|
||||
@@ -553,13 +587,13 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
const auto &right_data = node_based_graph.GetEdgeData(intersection[right_index].turn.eid);
|
||||
|
||||
const bool obvious_to_left =
|
||||
left_index == 0 || obvious_by_road_class(in_data.road_classification,
|
||||
best_data.road_classification,
|
||||
left_data.road_classification);
|
||||
left_index == 0 || obviousByRoadClass(in_data.road_classification,
|
||||
best_data.road_classification,
|
||||
left_data.road_classification);
|
||||
const bool obvious_to_right =
|
||||
right_index == 0 || obvious_by_road_class(in_data.road_classification,
|
||||
best_data.road_classification,
|
||||
right_data.road_classification);
|
||||
right_index == 0 || obviousByRoadClass(in_data.road_classification,
|
||||
best_data.road_classification,
|
||||
right_data.road_classification);
|
||||
|
||||
// if the best turn isn't narrow, but there is a nearly straight turn, we don't consider the
|
||||
// turn obvious
|
||||
@@ -624,9 +658,9 @@ std::size_t IntersectionHandler::findObviousTurn(const EdgeID via_edge,
|
||||
|
||||
const auto &turn_data = node_based_graph.GetEdgeData(intersection[i].turn.eid);
|
||||
const bool is_obvious_by_road_class =
|
||||
obvious_by_road_class(in_data.road_classification,
|
||||
continue_data.road_classification,
|
||||
turn_data.road_classification);
|
||||
obviousByRoadClass(in_data.road_classification,
|
||||
continue_data.road_classification,
|
||||
turn_data.road_classification);
|
||||
|
||||
// if the main road is obvious by class, we ignore the current road as a potential
|
||||
// prevention of obviousness
|
||||
|
||||
@@ -494,12 +494,6 @@ Intersection MotorwayHandler::fallback(Intersection intersection) const
|
||||
{
|
||||
for (auto &road : intersection)
|
||||
{
|
||||
const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid);
|
||||
|
||||
util::SimpleLogger().Write(logDEBUG)
|
||||
<< "road: " << toString(road) << " Name: " << out_data.name_id
|
||||
<< " Road Class: " << out_data.road_classification.ToString();
|
||||
|
||||
if (!road.entry_allowed)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -32,7 +32,8 @@ RoundaboutHandler::RoundaboutHandler(const util::NodeBasedDynamicGraph &node_bas
|
||||
name_table,
|
||||
street_name_suffix_table,
|
||||
intersection_generator),
|
||||
compressed_edge_container(compressed_edge_container), profile_properties(profile_properties)
|
||||
compressed_edge_container(compressed_edge_container), profile_properties(profile_properties),
|
||||
coordinate_extractor(node_based_graph, compressed_edge_container, node_info_list)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -180,13 +181,14 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
|
||||
|
||||
// there is a single non-roundabout edge
|
||||
const auto src_coordinate = getCoordinate(node);
|
||||
const auto next_coordinate =
|
||||
getRepresentativeCoordinate(node,
|
||||
node_based_graph.GetTarget(edge),
|
||||
edge,
|
||||
edge_data.reversed,
|
||||
compressed_edge_container,
|
||||
node_info_list);
|
||||
|
||||
const auto next_coordinate = coordinate_extractor.GetCoordinateAlongRoad(
|
||||
node,
|
||||
edge,
|
||||
edge_data.reversed,
|
||||
node_based_graph.GetTarget(edge),
|
||||
getLaneCountAtIntersection(node, node_based_graph));
|
||||
|
||||
result.push_back(
|
||||
util::coordinate_calculation::bearing(src_coordinate, next_coordinate));
|
||||
break;
|
||||
|
||||
@@ -27,49 +27,30 @@ struct TurnPossibility
|
||||
};
|
||||
|
||||
std::pair<util::guidance::EntryClass, util::guidance::BearingClass>
|
||||
classifyIntersection(NodeID nid,
|
||||
const Intersection &intersection,
|
||||
const util::NodeBasedDynamicGraph &node_based_graph,
|
||||
const extractor::CompressedEdgeContainer &compressed_geometries,
|
||||
const std::vector<extractor::QueryNode> &query_nodes)
|
||||
classifyIntersection(Intersection intersection)
|
||||
{
|
||||
if (intersection.empty())
|
||||
return {};
|
||||
|
||||
std::vector<TurnPossibility> turns;
|
||||
|
||||
const auto node_coordinate = util::Coordinate(query_nodes[nid].lon, query_nodes[nid].lat);
|
||||
|
||||
// generate a list of all turn angles between a base edge, the node and a current edge
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
const auto eid = road.turn.eid;
|
||||
const auto edge_coordinate = getRepresentativeCoordinate(
|
||||
nid, node_based_graph.GetTarget(eid), eid, false, compressed_geometries, query_nodes);
|
||||
|
||||
const double bearing =
|
||||
util::coordinate_calculation::bearing(node_coordinate, edge_coordinate);
|
||||
turns.push_back({road.entry_allowed, bearing});
|
||||
}
|
||||
|
||||
std::sort(
|
||||
turns.begin(), turns.end(), [](const TurnPossibility left, const TurnPossibility right) {
|
||||
return left.bearing < right.bearing;
|
||||
});
|
||||
std::sort(intersection.begin(),
|
||||
intersection.end(),
|
||||
[](const ConnectedRoad &left, const ConnectedRoad &right) {
|
||||
return left.turn.bearing < right.turn.bearing;
|
||||
});
|
||||
|
||||
util::guidance::EntryClass entry_class;
|
||||
util::guidance::BearingClass bearing_class;
|
||||
|
||||
const bool canBeDiscretized = [&]() {
|
||||
if (turns.size() <= 1)
|
||||
if (intersection.size() <= 1)
|
||||
return true;
|
||||
|
||||
DiscreteBearing last_discrete_bearing =
|
||||
util::guidance::BearingClass::getDiscreteBearing(std::round(turns.back().bearing));
|
||||
for (const auto turn : turns)
|
||||
DiscreteBearing last_discrete_bearing = util::guidance::BearingClass::getDiscreteBearing(
|
||||
std::round(intersection.back().turn.bearing));
|
||||
for (const auto road : intersection)
|
||||
{
|
||||
const DiscreteBearing discrete_bearing =
|
||||
util::guidance::BearingClass::getDiscreteBearing(std::round(turn.bearing));
|
||||
util::guidance::BearingClass::getDiscreteBearing(std::round(road.turn.bearing));
|
||||
if (discrete_bearing == last_discrete_bearing)
|
||||
return false;
|
||||
last_discrete_bearing = discrete_bearing;
|
||||
@@ -81,18 +62,18 @@ classifyIntersection(NodeID nid,
|
||||
std::size_t number = 0;
|
||||
if (canBeDiscretized)
|
||||
{
|
||||
if (util::guidance::BearingClass::getDiscreteBearing(turns.back().bearing) <
|
||||
util::guidance::BearingClass::getDiscreteBearing(turns.front().bearing))
|
||||
if (util::guidance::BearingClass::getDiscreteBearing(intersection.back().turn.bearing) <
|
||||
util::guidance::BearingClass::getDiscreteBearing(intersection.front().turn.bearing))
|
||||
{
|
||||
turns.insert(turns.begin(), turns.back());
|
||||
turns.pop_back();
|
||||
intersection.insert(intersection.begin(), intersection.back());
|
||||
intersection.pop_back();
|
||||
}
|
||||
for (const auto turn : turns)
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
if (turn.entry_allowed)
|
||||
if (road.entry_allowed)
|
||||
entry_class.activate(number);
|
||||
auto discrete_bearing_class =
|
||||
util::guidance::BearingClass::getDiscreteBearing(std::round(turn.bearing));
|
||||
util::guidance::BearingClass::getDiscreteBearing(std::round(road.turn.bearing));
|
||||
bearing_class.add(std::round(discrete_bearing_class *
|
||||
util::guidance::BearingClass::discrete_step_size));
|
||||
++number;
|
||||
@@ -100,11 +81,11 @@ classifyIntersection(NodeID nid,
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto turn : turns)
|
||||
for (const auto &road : intersection)
|
||||
{
|
||||
if (turn.entry_allowed)
|
||||
if (road.entry_allowed)
|
||||
entry_class.activate(number);
|
||||
bearing_class.add(std::round(turn.bearing));
|
||||
bearing_class.add(std::round(road.turn.bearing));
|
||||
++number;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,7 +136,7 @@ Intersection TurnHandler::handleThreeWayTurn(const EdgeID via_edge, Intersection
|
||||
OOOOOOO
|
||||
*/
|
||||
const auto fork_range = findFork(via_edge, intersection);
|
||||
if (fork_range.first == 1 && fork_range.second == 2)
|
||||
if (fork_range.first == 1 && fork_range.second == 2 && obvious_index == 0)
|
||||
assignFork(via_edge, intersection[2], intersection[1]);
|
||||
|
||||
/* T Intersection
|
||||
@@ -525,6 +525,7 @@ std::pair<std::size_t, std::size_t> TurnHandler::findFork(const EdgeID via_edge,
|
||||
return false;
|
||||
}();
|
||||
|
||||
// A fork can only happen between edges of similar types where none of the ones is obvious
|
||||
const bool has_compatible_classes = [&]() {
|
||||
const bool ramp_class = node_based_graph.GetEdgeData(intersection[right].turn.eid)
|
||||
.road_classification.IsLinkClass();
|
||||
@@ -533,6 +534,27 @@ std::pair<std::size_t, std::size_t> TurnHandler::findFork(const EdgeID via_edge,
|
||||
node_based_graph.GetEdgeData(intersection[index].turn.eid)
|
||||
.road_classification.IsLinkClass())
|
||||
return false;
|
||||
|
||||
const auto in_classification =
|
||||
node_based_graph.GetEdgeData(intersection[0].turn.eid).road_classification;
|
||||
for (std::size_t base_index = right; base_index <= left; ++base_index)
|
||||
{
|
||||
const auto base_classification =
|
||||
node_based_graph.GetEdgeData(intersection[base_index].turn.eid)
|
||||
.road_classification;
|
||||
for (std::size_t compare_index = right; compare_index <= left; ++compare_index)
|
||||
{
|
||||
if (base_index == compare_index)
|
||||
continue;
|
||||
|
||||
const auto compare_classification =
|
||||
node_based_graph.GetEdgeData(intersection[compare_index].turn.eid)
|
||||
.road_classification;
|
||||
if (obviousByRoadClass(
|
||||
in_classification, base_classification, compare_classification))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
|
||||
@@ -657,20 +679,20 @@ void TurnHandler::handleDistinctConflict(const EdgeID via_edge,
|
||||
|
||||
if (getTurnDirection(left.turn.angle) == DirectionModifier::Right)
|
||||
{
|
||||
if (angularDeviation(left.turn.angle, 90) > angularDeviation(right.turn.angle, 90))
|
||||
{
|
||||
left.turn.instruction = {left_type, DirectionModifier::SlightRight};
|
||||
right.turn.instruction = {right_type, DirectionModifier::Right};
|
||||
}
|
||||
else
|
||||
if (angularDeviation(left.turn.angle, 85) >= angularDeviation(right.turn.angle, 85))
|
||||
{
|
||||
left.turn.instruction = {left_type, DirectionModifier::Right};
|
||||
right.turn.instruction = {right_type, DirectionModifier::SharpRight};
|
||||
}
|
||||
else
|
||||
{
|
||||
left.turn.instruction = {left_type, DirectionModifier::SlightRight};
|
||||
right.turn.instruction = {right_type, DirectionModifier::Right};
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (angularDeviation(left.turn.angle, 270) > angularDeviation(right.turn.angle, 270))
|
||||
if (angularDeviation(left.turn.angle, 265) >= angularDeviation(right.turn.angle, 265))
|
||||
{
|
||||
left.turn.instruction = {left_type, DirectionModifier::SharpLeft};
|
||||
right.turn.instruction = {right_type, DirectionModifier::Left};
|
||||
|
||||
@@ -169,7 +169,10 @@ void LuaScriptingEnvironment::InitContext(LuaScriptingContext &context)
|
||||
&guidance::RoadClassification::SetLowPriorityFlag)
|
||||
.property("road_priority_class",
|
||||
&guidance::RoadClassification::GetClass,
|
||||
&guidance::RoadClassification::SetClass),
|
||||
&guidance::RoadClassification::SetClass)
|
||||
.property("num_lanes",
|
||||
&guidance::RoadClassification::GetNumberOfLanes,
|
||||
&guidance::RoadClassification::SetNumberOfLanes),
|
||||
|
||||
luabind::class_<ExtractionWay>("ResultWay")
|
||||
// .def(luabind::constructor<>())
|
||||
|
||||
+20
-4
@@ -242,6 +242,10 @@ Storage::ReturnCode Storage::Run(int max_wait)
|
||||
number_of_original_edges);
|
||||
shared_layout_ptr->SetBlockSize<extractor::TravelMode>(SharedDataLayout::TRAVEL_MODE,
|
||||
number_of_original_edges);
|
||||
shared_layout_ptr->SetBlockSize<util::guidance::TurnBearing>(SharedDataLayout::PRE_TURN_BEARING,
|
||||
number_of_original_edges);
|
||||
shared_layout_ptr->SetBlockSize<util::guidance::TurnBearing>(
|
||||
SharedDataLayout::POST_TURN_BEARING, number_of_original_edges);
|
||||
shared_layout_ptr->SetBlockSize<extractor::guidance::TurnInstruction>(
|
||||
SharedDataLayout::TURN_INSTRUCTION, number_of_original_edges);
|
||||
shared_layout_ptr->SetBlockSize<LaneDataID>(SharedDataLayout::LANE_DATA_ID,
|
||||
@@ -550,6 +554,12 @@ Storage::ReturnCode Storage::Run(int max_wait)
|
||||
extractor::TravelMode *travel_mode_ptr =
|
||||
shared_layout_ptr->GetBlockPtr<extractor::TravelMode, true>(shared_memory_ptr,
|
||||
SharedDataLayout::TRAVEL_MODE);
|
||||
util::guidance::TurnBearing *pre_turn_bearing_ptr =
|
||||
shared_layout_ptr->GetBlockPtr<util::guidance::TurnBearing, true>(
|
||||
shared_memory_ptr, SharedDataLayout::PRE_TURN_BEARING);
|
||||
util::guidance::TurnBearing *post_turn_bearing_ptr =
|
||||
shared_layout_ptr->GetBlockPtr<util::guidance::TurnBearing, true>(
|
||||
shared_memory_ptr, SharedDataLayout::POST_TURN_BEARING);
|
||||
|
||||
LaneDataID *lane_data_id_ptr = shared_layout_ptr->GetBlockPtr<LaneDataID, true>(
|
||||
shared_memory_ptr, SharedDataLayout::LANE_DATA_ID);
|
||||
@@ -571,6 +581,8 @@ Storage::ReturnCode Storage::Run(int max_wait)
|
||||
lane_data_id_ptr[i] = current_edge_data.lane_data_id;
|
||||
turn_instructions_ptr[i] = current_edge_data.turn_instruction;
|
||||
entry_class_id_ptr[i] = current_edge_data.entry_classid;
|
||||
pre_turn_bearing_ptr[i] = current_edge_data.pre_turn_bearing;
|
||||
post_turn_bearing_ptr[i] = current_edge_data.post_turn_bearing;
|
||||
}
|
||||
edges_input_stream.close();
|
||||
|
||||
@@ -774,20 +786,24 @@ Storage::ReturnCode Storage::Run(int max_wait)
|
||||
static_cast<SharedDataTimestamp *>(data_type_memory->Ptr());
|
||||
|
||||
{
|
||||
boost::interprocess::scoped_lock<boost::interprocess::named_upgradable_mutex>
|
||||
boost::interprocess::scoped_lock<boost::interprocess::named_upgradable_mutex>
|
||||
current_regions_exclusive_lock;
|
||||
|
||||
if (max_wait > 0)
|
||||
{
|
||||
util::SimpleLogger().Write() << "Waiting for " << max_wait << " seconds to write new dataset timestamp";
|
||||
auto end_time = boost::posix_time::microsec_clock::universal_time() + boost::posix_time::seconds(max_wait);
|
||||
util::SimpleLogger().Write() << "Waiting for " << max_wait
|
||||
<< " seconds to write new dataset timestamp";
|
||||
auto end_time = boost::posix_time::microsec_clock::universal_time() +
|
||||
boost::posix_time::seconds(max_wait);
|
||||
current_regions_exclusive_lock =
|
||||
boost::interprocess::scoped_lock<boost::interprocess::named_upgradable_mutex>(
|
||||
std::move(current_regions_lock), end_time);
|
||||
|
||||
if (!current_regions_exclusive_lock.owns())
|
||||
{
|
||||
util::SimpleLogger().Write(logWARNING) << "Aquiring the lock timed out after " << max_wait << " seconds. Claiming the lock by force.";
|
||||
util::SimpleLogger().Write(logWARNING) << "Aquiring the lock timed out after "
|
||||
<< max_wait
|
||||
<< " seconds. Claiming the lock by force.";
|
||||
current_regions_lock.unlock();
|
||||
current_regions_lock.release();
|
||||
storage::SharedBarriers::resetCurrentRegions();
|
||||
|
||||
@@ -292,6 +292,29 @@ Coordinate interpolateLinear(double factor, const Coordinate from, const Coordin
|
||||
return {std::move(interpolated_lon), std::move(interpolated_lat)};
|
||||
}
|
||||
|
||||
// compute the signed area of a triangle
|
||||
double signedArea(const Coordinate first_coordinate,
|
||||
const Coordinate second_coordinate,
|
||||
const Coordinate third_coordinate)
|
||||
{
|
||||
const auto lat_1 = static_cast<double>(toFloating(first_coordinate.lat));
|
||||
const auto lon_1 = static_cast<double>(toFloating(first_coordinate.lon));
|
||||
const auto lat_2 = static_cast<double>(toFloating(second_coordinate.lat));
|
||||
const auto lon_2 = static_cast<double>(toFloating(second_coordinate.lon));
|
||||
const auto lat_3 = static_cast<double>(toFloating(third_coordinate.lat));
|
||||
const auto lon_3 = static_cast<double>(toFloating(third_coordinate.lon));
|
||||
return 0.5 * (-lon_2 * lat_1 + lon_3 * lat_1 + lon_1 * lat_2 - lon_3 * lat_2 - lon_1 * lat_3 +
|
||||
lon_2 * lat_3);
|
||||
}
|
||||
|
||||
// check if a set of three coordinates is given in CCW order
|
||||
bool isCCW(const Coordinate first_coordinate,
|
||||
const Coordinate second_coordinate,
|
||||
const Coordinate third_coordinate)
|
||||
{
|
||||
return signedArea(first_coordinate, second_coordinate, third_coordinate) > 0;
|
||||
}
|
||||
|
||||
} // ns coordinate_calculation
|
||||
} // ns util
|
||||
} // ns osrm
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
#include "util/guidance/turn_bearing.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
constexpr double bearing_scale = 360.0 / 256.0;
|
||||
|
||||
// discretizes a bearing into distinct units of 1.4 degrees
|
||||
TurnBearing::TurnBearing(const double value) : bearing(value / bearing_scale)
|
||||
{
|
||||
BOOST_ASSERT_MSG(value >= 0 && value <= 360.0, "Bearing value needs to be between 0 and 360");
|
||||
}
|
||||
|
||||
double TurnBearing::Get() const { return bearing * bearing_scale; }
|
||||
|
||||
} // namespace guidance
|
||||
} // namespace util
|
||||
} // namespace osrm
|
||||
Reference in New Issue
Block a user