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:
@@ -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<>())
|
||||
|
||||
Reference in New Issue
Block a user