refactor merging of segregated roads

adjust to generalFindMaximum function
moved parallel detection to ratio/absolute based regression testing
considerably improved detection quality using normalised regression lines
only follow initial direction/narrow turns for parallel detection
This commit is contained in:
Moritz Kobitzsch
2016-12-06 13:22:51 +01:00
parent f7ad2e1e26
commit e6ff17ab2a
40 changed files with 2397 additions and 949 deletions
+21 -23
View File
@@ -260,8 +260,8 @@ void closeOffRoundabout(const bool on_roundabout,
TurnType::EnterRoundaboutIntersectionAtExit)
{
BOOST_ASSERT(!propagation_step.intersections.empty());
const double angle = util::angleBetweenBearings(
util::reverseBearing(entry_intersection.bearings[entry_intersection.in]),
const double angle = util::bearing::angleBetween(
util::bearing::reverse(entry_intersection.bearings[entry_intersection.in]),
exit_bearing);
auto bearings = propagation_step.intersections.front().bearings;
@@ -306,7 +306,7 @@ bool isUTurn(const RouteStep &in_step, const RouteStep &out_step, const RouteSte
(isLinkroad(in_step) && out_step.name_id != EMPTY_NAMEID &&
pre_in_step.name_id != EMPTY_NAMEID && !isNoticeableNameChange(pre_in_step, out_step));
const bool takes_u_turn = bearingsAreReversed(
util::reverseBearing(
util::bearing::reverse(
in_step.intersections.front().bearings[in_step.intersections.front().in]),
out_step.intersections.front().bearings[out_step.intersections.front().out]);
@@ -318,20 +318,20 @@ double findTotalTurnAngle(const RouteStep &entry_step, const RouteStep &exit_ste
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::reverseBearing(exit_intersection.bearings[exit_intersection.in]);
util::bearing::reverse(exit_intersection.bearings[exit_intersection.in]);
const auto entry_intersection = entry_step.intersections.front();
const auto entry_step_entry_bearing =
util::reverseBearing(entry_intersection.bearings[entry_intersection.in]);
util::bearing::reverse(entry_intersection.bearings[entry_intersection.in]);
const auto entry_step_exit_bearing = entry_intersection.bearings[entry_intersection.out];
const auto exit_angle =
util::angleBetweenBearings(exit_step_entry_bearing, exit_step_exit_bearing);
util::bearing::angleBetween(exit_step_entry_bearing, exit_step_exit_bearing);
const auto entry_angle =
util::angleBetweenBearings(entry_step_entry_bearing, entry_step_exit_bearing);
util::bearing::angleBetween(entry_step_entry_bearing, entry_step_exit_bearing);
const double total_angle =
util::angleBetweenBearings(entry_step_entry_bearing, exit_step_exit_bearing);
util::bearing::angleBetween(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)) &&
@@ -391,7 +391,7 @@ void collapseUTurn(std::vector<RouteStep> &steps,
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
// additionall collapse a name-change as well
const auto next_step_index = step_index + 1;
const bool continues_with_name_change =
(next_step_index < steps.size()) && compatible(steps[step_index], steps[next_step_index]) &&
@@ -531,18 +531,18 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
if (continue_or_suppressed || turning_name)
{
const auto in_bearing = [](const RouteStep &step) {
return util::reverseBearing(
return util::bearing::reverse(
step.intersections.front().bearings[step.intersections.front().in]);
};
const auto out_bearing = [](const RouteStep &step) {
return step.intersections.front().bearings[step.intersections.front().out];
};
const auto first_angle = util::angleBetweenBearings(in_bearing(one_back_step),
out_bearing(one_back_step));
const auto second_angle =
util::angleBetweenBearings(in_bearing(current_step), out_bearing(current_step));
const auto bearing_turn_angle = util::angleBetweenBearings(
const auto first_angle = util::bearing::angleBetween(in_bearing(one_back_step),
out_bearing(one_back_step));
const auto second_angle = util::bearing::angleBetween(in_bearing(current_step),
out_bearing(current_step));
const auto bearing_turn_angle = util::bearing::angleBetween(
in_bearing(one_back_step), out_bearing(current_step));
// When looking at an intersection, some angles, even though present, feel more like
@@ -676,7 +676,7 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
};
// If we Merge onto the same street, we end up with a u-turn in some cases
if (bearingsAreReversed(util::reverseBearing(getBearing(true, one_back_step)),
if (bearingsAreReversed(util::bearing::reverse(getBearing(true, one_back_step)),
getBearing(false, current_step)))
{
steps[one_back_index].maneuver.instruction.direction_modifier =
@@ -760,7 +760,7 @@ bool isStaggeredIntersection(const std::vector<RouteStep> &steps,
const auto &intersection = step.intersections.front();
const auto entry_bearing = intersection.bearings[intersection.in];
const auto exit_bearing = intersection.bearings[intersection.out];
return util::angleBetweenBearings(entry_bearing, exit_bearing);
return util::bearing::angleBetween(entry_bearing, exit_bearing);
};
// Instead of using turn modifiers (e.g. as in isRightTurn) we want to be more strict here.
@@ -915,7 +915,7 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
// unterminated roundabout
// Move backwards through the instructions until the start and remove the exit number
// A roundabout without exit translates to enter-roundabout.
// A roundabout without exit translates to enter-roundabout
if (has_entered_roundabout || on_roundabout)
{
fixFinalRoundabout(steps);
@@ -1238,10 +1238,8 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
if (zero_length_step)
{
// since we are not only checking for epsilon but for a full meter, we can have multiple
// coordinates here.
// move offsets to front
// geometry offsets have to be adjusted. Move all offsets to the front and reduce by
// one. (This is an inplace forward one and reduce by one)
// coordinates here. Move all offsets to the front and reduce by one. (This is an
// inplace forward one and reduce by one)
std::transform(geometry.segment_offsets.begin() + 1,
geometry.segment_offsets.end(),
geometry.segment_offsets.begin(),
@@ -1378,7 +1376,7 @@ void trimShortSegments(std::vector<RouteStep> &steps, LegGeometry &geometry)
geometry.locations[next_to_last_step.geometry_end - 2],
geometry.locations[last_step.geometry_begin]));
last_step.maneuver.bearing_before = bearing;
last_step.intersections.front().bearings.front() = util::reverseBearing(bearing);
last_step.intersections.front().bearings.front() = util::bearing::reverse(bearing);
}
BOOST_ASSERT(steps.back().geometry_end == geometry.locations.size());
+2 -2
View File
@@ -415,9 +415,9 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
turn_analysis.GetIntersectionGenerator().TransformIntersectionShapeIntoView(
node_along_road_entering,
incoming_edge,
shape_result.normalised_intersection_shape,
shape_result.annotated_normalized_shape.normalized_shape,
shape_result.intersection_shape,
shape_result.merging_map);
shape_result.annotated_normalized_shape.performed_merges);
auto intersection = turn_analysis.AssignTurnTypes(
node_along_road_entering, incoming_edge, intersection_with_flags_and_angles);
+1
View File
@@ -251,6 +251,7 @@ int Extractor::run(ScriptingEnvironment &scripting_environment)
std::vector<bool> node_is_startpoint;
std::vector<EdgeWeight> edge_based_node_weights;
std::vector<QueryNode> internal_to_external_node_map;
auto graph_size = BuildEdgeExpandedGraph(scripting_environment,
internal_to_external_node_map,
edge_based_node_list,
+2 -2
View File
@@ -139,8 +139,6 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
return;
}
// FIXME this need to be moved into the profiles
const guidance::RoadClassification road_classification = parsed_way.road_classification;
const auto laneStringToDescription = [](const std::string &lane_string) -> TurnLaneDescription {
if (lane_string.empty())
return {};
@@ -237,6 +235,8 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti
const auto turn_lane_id_forward = requestId(parsed_way.turn_lanes_forward);
const auto turn_lane_id_backward = requestId(parsed_way.turn_lanes_backward);
const auto road_classification = parsed_way.road_classification;
const constexpr auto MAX_STRING_LENGTH = 255u;
// Get the unique identifier for the street name, destination, and ref
const auto name_iterator = string_map.find(
-69
View File
@@ -1,69 +0,0 @@
#include "extractor/geojson_debug_policies.hpp"
#include "util/coordinate.hpp"
#include "util/geojson_debug_policy_toolkit.hpp"
#include <algorithm>
namespace osrm
{
namespace extractor
{
IntersectionPrinter::IntersectionPrinter(
const util::NodeBasedDynamicGraph &node_based_graph,
const std::vector<extractor::QueryNode> &node_coordinates,
const extractor::guidance::CoordinateExtractor &coordinate_extractor)
: node_based_graph(node_based_graph), node_coordinates(node_coordinates),
coordinate_extractor(coordinate_extractor)
{
}
util::json::Array IntersectionPrinter::
operator()(const NodeID intersection_node,
const extractor::guidance::Intersection &intersection,
const boost::optional<util::json::Object> &node_style,
const boost::optional<util::json::Object> &way_style) const
{
// request the number of lanes. This process needs to be in sync with what happens over at
// intersection_generator
const auto intersection_lanes = intersection.getHighestConnectedLaneCount(node_based_graph);
std::vector<util::Coordinate> coordinates;
coordinates.reserve(intersection.size());
coordinates.push_back(node_coordinates[intersection_node]);
const auto road_to_coordinate = [&](const extractor::guidance::ConnectedRoad &connected_road) {
const constexpr auto FORWARD = false;
const auto to_node = node_based_graph.GetTarget(connected_road.eid);
return coordinate_extractor.GetCoordinateAlongRoad(
intersection_node, connected_road.eid, FORWARD, to_node, intersection_lanes);
};
std::transform(intersection.begin(),
intersection.end(),
std::back_inserter(coordinates),
road_to_coordinate);
util::json::Array features;
features.values.push_back(
util::makeFeature("MultiPoint", makeJsonArray(coordinates), node_style));
if (coordinates.size() > 1)
{
std::vector<util::Coordinate> line_coordinates(2);
line_coordinates[0] = coordinates.front();
const auto coordinate_to_line = [&](const util::Coordinate coordinate) {
line_coordinates[1] = coordinate;
return util::makeFeature("LineString", makeJsonArray(line_coordinates), way_style);
};
std::transform(std::next(coordinates.begin()),
coordinates.end(),
std::back_inserter(features.values),
coordinate_to_line);
}
return features;
}
} /* namespace extractor */
} /* namespace osrm */
+49 -48
View File
@@ -31,7 +31,6 @@ 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 = 40.0;
// The count of lanes assumed when no lanes are present. Since most roads will have lanes for both
@@ -650,7 +649,7 @@ bool CoordinateExtractor::IsCurve(const std::vector<util::Coordinate> &coordinat
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);
const auto total_angle = util::angularDeviation(begin_bearing, end_bearing);
return total_angle > 0.5 * NARROW_TURN_ANGLE;
}();
@@ -754,59 +753,61 @@ bool CoordinateExtractor::IsCurve(const std::vector<util::Coordinate> &coordinat
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;
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);
// 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);
const auto detect_invalid_curve = [&](const double previous_angle,
const double current_angle) {
const auto both_actually_turn =
(util::angularDeviation(previous_angle, STRAIGHT_ANGLE) > FUZZY_ANGLE_DIFFERENCE) &&
(util::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;
// 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)
const bool is_straight = util::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)
{
// 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;
};
} // 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);
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();
}();
// 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);
}
@@ -1147,8 +1148,8 @@ CoordinateExtractor::RegressionLine(const std::vector<util::Coordinate> &coordin
return {coordinates.front(), coordinates.back()};
// compute the regression vector based on the sum of least squares
const auto regression_line =
util::coordinate_calculation::leastSquareRegression(sampled_coordinates);
const auto regression_line = util::coordinate_calculation::leastSquareRegression(
sampled_coordinates.begin(), sampled_coordinates.end());
const auto coord_between_front =
util::coordinate_calculation::projectPointOnSegment(
regression_line.first, regression_line.second, coordinates.front())
+21 -1
View File
@@ -34,7 +34,7 @@ void ConnectedRoad::mirror()
DirectionModifier::MaxDirectionModifier,
"The list of mirrored modifiers needs to match the available modifiers in size.");
if (angularDeviation(angle, 0) > std::numeric_limits<double>::epsilon())
if (util::angularDeviation(angle, 0) > std::numeric_limits<double>::epsilon())
{
angle = 360 - angle;
instruction.direction_modifier = mirrored_modifiers[instruction.direction_modifier];
@@ -48,6 +48,26 @@ ConnectedRoad ConnectedRoad::getMirroredCopy() const
return copy;
}
std::string toString(const IntersectionShapeData &shape)
{
std::string result =
"[shape] " + std::to_string(shape.eid) + " bearing: " + std::to_string(shape.bearing);
return result;
}
std::string toString(const IntersectionViewData &view)
{
std::string result = "[view] ";
result += std::to_string(view.eid);
result += " allows entry: ";
result += std::to_string(view.entry_allowed);
result += " angle: ";
result += std::to_string(view.angle);
result += " bearing: ";
result += std::to_string(view.bearing);
return result;
}
std::string toString(const ConnectedRoad &road)
{
std::string result = "[connection] ";
@@ -1,5 +1,8 @@
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/geojson_debug_policies.hpp"
#include "util/geojson_debug_logger.hpp"
#include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp"
@@ -76,7 +79,9 @@ IntersectionGenerator::ComputeIntersectionShape(const NodeID node_at_center_of_i
node_at_center_of_intersection, edge_connected_to_intersection, !INVERT, to_node);
const auto segment_length = util::coordinate_calculation::getLength(
coordinates, util::coordinate_calculation::haversineDistance);
coordinates.begin(),
coordinates.end(),
util::coordinate_calculation::haversineDistance);
const auto extract_coordinate = [&](const NodeID from_node,
const EdgeID via_eid,
@@ -116,9 +121,9 @@ IntersectionGenerator::ComputeIntersectionShape(const NodeID node_at_center_of_i
return node_based_graph.GetTarget(data.eid) == *sorting_base;
});
if (itr != intersection.end())
return util::reverseBearing(itr->bearing);
return util::bearing::reverse(itr->bearing);
}
return util::reverseBearing(intersection.begin()->bearing);
return util::bearing::reverse(intersection.begin()->bearing);
}();
std::sort(
intersection.begin(), intersection.end(), makeCompareShapeDataByBearing(base_bearing));
@@ -154,8 +159,8 @@ IntersectionView IntersectionGenerator::GetConnectedRoads(const NodeID from_node
return TransformIntersectionShapeIntoView(from_node, via_eid, std::move(intersection));
}
std::pair<NodeID, EdgeID> IntersectionGenerator::SkipDegreeTwoNodes(const NodeID starting_node,
const EdgeID via_edge) const
IntersectionGenerationParameters
IntersectionGenerator::SkipDegreeTwoNodes(const NodeID starting_node, const EdgeID via_edge) const
{
NodeID query_node = starting_node;
EdgeID query_edge = via_edge;
@@ -177,16 +182,17 @@ std::pair<NodeID, EdgeID> IntersectionGenerator::SkipDegreeTwoNodes(const NodeID
visited_nodes.insert(query_node);
const auto next_node = node_based_graph.GetTarget(query_edge);
const auto next_edge = get_next_edge(query_node, query_edge);
query_node = next_node;
query_edge = next_edge;
if (!node_based_graph.GetEdgeData(query_edge)
.IsCompatibleTo(node_based_graph.GetEdgeData(next_edge)) ||
node_based_graph.GetTarget(next_edge) == starting_node)
break;
query_node = next_node;
query_edge = next_edge;
}
return std::make_pair(query_node, query_edge);
return {query_node, query_edge};
}
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
@@ -205,9 +211,9 @@ IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
const NodeID previous_node,
const EdgeID entering_via_edge,
const IntersectionShape &normalised_intersection,
const IntersectionShape &normalized_intersection,
const IntersectionShape &intersection,
const std::vector<std::pair<EdgeID, EdgeID>> &performed_merges) const
const std::vector<IntersectionNormalizationOperation> &performed_merges) const
{
const auto node_at_intersection = node_based_graph.GetTarget(entering_via_edge);
@@ -259,40 +265,40 @@ IntersectionView IntersectionGenerator::TransformIntersectionShapeIntoView(
const auto uturn_bearing = [&]() {
const auto merge_entry = std::find_if(
performed_merges.begin(), performed_merges.end(), [&uturn_edge_itr](const auto entry) {
return entry.first == uturn_edge_itr->eid;
return entry.merged_eid == uturn_edge_itr->eid;
});
if (merge_entry != performed_merges.end())
{
const auto merged_into_id = merge_entry->second;
const auto merged_into_id = merge_entry->into_eid;
const auto merged_u_turn = std::find_if(
normalised_intersection.begin(),
normalised_intersection.end(),
normalized_intersection.begin(),
normalized_intersection.end(),
[&](const IntersectionShapeData &road) { return road.eid == merged_into_id; });
BOOST_ASSERT(merged_u_turn != normalised_intersection.end());
return util::reverseBearing(merged_u_turn->bearing);
BOOST_ASSERT(merged_u_turn != normalized_intersection.end());
return util::bearing::reverse(merged_u_turn->bearing);
}
else
{
const auto uturn_edge_at_normalised_intersection_itr =
std::find_if(normalised_intersection.begin(),
normalised_intersection.end(),
const auto uturn_edge_at_normalized_intersection_itr =
std::find_if(normalized_intersection.begin(),
normalized_intersection.end(),
connect_to_previous_node);
BOOST_ASSERT(uturn_edge_at_normalised_intersection_itr !=
normalised_intersection.end());
return util::reverseBearing(uturn_edge_at_normalised_intersection_itr->bearing);
BOOST_ASSERT(uturn_edge_at_normalized_intersection_itr !=
normalized_intersection.end());
return util::bearing::reverse(uturn_edge_at_normalized_intersection_itr->bearing);
}
}();
IntersectionView intersection_view;
intersection_view.reserve(normalised_intersection.size());
std::transform(normalised_intersection.begin(),
normalised_intersection.end(),
intersection_view.reserve(normalized_intersection.size());
std::transform(normalized_intersection.begin(),
normalized_intersection.end(),
std::back_inserter(intersection_view),
[&](const IntersectionShapeData &road) {
return IntersectionViewData(
road,
is_allowed_turn(road),
util::angleBetweenBearings(uturn_bearing, road.bearing));
util::bearing::angleBetween(uturn_bearing, road.bearing));
});
const auto uturn_edge_at_intersection_view_itr =
@@ -5,10 +5,14 @@
#include "util/guidance/name_announcements.hpp"
#include "util/log.hpp"
#include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp"
#include <algorithm>
#include <cstddef>
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
using osrm::extractor::guidance::getTurnDirection;
using osrm::util::angularDeviation;
namespace osrm
@@ -384,19 +388,17 @@ IntersectionHandler::getNextIntersection(const NodeID at, const EdgeID via) cons
// Starting at node `a` via edge `e0` the intersection generator returns the intersection at `c`
// writing `tl` (traffic signal) node and the edge `e1` which has the intersection as target.
NodeID node = SPECIAL_NODEID;
EdgeID edge = SPECIAL_EDGEID;
std::tie(node, edge) = intersection_generator.SkipDegreeTwoNodes(at, via);
auto intersection = intersection_generator(node, edge);
const auto intersection_parameters = intersection_generator.SkipDegreeTwoNodes(at, via);
// This should never happen, guard against nevertheless
if (node == SPECIAL_NODEID || edge == SPECIAL_EDGEID)
if (intersection_parameters.nid == SPECIAL_NODEID ||
intersection_parameters.via_eid == SPECIAL_EDGEID)
{
return boost::none;
}
auto intersection_node = node_based_graph.GetTarget(edge);
auto intersection =
intersection_generator(intersection_parameters.nid, intersection_parameters.via_eid);
auto intersection_node = node_based_graph.GetTarget(intersection_parameters.via_eid);
if (intersection.size() <= 2 || intersection.isTrafficSignalOrBarrier())
{
@@ -1,7 +1,6 @@
#include "extractor/guidance/intersection_normalizer.hpp"
#include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/guidance/name_announcements.hpp"
#include <tuple>
#include <utility>
@@ -21,34 +20,40 @@ IntersectionNormalizer::IntersectionNormalizer(
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table,
const IntersectionGenerator &intersection_generator)
: node_based_graph(node_based_graph), node_coordinates(node_coordinates),
name_table(name_table), street_name_suffix_table(street_name_suffix_table),
intersection_generator(intersection_generator)
: node_based_graph(node_based_graph), intersection_generator(intersection_generator),
mergable_road_detector(node_based_graph,
node_coordinates,
intersection_generator,
intersection_generator.GetCoordinateExtractor(),
name_table,
street_name_suffix_table)
{
}
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>> IntersectionNormalizer::
IntersectionNormalizer::NormalizationResult IntersectionNormalizer::
operator()(const NodeID node_at_intersection, IntersectionShape intersection) const
{
const auto intersection_copy = intersection;
auto merged_shape_and_merges =
MergeSegregatedRoads(node_at_intersection, std::move(intersection));
merged_shape_and_merges.first = AdjustBearingsForMergeAtDestination(
node_at_intersection, std::move(merged_shape_and_merges.first));
merged_shape_and_merges.normalized_shape = AdjustBearingsForMergeAtDestination(
node_at_intersection, std::move(merged_shape_and_merges.normalized_shape));
return merged_shape_and_merges;
}
bool IntersectionNormalizer::CanMerge(const NodeID intersection_node,
const IntersectionShape &intersection,
std::size_t first_index,
std::size_t second_index) const
std::size_t fist_index_in_ccw,
std::size_t second_index_in_ccw) const
{
BOOST_ASSERT(((first_index + 1) % intersection.size()) == second_index);
BOOST_ASSERT(((fist_index_in_ccw + 1) % intersection.size()) == second_index_in_ccw);
// call wrapper to capture intersection_node and intersection
const auto mergable = [this, intersection_node, &intersection](const std::size_t left_index,
const std::size_t right_index) {
return InnerCanMerge(intersection_node, intersection, left_index, right_index);
};
// don't merge on degree two, since it's most likely a bollard/traffic light or a round way
if (intersection.size() <= 2)
return false;
const auto can_merge = mergable_road_detector.CanMergeRoad(
intersection_node, intersection[fist_index_in_ccw], intersection[second_index_in_ccw]);
/*
* Merging should never depend on order/never merge more than two roads. To ensure that we don't
@@ -56,143 +61,71 @@ bool IntersectionNormalizer::CanMerge(const NodeID intersection_node,
* parking lots/border checkpoints), we check if the neigboring roads would be merged as well.
* In that case, we cannot merge, since we would end up merging multiple items together
*/
if (mergable(first_index, second_index))
{
const auto is_distinct_merge =
!mergable(second_index, (second_index + 1) % intersection.size()) &&
!mergable((first_index + intersection.size() - 1) % intersection.size(), first_index) &&
!mergable(second_index,
(first_index + intersection.size() - 1) % intersection.size()) &&
!mergable(first_index, (second_index + 1) % intersection.size());
return is_distinct_merge;
}
else
return false;
}
// Checks for mergability of two ways that represent the same intersection. For further
// information see interface documentation in header.
bool IntersectionNormalizer::InnerCanMerge(const NodeID node_at_intersection,
const IntersectionShape &intersection,
std::size_t first_index,
std::size_t second_index) const
{
const auto &first_data = node_based_graph.GetEdgeData(intersection[first_index].eid);
const auto &second_data = node_based_graph.GetEdgeData(intersection[second_index].eid);
// only merge named ids
if (first_data.name_id == EMPTY_NAMEID || second_data.name_id == EMPTY_NAMEID)
return false;
// need to be same name
if (util::guidance::requiresNameAnnounced(
first_data.name_id, second_data.name_id, name_table, street_name_suffix_table))
return false;
// needs to be symmetrical for names
if (util::guidance::requiresNameAnnounced(
second_data.name_id, first_data.name_id, name_table, street_name_suffix_table))
return false;
// compatibility is required
if (first_data.travel_mode != second_data.travel_mode)
return false;
if (first_data.road_classification != second_data.road_classification)
return false;
// may not be on a roundabout
if (first_data.roundabout || second_data.roundabout || first_data.circular ||
second_data.circular)
return false;
// exactly one of them has to be reversed
if (first_data.reversed == second_data.reversed)
return false;
// mergeable if the angle is not too big
const auto angle_between =
angularDeviation(intersection[first_index].bearing, intersection[second_index].bearing);
const auto coordinate_at_intersection = node_coordinates[node_at_intersection];
if (angle_between >= 120)
return false;
const auto isValidYArm = [this, intersection, coordinate_at_intersection, node_at_intersection](
const std::size_t index, const std::size_t other_index) {
const auto GetActualTarget = [&](const std::size_t index) {
EdgeID edge_id;
std::tie(std::ignore, edge_id) = intersection_generator.SkipDegreeTwoNodes(
node_at_intersection, intersection[index].eid);
return node_based_graph.GetTarget(edge_id);
};
const auto target_id = GetActualTarget(index);
const auto other_target_id = GetActualTarget(other_index);
if (target_id == node_at_intersection || other_target_id == node_at_intersection)
return false;
const auto coordinate_at_target = node_coordinates[target_id];
const auto coordinate_at_other_target = node_coordinates[other_target_id];
const auto turn_bearing =
util::coordinate_calculation::bearing(coordinate_at_intersection, coordinate_at_target);
const auto other_turn_bearing = util::coordinate_calculation::bearing(
coordinate_at_intersection, coordinate_at_other_target);
// fuzzy becomes narrower due to minor differences in angle computations, yay floating point
const bool becomes_narrower =
angularDeviation(turn_bearing, other_turn_bearing) < NARROW_TURN_ANGLE &&
angularDeviation(turn_bearing, other_turn_bearing) <=
angularDeviation(intersection[index].bearing, intersection[other_index].bearing) +
MAXIMAL_ALLOWED_NO_TURN_DEVIATION;
return becomes_narrower;
const auto is_distinct = [&]() {
const auto next_index_in_ccw = (second_index_in_ccw + 1) % intersection.size();
const auto distinct_to_next_in_ccw = mergable_road_detector.IsDistinctFrom(
intersection[second_index_in_ccw], intersection[next_index_in_ccw]);
const auto prev_index_in_ccw =
(fist_index_in_ccw + intersection.size() - 1) % intersection.size();
const auto distinct_to_prev_in_ccw = mergable_road_detector.IsDistinctFrom(
intersection[prev_index_in_ccw], intersection[fist_index_in_ccw]);
return distinct_to_next_in_ccw && distinct_to_prev_in_ccw;
};
const bool is_y_arm_first = isValidYArm(first_index, second_index);
const bool is_y_arm_second = isValidYArm(second_index, first_index);
// use lazy evaluation to check only if mergable
return can_merge && is_distinct();
}
// Only merge valid y-arms
if (!is_y_arm_first || !is_y_arm_second)
return false;
IntersectionNormalizationOperation
IntersectionNormalizer::DetermineMergeDirection(const IntersectionShapeData &lhs,
const IntersectionShapeData &rhs) const
{
if (node_based_graph.GetEdgeData(lhs.eid).reversed)
return {lhs.eid, rhs.eid};
else
return {rhs.eid, lhs.eid};
}
if (angle_between < 60)
return true;
IntersectionShapeData IntersectionNormalizer::MergeRoads(const IntersectionShapeData &into,
const IntersectionShapeData &from) const
{
// 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;
};
// Finally, we also allow merging if all streets offer the same name, it is only three roads and
// the angle is not fully extreme:
if (intersection.size() != 3)
return false;
// since we have an intersection of size three now, there is only one index we are not looking
// at right now. The final index in the intersection is calculated next:
const std::size_t third_index = [first_index, second_index]() {
if (first_index == 0)
return second_index == 2 ? 1 : 2;
else if (first_index == 1)
return second_index == 2 ? 0 : 2;
// 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
return second_index == 1 ? 0 : 1;
}();
{
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;
}
};
// needs to be same road coming in
const auto &third_data = node_based_graph.GetEdgeData(intersection[third_index].eid);
auto result = into;
BOOST_ASSERT(!node_based_graph.GetEdgeData(into.eid).reversed);
result.bearing = combineAngles(into.bearing, from.bearing);
BOOST_ASSERT(0 <= result.bearing && result.bearing < 360.0);
return result;
}
if (third_data.name_id != EMPTY_NAMEID &&
util::guidance::requiresNameAnnounced(
third_data.name_id, first_data.name_id, name_table, street_name_suffix_table))
return false;
// we only allow collapsing of a Y like fork. So the angle to the third index has to be
// roughly equal:
const auto y_angle_difference = angularDeviation(
angularDeviation(intersection[third_index].bearing, intersection[first_index].bearing),
angularDeviation(intersection[third_index].bearing, intersection[second_index].bearing));
// Allow larger angles if its three roads only of the same name
// This is a heuristic and might need to be revised.
const bool assume_y_intersection =
angle_between < 100 && y_angle_difference < FUZZY_ANGLE_DIFFERENCE;
return assume_y_intersection;
IntersectionShapeData
IntersectionNormalizer::MergeRoads(const IntersectionNormalizationOperation direction,
const IntersectionShapeData &lhs,
const IntersectionShapeData &rhs) const
{
if (direction.merged_eid == lhs.eid)
return MergeRoads(rhs, lhs);
else
return MergeRoads(lhs, rhs);
}
/*
@@ -218,7 +151,7 @@ bool IntersectionNormalizer::InnerCanMerge(const NodeID node_at_intersection,
* Anything containing the first u-turn in a merge affects all other angles
* and is handled separately from all others.
*/
std::pair<IntersectionShape, std::vector<std::pair<EdgeID, EdgeID>>>
IntersectionNormalizer::NormalizationResult
IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
IntersectionShape intersection) const
{
@@ -226,50 +159,25 @@ IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
return (index + intersection.size() - 1) % intersection.size();
};
// 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;
};
// 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
{
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;
}
};
// This map stores for all edges that participated in a merging operation in which edge id they
// end up in the end. We only store what we have merged into other edges.
std::vector<std::pair<EdgeID, EdgeID>> merging_map;
std::vector<IntersectionNormalizationOperation> merging_map;
const auto merge = [this, &merging_map](const IntersectionShapeData &first,
const IntersectionShapeData &second) {
const auto merge = [this, combineAngles, &merging_map](const IntersectionShapeData &first,
const IntersectionShapeData &second) {
IntersectionShapeData result =
!node_based_graph.GetEdgeData(first.eid).reversed ? first : second;
result.bearing = combineAngles(first.bearing, second.bearing);
BOOST_ASSERT(0 <= result.bearing && result.bearing < 360.0);
// the other ID
const auto merged_from = result.eid == first.eid ? second.eid : first.eid;
const auto direction = DetermineMergeDirection(first, second);
BOOST_ASSERT(
std::find_if(merging_map.begin(), merging_map.end(), [merged_from](const auto pair) {
return pair.first == merged_from;
std::find_if(merging_map.begin(), merging_map.end(), [direction](const auto pair) {
return pair.merged_eid == direction.merged_eid;
}) == merging_map.end());
merging_map.push_back(std::make_pair(merged_from, result.eid));
return result;
merging_map.push_back(direction);
return MergeRoads(direction, first, second);
};
if (intersection.size() <= 1)
return std::make_pair(intersection, merging_map);
return {intersection, merging_map};
const auto intersection_copy = intersection;
// check for merges including the basic u-turn
// these result in an adjustment of all other angles. This is due to how these angles are
// perceived. Considering the following example:
@@ -301,11 +209,9 @@ IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
// If the merge occurs at the u-turn edge, we need to adjust all angles, though, since they are
// with respect to the now changed perceived location of a. If we move (a) to the left, we add
// the difference to all angles. Otherwise we subtract it.
bool merged_first = false;
// these result in an adjustment of all other angles
if (CanMerge(intersection_node, intersection, intersection.size() - 1, 0))
{
merged_first = true;
// moving `a` to the left
intersection[0] = merge(intersection.front(), intersection.back());
// FIXME if we have a left-sided country, we need to switch this off and enable it
@@ -314,7 +220,6 @@ IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
}
else if (CanMerge(intersection_node, intersection, 0, 1))
{
merged_first = true;
intersection[0] = merge(intersection.front(), intersection[1]);
intersection.erase(intersection.begin() + 1);
}
@@ -331,8 +236,7 @@ IntersectionNormalizer::MergeSegregatedRoads(const NodeID intersection_node,
--index;
}
}
return std::make_pair(intersection, merging_map);
return {intersection, merging_map};
}
// OSM can have some very steep angles for joining roads. Considering the following intersection:
@@ -432,7 +336,7 @@ IntersectionNormalizer::AdjustBearingsForMergeAtDestination(const NodeID node_at
intersection[(intersection.size() + index - 1) % intersection.size()]);
// at the target intersection, we merge to the right, so we need to shift the current
// angle to the left
road.bearing = adjustAngle(road.bearing, -corrected_offset);
road.bearing = adjustAngle(road.bearing, corrected_offset);
}
else if (CanMerge(node_at_next_intersection,
next_intersection_along_road,
@@ -447,7 +351,7 @@ IntersectionNormalizer::AdjustBearingsForMergeAtDestination(const NodeID node_at
get_corrected_offset(offset, road, intersection[(index + 1) % intersection.size()]);
// at the target intersection, we merge to the left, so we need to shift the current
// angle to the right
road.bearing = adjustAngle(road.bearing, corrected_offset);
road.bearing = adjustAngle(road.bearing, -corrected_offset);
}
}
return intersection;
@@ -0,0 +1,469 @@
#include "extractor/guidance/mergable_road_detector.hpp"
#include "extractor/guidance/constants.hpp"
#include "extractor/guidance/coordinate_extractor.hpp"
#include "extractor/guidance/intersection_generator.hpp"
#include "extractor/guidance/node_based_graph_walker.hpp"
#include "extractor/query_node.hpp"
#include "extractor/suffix_table.hpp"
#include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp"
#include "util/guidance/name_announcements.hpp"
#include "util/name_table.hpp"
using osrm::util::angularDeviation;
namespace osrm
{
namespace extractor
{
namespace guidance
{
namespace
{
// check a connected road for equality of a name
inline auto makeCheckRoadForName(const NameID name_id,
const util::NodeBasedDynamicGraph &node_based_graph,
const util::NameTable &name_table,
const SuffixTable &suffix_table)
{
return [name_id, &node_based_graph, &name_table, &suffix_table](
const MergableRoadDetector::MergableRoadData &road) {
// since we filter here, we don't want any other name than the one we are looking for
const auto road_name = node_based_graph.GetEdgeData(road.eid).name_id;
if (name_id == EMPTY_NAMEID || road_name == EMPTY_NAMEID)
return true;
const auto requires_announcement =
util::guidance::requiresNameAnnounced(name_id, road_name, name_table, suffix_table) ||
util::guidance::requiresNameAnnounced(road_name, name_id, name_table, suffix_table);
return requires_announcement;
};
}
}
MergableRoadDetector::MergableRoadDetector(const util::NodeBasedDynamicGraph &node_based_graph,
const std::vector<QueryNode> &node_coordinates,
const IntersectionGenerator &intersection_generator,
const CoordinateExtractor &coordinate_extractor,
const util::NameTable &name_table,
const SuffixTable &street_name_suffix_table)
: node_based_graph(node_based_graph), node_coordinates(node_coordinates),
intersection_generator(intersection_generator), coordinate_extractor(coordinate_extractor),
name_table(name_table), street_name_suffix_table(street_name_suffix_table)
{
}
bool MergableRoadDetector::CanMergeRoad(const NodeID intersection_node,
const IntersectionShapeData &lhs,
const IntersectionShapeData &rhs) const
{
// roads should be somewhat close
if (angularDeviation(lhs.bearing, rhs.bearing) > MERGABLE_ANGLE_DIFFERENCE)
return false;
const auto &lhs_edge_data = node_based_graph.GetEdgeData(lhs.eid);
const auto &rhs_edge_data = node_based_graph.GetEdgeData(rhs.eid);
// and they need to describe the same road
if (!EdgeDataSupportsMerge(lhs_edge_data, rhs_edge_data))
return false;
/* don't use any circular links, since they mess up detection we jump out early.
*
* / -- \
* a ---- b - - /
*/
const auto road_target = [this](const MergableRoadData &road) {
return node_based_graph.GetTarget(road.eid);
};
// TODO might have to skip over trivial intersections
if (road_target(lhs) == intersection_node || road_target(lhs) == intersection_node)
return false;
// Don't merge turning circles/traffic loops
if (IsTrafficLoop(intersection_node, lhs) || IsTrafficLoop(intersection_node, rhs))
return false;
// needs to be checked prior to link roads, since connections can seem like links
if (IsTrafficIsland(intersection_node, lhs, rhs))
return true;
// Don't merge link roads
if (IsLinkRoad(intersection_node, lhs) || IsLinkRoad(intersection_node, rhs))
return false;
// check if we simply split up prior to an intersection
if (IsNarrowTriangle(intersection_node, lhs, rhs))
return true;
// finally check if two roads describe the direction
return HaveSameDirection(intersection_node, lhs, rhs);
}
bool MergableRoadDetector::HaveIdenticalNames(const NameID lhs, const NameID rhs) const
{
const auto non_empty = (lhs != EMPTY_NAMEID) && (rhs != EMPTY_NAMEID);
// symmetrical check for announcements
return non_empty &&
!util::guidance::requiresNameAnnounced(lhs, rhs, name_table, street_name_suffix_table) &&
!util::guidance::requiresNameAnnounced(rhs, lhs, name_table, street_name_suffix_table);
}
bool MergableRoadDetector::IsDistinctFrom(const MergableRoadData &lhs,
const MergableRoadData &rhs) const
{
// needs to be far away
if (angularDeviation(lhs.bearing, rhs.bearing) > MERGABLE_ANGLE_DIFFERENCE)
return true;
else // or it cannot have the same name
return !HaveIdenticalNames(node_based_graph.GetEdgeData(lhs.eid).name_id,
node_based_graph.GetEdgeData(rhs.eid).name_id);
}
bool MergableRoadDetector::EdgeDataSupportsMerge(const util::NodeBasedEdgeData &lhs_edge_data,
const util::NodeBasedEdgeData &rhs_edge_data) const
{
// roundabouts are special, simply don't hurt them. We might not want to bear the
// consequences
if (lhs_edge_data.roundabout || rhs_edge_data.roundabout)
return false;
/* to describe the same road, but in opposite directions (which is what we require for a
* merge), the roads have to feature one reversed and one non-reversed edge
*/
if (lhs_edge_data.reversed == rhs_edge_data.reversed)
return false;
/* The travel mode should be the same for both roads. If we were to merge different travel
* modes, we would hide information/run the risk of loosing valid choices (e.g. short period
* of pushing)
*/
if (lhs_edge_data.travel_mode != rhs_edge_data.travel_mode)
return false;
// we require valid names
if (!HaveIdenticalNames(lhs_edge_data.name_id, rhs_edge_data.name_id))
return false;
return lhs_edge_data.road_classification == rhs_edge_data.road_classification;
}
bool MergableRoadDetector::IsTrafficLoop(const NodeID intersection_node,
const MergableRoadData &road) const
{
const auto connection = intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid);
return intersection_node == node_based_graph.GetTarget(connection.via_eid);
}
bool MergableRoadDetector::IsNarrowTriangle(const NodeID intersection_node,
const MergableRoadData &lhs,
const MergableRoadData &rhs) const
{
// selection data to the right and left
const auto constexpr SMALL_RANDOM_HOPLIMIT = 5;
IntersectionFinderAccumulator left_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator),
right_accumulator(SMALL_RANDOM_HOPLIMIT, intersection_generator);
/* Standard following the straightmost road
* Since both items have the same id, we can `select` based on any setup
*/
SelectStraightmostRoadByNameAndOnlyChoice selector(
node_based_graph.GetEdgeData(lhs.eid).name_id, lhs.bearing, /*requires entry=*/false);
NodeBasedGraphWalker graph_walker(node_based_graph, intersection_generator);
graph_walker.TraverseRoad(intersection_node, lhs.eid, left_accumulator, selector);
/* if the intersection does not have a right turn, we continue onto the next one once
* (skipping over a single small side street)
*/
if (angularDeviation(left_accumulator.intersection.findClosestTurn(ORTHOGONAL_ANGLE)->angle,
ORTHOGONAL_ANGLE) > NARROW_TURN_ANGLE)
{
graph_walker.TraverseRoad(
node_based_graph.GetTarget(left_accumulator.via_edge_id),
left_accumulator.intersection.findClosestTurn(STRAIGHT_ANGLE)->eid,
left_accumulator,
selector);
}
const auto distance_to_triangle = util::coordinate_calculation::haversineDistance(
node_coordinates[intersection_node],
node_coordinates[node_based_graph.GetTarget(left_accumulator.via_edge_id)]);
// don't move too far down the road
const constexpr auto RANGE_TO_TRIANGLE_LIMIT = 80;
if (distance_to_triangle > RANGE_TO_TRIANGLE_LIMIT)
return false;
graph_walker.TraverseRoad(intersection_node, rhs.eid, right_accumulator, selector);
if (angularDeviation(right_accumulator.intersection.findClosestTurn(270)->angle, 270) >
NARROW_TURN_ANGLE)
{
graph_walker.TraverseRoad(
node_based_graph.GetTarget(right_accumulator.via_edge_id),
right_accumulator.intersection.findClosestTurn(STRAIGHT_ANGLE)->eid,
right_accumulator,
selector);
}
BOOST_ASSERT(!left_accumulator.intersection.empty() && !right_accumulator.intersection.empty());
// find the closes resembling a right turn
const auto connector_turn = left_accumulator.intersection.findClosestTurn(ORTHOGONAL_ANGLE);
/* check if that right turn connects to the right_accumulator intersection (i.e. we have a
* triangle)
* a connection should be somewhat to the right, when looking at the left side of the
* triangle
*
* b ..... c
* \ /
* \ /
* \ /
* a
*
* e.g. here when looking at `a,b`, a narrow triangle should offer a turn to the right, when
* we want to connect to c
*/
if (angularDeviation(connector_turn->angle, ORTHOGONAL_ANGLE) > NARROW_TURN_ANGLE)
return false;
const auto num_lanes = [this](const MergableRoadData &road) {
return std::max<std::uint8_t>(
node_based_graph.GetEdgeData(road.eid).road_classification.GetNumberOfLanes(), 1);
};
// the width we can bridge at the intersection
const auto assumed_road_width = (num_lanes(lhs) + num_lanes(rhs)) * ASSUMED_LANE_WIDTH;
const constexpr auto MAXIMAL_ALLOWED_TRAFFIC_ISLAND_WIDTH = 10;
const auto distance_between_triangle_corners = util::coordinate_calculation::haversineDistance(
node_coordinates[node_based_graph.GetTarget(left_accumulator.via_edge_id)],
node_coordinates[node_based_graph.GetTarget(right_accumulator.via_edge_id)]);
if (distance_between_triangle_corners >
(assumed_road_width + MAXIMAL_ALLOWED_TRAFFIC_ISLAND_WIDTH))
return false;
// check if both intersections are connected
IntersectionFinderAccumulator connect_accumulator(SMALL_RANDOM_HOPLIMIT,
intersection_generator);
graph_walker.TraverseRoad(node_based_graph.GetTarget(left_accumulator.via_edge_id),
connector_turn->eid,
connect_accumulator,
selector);
// the if both items are connected
return node_based_graph.GetTarget(connect_accumulator.via_edge_id) ==
node_based_graph.GetTarget(right_accumulator.via_edge_id);
}
bool MergableRoadDetector::HaveSameDirection(const NodeID intersection_node,
const MergableRoadData &lhs,
const MergableRoadData &rhs) const
{
if (angularDeviation(lhs.bearing, rhs.bearing) > MERGABLE_ANGLE_DIFFERENCE)
return false;
// Find a coordinate following a road that is far away
NodeBasedGraphWalker graph_walker(node_based_graph, intersection_generator);
const auto getCoordinatesAlongWay = [&](const EdgeID edge_id, const double max_length) {
LengthLimitedCoordinateAccumulator accumulator(coordinate_extractor, max_length);
SelectStraightmostRoadByNameAndOnlyChoice selector(
node_based_graph.GetEdgeData(edge_id).name_id, lhs.bearing, /*requires_entry=*/false);
graph_walker.TraverseRoad(intersection_node, edge_id, accumulator, selector);
return std::make_pair(accumulator.accumulated_length, accumulator.coordinates);
};
std::vector<util::Coordinate> coordinates_to_the_left, coordinates_to_the_right;
double distance_traversed_to_the_left, distance_traversed_to_the_right;
// many roads only do short parallel segments. To get a good impression of how `parallel` two
// roads are, we look 100 meters down the road (wich can be quite short for very broad roads).
const double constexpr distance_to_extract = 100;
std::tie(distance_traversed_to_the_left, coordinates_to_the_left) =
getCoordinatesAlongWay(lhs.eid, distance_to_extract);
// tuned parameter, if we didn't get as far as 40 meters, we might barely look past an
// intersection.
const auto constexpr MINIMUM_LENGTH_FOR_PARALLEL_DETECTION = 40;
// quit early if the road is not very long
if (distance_traversed_to_the_left <= MINIMUM_LENGTH_FOR_PARALLEL_DETECTION)
return false;
std::tie(distance_traversed_to_the_right, coordinates_to_the_right) =
getCoordinatesAlongWay(rhs.eid, distance_to_extract);
if (distance_traversed_to_the_right <= MINIMUM_LENGTH_FOR_PARALLEL_DETECTION)
return false;
const auto connect_again = (coordinates_to_the_left.back() == coordinates_to_the_right.back());
// sampling to correctly weight longer segments in regression calculations
const auto constexpr SAMPLE_INTERVAL = 5;
coordinates_to_the_left = coordinate_extractor.SampleCoordinates(
std::move(coordinates_to_the_left), distance_to_extract, SAMPLE_INTERVAL);
coordinates_to_the_right = coordinate_extractor.SampleCoordinates(
std::move(coordinates_to_the_right), distance_to_extract, SAMPLE_INTERVAL);
/* extract the number of lanes for a road
* restricts a vector to the last two thirds of the length
*/
const auto prune = [](auto &data_vector) {
BOOST_ASSERT(data_vector.size() >= 3);
//erase the first third of the vector
data_vector.erase(data_vector.begin(), data_vector.begin() + data_vector.size() / 3);
};
/* if the coordinates meet up again, e.g. due to a split and join, pruning can have a negative
* effect. We therefore only prune away the beginning, if the roads don't meet up again as well.
*/
if (!connect_again)
{
prune(coordinates_to_the_left);
prune(coordinates_to_the_right);
}
const auto are_parallel =
util::coordinate_calculation::areParallel(coordinates_to_the_left.begin(),
coordinates_to_the_left.end(),
coordinates_to_the_right.begin(),
coordinates_to_the_right.end());
if (!are_parallel)
return false;
// compare reference distance:
const auto distance_between_roads = util::coordinate_calculation::findClosestDistance(
coordinates_to_the_left[coordinates_to_the_left.size() / 2],
coordinates_to_the_right.begin(),
coordinates_to_the_right.end());
const auto lane_count_lhs = std::max<int>(
1, node_based_graph.GetEdgeData(lhs.eid).road_classification.GetNumberOfLanes());
const auto lane_count_rhs = std::max<int>(
1, node_based_graph.GetEdgeData(rhs.eid).road_classification.GetNumberOfLanes());
const auto combined_road_width = 0.5 * (lane_count_lhs + lane_count_rhs) * ASSUMED_LANE_WIDTH;
const auto constexpr MAXIMAL_ALLOWED_SEPARATION_WIDTH = 8;
return distance_between_roads <= combined_road_width + MAXIMAL_ALLOWED_SEPARATION_WIDTH;
}
bool MergableRoadDetector::IsTrafficIsland(const NodeID intersection_node,
const MergableRoadData &lhs,
const MergableRoadData &rhs) const
{
/* compute the set of all intersection_nodes along the way of an edge, until it reaches a
* location with the same name repeatet at least three times
*/
const auto left_connection =
intersection_generator.SkipDegreeTwoNodes(intersection_node, lhs.eid);
const auto right_connection =
intersection_generator.SkipDegreeTwoNodes(intersection_node, rhs.eid);
const auto left_candidate = node_based_graph.GetTarget(left_connection.via_eid);
const auto right_candidate = node_based_graph.GetTarget(right_connection.via_eid);
const auto candidate_is_valid =
left_candidate == right_candidate && left_candidate != intersection_node;
if (!candidate_is_valid)
return false;
// check if all entries at the destination or at the source are the same
const auto all_same_name_and_degree_three = [this](const NodeID nid) {
// check if the intersection found has degree three
if (node_based_graph.GetOutDegree(nid) != 3)
return false;
// check if all items share a name
const auto range = node_based_graph.GetAdjacentEdgeRange(nid);
const auto required_name_id = node_based_graph.GetEdgeData(range.front()).name_id;
const auto has_required_name = [this, required_name_id](const auto edge_id) {
const auto road_name = node_based_graph.GetEdgeData(edge_id).name_id;
if (required_name_id == EMPTY_NAMEID || road_name == EMPTY_NAMEID)
return false;
return !util::guidance::requiresNameAnnounced(
required_name_id, road_name, name_table, street_name_suffix_table) ||
!util::guidance::requiresNameAnnounced(
road_name, required_name_id, name_table, street_name_suffix_table);
};
/* the beautiful way would be:
* return range.end() == std::find_if_not(range.begin(), range.end(), has_required_name);
* but that does not work due to range concepts
*/
for (const auto eid : range)
if (!has_required_name(eid))
return false;
return true;
};
const auto degree_three_connect_in = all_same_name_and_degree_three(intersection_node);
const auto degree_three_connect_out = all_same_name_and_degree_three(left_candidate);
if (!degree_three_connect_in && !degree_three_connect_out)
return false;
const auto distance_between_candidates = util::coordinate_calculation::haversineDistance(
node_coordinates[intersection_node], node_coordinates[left_candidate]);
const auto both_split_join = degree_three_connect_in && degree_three_connect_out;
// allow longer separations if both are joining directly
// widths are chosen via tuning on traffic islands
return both_split_join ? (distance_between_candidates < 30)
: (distance_between_candidates < 15);
}
bool MergableRoadDetector::IsLinkRoad(const NodeID intersection_node,
const MergableRoadData &road) const
{
const auto next_intersection_parameters =
intersection_generator.SkipDegreeTwoNodes(intersection_node, road.eid);
const auto next_intersection_along_road = intersection_generator.GetConnectedRoads(
next_intersection_parameters.nid, next_intersection_parameters.via_eid);
const auto extract_name_id = [this](const MergableRoadData &road) {
return node_based_graph.GetEdgeData(road.eid).name_id;
};
const auto requested_name_id = extract_name_id(road);
const auto next_road_along_path = next_intersection_along_road.findClosestTurn(
STRAIGHT_ANGLE,
makeCheckRoadForName(
requested_name_id, node_based_graph, name_table, street_name_suffix_table));
// we need to have a continuing road to successfully detect a link road
if (next_road_along_path == next_intersection_along_road.end())
return false;
const auto opposite_of_next_road_along_path = next_intersection_along_road.findClosestTurn(
util::restrictAngleToValidRange(next_road_along_path->angle + STRAIGHT_ANGLE));
// we cannot be looking at the same road we came from
if (node_based_graph.GetTarget(opposite_of_next_road_along_path->eid) ==
next_intersection_parameters.nid)
return false;
/* check if the opposite of the next road decision was sane. It could have been just as well our
* incoming road.
*/
if (angularDeviation(angularDeviation(next_road_along_path->angle, STRAIGHT_ANGLE),
angularDeviation(opposite_of_next_road_along_path->angle, 0)) <
FUZZY_ANGLE_DIFFERENCE)
return false;
// near straight road that continues
return angularDeviation(opposite_of_next_road_along_path->angle, next_road_along_path->angle) >=
(STRAIGHT_ANGLE - FUZZY_ANGLE_DIFFERENCE) &&
EdgeDataSupportsMerge(
node_based_graph.GetEdgeData(next_road_along_path->eid),
node_based_graph.GetEdgeData(opposite_of_next_road_along_path->eid));
}
} // namespace guidance
} // namespace extractor
} // namespace osrm
@@ -1,4 +1,7 @@
#include "extractor/guidance/node_based_graph_walker.hpp"
#include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp"
#include <utility>
using osrm::util::angularDeviation;
@@ -18,11 +21,8 @@ NodeBasedGraphWalker::NodeBasedGraphWalker(const util::NodeBasedDynamicGraph &no
}
LengthLimitedCoordinateAccumulator::LengthLimitedCoordinateAccumulator(
const extractor::guidance::CoordinateExtractor &coordinate_extractor,
const util::NodeBasedDynamicGraph &node_based_graph,
const double max_length)
: coordinate_extractor(coordinate_extractor), node_based_graph(node_based_graph),
max_length(max_length), accumulated_length(0)
const extractor::guidance::CoordinateExtractor &coordinate_extractor, const double max_length)
: accumulated_length(0), coordinate_extractor(coordinate_extractor), max_length(max_length)
{
}
@@ -37,8 +37,10 @@ void LengthLimitedCoordinateAccumulator::update(const NodeID from_node,
auto current_coordinates =
coordinate_extractor.GetForwardCoordinatesAlongRoad(from_node, via_edge);
const auto length = util::coordinate_calculation::getLength(
current_coordinates, util::coordinate_calculation::haversineDistance);
const auto length =
util::coordinate_calculation::getLength(current_coordinates.begin(),
current_coordinates.end(),
util::coordinate_calculation::haversineDistance);
// in case we get too many coordinates, we limit them to our desired length
if (length + accumulated_length > max_length)
@@ -48,6 +50,7 @@ void LengthLimitedCoordinateAccumulator::update(const NodeID from_node,
coordinates.insert(coordinates.end(), current_coordinates.begin(), current_coordinates.end());
accumulated_length += length;
accumulated_length = std::min(accumulated_length, max_length);
}
// ---------------------------------------------------------------------------------
@@ -60,17 +63,15 @@ SelectRoadByNameOnlyChoiceAndStraightness::SelectRoadByNameOnlyChoiceAndStraight
boost::optional<EdgeID> SelectRoadByNameOnlyChoiceAndStraightness::
operator()(const NodeID /*nid*/,
const EdgeID /*via_edge_id*/,
const Intersection &intersection,
const IntersectionView &intersection,
const util::NodeBasedDynamicGraph &node_based_graph) const
{
BOOST_ASSERT(!intersection.empty());
const auto comparator = [this, &node_based_graph](const ConnectedRoad &lhs,
const ConnectedRoad &rhs) {
const auto comparator = [this, &node_based_graph](const IntersectionViewData &lhs,
const IntersectionViewData &rhs) {
// the score of an elemnt results in an ranking preferring valid entries, if required over
// invalid
// requested name_ids over non-requested
// narrow deviations over non-narrow
const auto score = [this, &node_based_graph](const ConnectedRoad &road) {
// invalid requested name_ids over non-requested narrow deviations over non-narrow
const auto score = [this, &node_based_graph](const IntersectionViewData &road) {
double result_score = 0;
// since angular deviation is limited by 0-180, we add 360 for invalid
if (requires_entry && !road.entry_allowed)
@@ -95,6 +96,75 @@ operator()(const NodeID /*nid*/,
return (*min_element).eid;
}
// ---------------------------------------------------------------------------------
SelectStraightmostRoadByNameAndOnlyChoice::SelectStraightmostRoadByNameAndOnlyChoice(
const NameID desired_name_id, const double initial_bearing, const bool requires_entry)
: desired_name_id(desired_name_id), initial_bearing(initial_bearing),
requires_entry(requires_entry)
{
}
boost::optional<EdgeID> SelectStraightmostRoadByNameAndOnlyChoice::
operator()(const NodeID /*nid*/,
const EdgeID /*via_edge_id*/,
const IntersectionView &intersection,
const util::NodeBasedDynamicGraph &node_based_graph) const
{
BOOST_ASSERT(!intersection.empty());
if (intersection.size() == 1)
return {};
const auto comparator = [this, &node_based_graph](const IntersectionViewData &lhs,
const IntersectionViewData &rhs) {
// the score of an elemnt results in an ranking preferring valid entries, if required over
// invalid requested name_ids over non-requested narrow deviations over non-narrow
const auto score = [this, &node_based_graph](const IntersectionViewData &road) {
double result_score = 0;
// since angular deviation is limited by 0-180, we add 360 for invalid
if (requires_entry && !road.entry_allowed)
result_score += 360.;
// 180 for undesired name-ids
if (desired_name_id != node_based_graph.GetEdgeData(road.eid).name_id)
result_score += 180;
return result_score + angularDeviation(road.angle, STRAIGHT_ANGLE);
};
return score(lhs) < score(rhs);
};
const auto count_desired_name =
std::count_if(std::begin(intersection),
std::end(intersection),
[this, &node_based_graph](const auto &road) {
return node_based_graph.GetEdgeData(road.eid).name_id == desired_name_id;
});
if (count_desired_name > 2)
return {};
const auto min_element =
std::min_element(std::next(std::begin(intersection)), std::end(intersection), comparator);
const auto is_valid_choice = !requires_entry || min_element->entry_allowed;
const auto is_only_choice_with_same_name =
count_desired_name <= 2 && // <= in case we come from a bridge
node_based_graph.GetEdgeData(min_element->eid).name_id == desired_name_id &&
angularDeviation(min_element->angle, STRAIGHT_ANGLE) < 100; // don't do crazy turns
const auto has_valid_angle =
((intersection.size() == 2 ||
intersection.findClosestTurn(STRAIGHT_ANGLE) == min_element) &&
angularDeviation(min_element->angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) &&
angularDeviation(initial_bearing, min_element->bearing) < NARROW_TURN_ANGLE;
// in cases where we have two edges between roads, we can have quite severe angles due to the
// random split OSRM does to break up parallel edges at any coordinate
if (!is_valid_choice || !(is_only_choice_with_same_name || has_valid_angle))
return {};
else
return (*min_element).eid;
}
// ---------------------------------------------------------------------------------
IntersectionFinderAccumulator::IntersectionFinderAccumulator(
const std::uint8_t hop_limit, const IntersectionGenerator &intersection_generator)
@@ -123,7 +193,7 @@ void IntersectionFinderAccumulator::update(const NodeID from_node,
nid = from_node;
via_edge_id = via_edge;
intersection = intersection_generator.GetConnectedRoads(from_node, via_edge);
intersection = intersection_generator.GetConnectedRoads(from_node, via_edge, true);
}
} // namespace guidance
@@ -5,7 +5,6 @@
#include "util/guidance/name_announcements.hpp"
#include <cmath>
#include <algorithm>
#include <iterator>
#include <limits>
+5 -6
View File
@@ -80,13 +80,13 @@ Intersection TurnAnalysis::operator()(const NodeID node_prior_to_intersection,
TurnAnalysis::ShapeResult shape_result =
ComputeIntersectionShapes(node_based_graph.GetTarget(entering_via_edge));
// assign valid flags to normalised_shape
// assign valid flags to normalized_shape
const auto intersection_view = intersection_generator.TransformIntersectionShapeIntoView(
node_prior_to_intersection,
entering_via_edge,
shape_result.normalised_intersection_shape,
shape_result.annotated_normalized_shape.normalized_shape,
shape_result.intersection_shape,
shape_result.merging_map);
shape_result.annotated_normalized_shape.performed_merges);
// assign the turn types to the intersection
return AssignTurnTypes(node_prior_to_intersection, entering_via_edge, intersection_view);
@@ -171,9 +171,8 @@ TurnAnalysis::ComputeIntersectionShapes(const NodeID node_at_center_of_intersect
intersection_shape.intersection_shape =
intersection_generator.ComputeIntersectionShape(node_at_center_of_intersection);
std::tie(intersection_shape.normalised_intersection_shape, intersection_shape.merging_map) =
intersection_normalizer(node_at_center_of_intersection,
intersection_shape.intersection_shape);
intersection_shape.annotated_normalized_shape = intersection_normalizer(
node_at_center_of_intersection, intersection_shape.intersection_shape);
return intersection_shape;
}
+7 -3
View File
@@ -1,5 +1,6 @@
#include "extractor/guidance/turn_discovery.hpp"
#include "extractor/guidance/constants.hpp"
#include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp"
using osrm::util::angularDeviation;
@@ -43,9 +44,12 @@ bool findPreviousIntersection(const NodeID node_v,
const constexpr double COMBINE_DISTANCE_CUTOFF = 30;
const auto coordinate_extractor = intersection_generator.GetCoordinateExtractor();
const auto via_edge_length = util::coordinate_calculation::getLength(
coordinate_extractor.GetForwardCoordinatesAlongRoad(node_v, via_edge),
&util::coordinate_calculation::haversineDistance);
const auto coordinates_along_via_edge =
coordinate_extractor.GetForwardCoordinatesAlongRoad(node_v, via_edge);
const auto via_edge_length =
util::coordinate_calculation::getLength(coordinates_along_via_edge.begin(),
coordinates_along_via_edge.end(),
&util::coordinate_calculation::haversineDistance);
// we check if via-edge is too short. In this case the previous turn cannot influence the turn
// at via_edge and the intersection at NODE_W
-1
View File
@@ -10,7 +10,6 @@
#include <boost/assert.hpp>
using EdgeData = osrm::util::NodeBasedDynamicGraph::EdgeData;
using osrm::extractor::guidance::getTurnDirection;
using osrm::util::angularDeviation;
+66 -53
View File
@@ -5,8 +5,8 @@
#include <boost/assert.hpp>
#include <cmath>
#include <algorithm>
#include <iterator>
#include <limits>
#include <utility>
@@ -125,29 +125,17 @@ Coordinate centroid(const Coordinate lhs, const Coordinate rhs)
return centroid;
}
double degToRad(const double degree)
{
using namespace boost::math::constants;
return degree * (pi<double>() / 180.0);
}
double radToDeg(const double radian)
{
using namespace boost::math::constants;
return radian * (180.0 * (1. / pi<double>()));
}
double bearing(const Coordinate first_coordinate, const Coordinate second_coordinate)
{
const double lon_diff =
static_cast<double>(toFloating(second_coordinate.lon - first_coordinate.lon));
const double lon_delta = degToRad(lon_diff);
const double lat1 = degToRad(static_cast<double>(toFloating(first_coordinate.lat)));
const double lat2 = degToRad(static_cast<double>(toFloating(second_coordinate.lat)));
const double lon_delta = detail::degToRad(lon_diff);
const double lat1 = detail::degToRad(static_cast<double>(toFloating(first_coordinate.lat)));
const double lat2 = detail::degToRad(static_cast<double>(toFloating(second_coordinate.lat)));
const double y = std::sin(lon_delta) * std::cos(lat2);
const double x =
std::cos(lat1) * std::sin(lat2) - std::sin(lat1) * std::cos(lat2) * std::cos(lon_delta);
double result = radToDeg(std::atan2(y, x));
double result = detail::radToDeg(std::atan2(y, x));
while (result < 0.0)
{
result += 360.0;
@@ -320,47 +308,72 @@ bool isCCW(const Coordinate first_coordinate,
return signedArea(first_coordinate, second_coordinate, third_coordinate) > 0;
}
std::pair<util::Coordinate, util::Coordinate>
leastSquareRegression(const std::vector<util::Coordinate> &coordinates)
// find the closest distance between a coordinate and a segment
double findClosestDistance(const Coordinate coordinate,
const Coordinate segment_begin,
const Coordinate segment_end)
{
BOOST_ASSERT(coordinates.size() >= 2);
double sum_lon = 0, sum_lat = 0, sum_lon_lat = 0, sum_lon_lon = 0;
double min_lon = static_cast<double>(toFloating(coordinates.front().lon));
double max_lon = static_cast<double>(toFloating(coordinates.front().lon));
for (const auto coord : coordinates)
{
min_lon = std::min(min_lon, static_cast<double>(toFloating(coord.lon)));
max_lon = std::max(max_lon, static_cast<double>(toFloating(coord.lon)));
sum_lon += static_cast<double>(toFloating(coord.lon));
sum_lon_lon +=
static_cast<double>(toFloating(coord.lon)) * static_cast<double>(toFloating(coord.lon));
sum_lat += static_cast<double>(toFloating(coord.lat));
sum_lon_lat +=
static_cast<double>(toFloating(coord.lon)) * static_cast<double>(toFloating(coord.lat));
}
return haversineDistance(coordinate,
projectPointOnSegment(segment_begin, segment_end, coordinate).second);
}
const auto dividend = coordinates.size() * sum_lon_lat - sum_lon * sum_lat;
const auto divisor = coordinates.size() * sum_lon_lon - sum_lon * sum_lon;
if (std::abs(divisor) < std::numeric_limits<double>::epsilon())
return std::make_pair(coordinates.front(), coordinates.back());
// find the closes distance between two sets of coordinates
double findClosestDistance(const std::vector<Coordinate> &lhs, const std::vector<Coordinate> &rhs)
{
double current_min = std::numeric_limits<double>::max();
// slope of the regression line
const auto slope = dividend / divisor;
const auto intercept = (sum_lat - slope * sum_lon) / coordinates.size();
const auto GetLatAtLon = [intercept,
slope](const util::FloatLongitude longitude) -> util::FloatLatitude {
return {intercept + slope * static_cast<double>((longitude))};
const auto compute_minimum_distance_in_rhs = [&current_min, &rhs](const Coordinate coordinate) {
current_min =
std::min(current_min, findClosestDistance(coordinate, rhs.begin(), rhs.end()));
return false;
};
const util::Coordinate regression_first = {
toFixed(util::FloatLongitude{min_lon - 1}),
toFixed(util::FloatLatitude(GetLatAtLon(util::FloatLongitude{min_lon - 1})))};
const util::Coordinate regression_end = {
toFixed(util::FloatLongitude{max_lon + 1}),
toFixed(util::FloatLatitude(GetLatAtLon(util::FloatLongitude{max_lon + 1})))};
std::find_if(std::begin(lhs), std::end(lhs), compute_minimum_distance_in_rhs);
return current_min;
}
return {regression_first, regression_end};
std::vector<double> getDeviations(const std::vector<Coordinate> &from,
const std::vector<Coordinate> &to)
{
auto find_deviation = [&to](const Coordinate coordinate) {
return findClosestDistance(coordinate, to.begin(), to.end());
};
std::vector<double> deviations_from;
deviations_from.reserve(from.size());
std::transform(
std::begin(from), std::end(from), std::back_inserter(deviations_from), find_deviation);
return deviations_from;
}
Coordinate rotateCCWAroundZero(Coordinate coordinate, double angle_in_radians)
{
/*
* a rotation around 0,0 in vector space is defined as
*
* | cos a -sin a | . | lon |
* | sin a cos a | | lat |
*
* resulting in cos a lon - sin a lon for the new longitude and sin a lon + cos a lat for the
* new latitude
*/
const auto cos_alpha = cos(angle_in_radians);
const auto sin_alpha = sin(angle_in_radians);
const auto lon = static_cast<double>(toFloating(coordinate.lon));
const auto lat = static_cast<double>(toFloating(coordinate.lat));
return {util::FloatLongitude{cos_alpha * lon - sin_alpha * lat},
util::FloatLatitude{sin_alpha * lon + cos_alpha * lat}};
}
Coordinate difference(const Coordinate lhs, const Coordinate rhs)
{
const auto lon_diff_int = static_cast<int>(lhs.lon) - static_cast<int>(rhs.lon);
const auto lat_diff_int = static_cast<int>(lhs.lat) - static_cast<int>(rhs.lat);
return {util::FixedLongitude{lon_diff_int}, util::FixedLatitude{lat_diff_int}};
}
} // ns coordinate_calculation
+1
View File
@@ -36,6 +36,7 @@ NodeIdVectorToMultiPoint::NodeIdVectorToMultiPoint(
: node_coordinates(node_coordinates)
{
}
util::json::Object NodeIdVectorToMultiPoint::
operator()(const std::vector<NodeID> &node_ids,
const boost::optional<json::Object> &properties) const