Remove assertions that could be triggered by bad data. ()

When two consecutive nodes have identical coordinates, there is no valid
bearing.  For now, make equal nodes have bearing 0.

Full fix still needs to be done via https://github.com/Project-OSRM/osrm-backend/issues/3470.
This commit is contained in:
Moritz Kobitzsch 2017-01-07 02:10:02 +01:00 committed by Daniel Patterson
parent f7e8581a1b
commit 15c8fd326f
9 changed files with 132 additions and 42 deletions

View File

@ -1314,3 +1314,29 @@ Feature: Simple Turns
When I route I should get When I route I should get
| waypoints | route | intersections | | waypoints | route | intersections |
| a,g | ab,bcdefgh,bcdefgh | true:90;true:45 false:180 false:270;true:180 | | a,g | ab,bcdefgh,bcdefgh | true:90;true:45 false:180 false:270;true:180 |
#https://github.com/Project-OSRM/osrm-backend/pull/3469#issuecomment-270806580
Scenario: Oszillating Lower Priority Road
#Given the node map
# """
# a -db c
# f
# """
Given the node locations
| node | lat | lon | # |
| a | 1.0 | 1.0 | |
| b | 1.0000179813587253 | 1.0 | |
| c | 1.0000204580571323 | 1.0 | |
| d | 1.0000179813587253 | 1.0 | same as b |
| f | 1.0000179813587253 | 1.0000179813587253 | |
And the ways
| nodes | oneway | lanes | highway |
| ab | yes | 1 | primary |
| bf | yes | 1 | primary |
| bcd | yes | 1 | service |
# we don't care for turn instructions, this is a coordinate extraction bug check
When I route I should get
| waypoints | route |
| a,d | ab,ab |

View File

@ -28,7 +28,8 @@ class CoordinateExtractor
/* Find a interpolated coordinate a long the compressed geometries. The desired coordinate /* Find a interpolated coordinate a long the compressed geometries. The desired coordinate
* should be in a certain distance. This method is dedicated to find representative coordinates * should be in a certain distance. This method is dedicated to find representative coordinates
* at turns. * at turns.
* Since we are computing the length of the segment anyhow, we also return it. * Note: The segment between intersection and turn coordinate can be zero, if the OSM modelling
* is unfortunate. See https://github.com/Project-OSRM/osrm-backend/issues/3470
*/ */
OSRM_ATTR_WARN_UNUSED OSRM_ATTR_WARN_UNUSED
util::Coordinate GetCoordinateAlongRoad(const NodeID intersection_node, util::Coordinate GetCoordinateAlongRoad(const NodeID intersection_node,

View File

@ -93,23 +93,21 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
const std::uint8_t intersection_lanes, const std::uint8_t intersection_lanes,
std::vector<util::Coordinate> coordinates) const std::vector<util::Coordinate> coordinates) const
{ {
const auto is_valid_result = [&](const util::Coordinate coordinate) { // check if the coordinate is equal to the interseciton coordinate
const auto not_same_as_start = [&](const util::Coordinate coordinate) {
return util::Coordinate(traversed_in_reverse return util::Coordinate(traversed_in_reverse
? node_coordinates[to_node] ? node_coordinates[to_node]
: node_coordinates[intersection_node]) != coordinate; : node_coordinates[intersection_node]) != coordinate;
}; };
// this is only used for debug purposes in assertions. We don't want warnings about it // this is only used for debug purposes in assertions. We don't want warnings about it
(void)is_valid_result; (void)not_same_as_start;
// the lane count might not always be set. We need to assume a positive number, though. Here we
// select the number of lanes to operate on
const auto considered_lanes =
GetOffsetCorrectionFactor(node_based_graph.GetEdgeData(turn_edge).road_classification) *
((intersection_lanes == 0) ? ASSUMED_LANE_COUNT : intersection_lanes);
// Fallback. These roads are small broken self-loops that shouldn't be in the data at all // Fallback. These roads are small broken self-loops that shouldn't be in the data at all
if (intersection_node == to_node) if (intersection_node == to_node)
{
BOOST_ASSERT(coordinates.size() >= 2);
return coordinates[1]; return coordinates[1];
}
/* if we are looking at a straight line, we don't care where exactly the coordinate /* 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 * is. Simply return the final coordinate. Turn angles/turn vectors are the same no matter which
@ -117,8 +115,8 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
*/ */
if (coordinates.size() <= 2) if (coordinates.size() <= 2)
{ {
// Here we can't check for validity, due to possible dead-ends with repeated coordinates // TODO: possibly re-enable with https://github.com/Project-OSRM/osrm-backend/issues/3470
// BOOST_ASSERT(is_valid_result(coordinates.back())); // BOOST_ASSERT(not_same_as_start(result));
return coordinates.back(); return coordinates.back();
} }
@ -130,22 +128,19 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
if (intersection_node == to_node) if (intersection_node == to_node)
{ {
const auto result = ExtractCoordinateAtLength(skipping_inaccuracies_distance, coordinates); const auto result = ExtractCoordinateAtLength(skipping_inaccuracies_distance, coordinates);
BOOST_ASSERT(is_valid_result(result)); // TODO: possibly re-enable with https://github.com/Project-OSRM/osrm-backend/issues/3470
// BOOST_ASSERT(not_same_as_start(result));
return result; return result;
} }
// 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 auto &turn_edge_data = node_based_graph.GetEdgeData(turn_edge);
// roundabouts, check early to avoid other costly checks // roundabouts, check early to avoid other costly checks
if (turn_edge_data.roundabout || turn_edge_data.circular) if (turn_edge_data.roundabout || turn_edge_data.circular)
{ {
const auto result = ExtractCoordinateAtLength(skipping_inaccuracies_distance, coordinates); const auto result = ExtractCoordinateAtLength(skipping_inaccuracies_distance, coordinates);
BOOST_ASSERT(is_valid_result(result)); // TODO: possibly re-enable with https://github.com/Project-OSRM/osrm-backend/issues/3470
// BOOST_ASSERT(not_same_as_start(result));
return result; return result;
} }
@ -165,22 +160,35 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
if (coordinates.size() > 2 && if (coordinates.size() > 2 &&
util::coordinate_calculation::haversineDistance(turn_coordinate, coordinates[1]) < util::coordinate_calculation::haversineDistance(turn_coordinate, coordinates[1]) <
ASSUMED_LANE_WIDTH) ASSUMED_LANE_WIDTH)
{
const auto initial_distance =
util::coordinate_calculation::haversineDistance(turn_coordinate, coordinates[1]);
const auto total_distance = util::coordinate_calculation::haversineDistance(
turn_coordinate, coordinates.back());
if (initial_distance > ASSUMED_LANE_WIDTH && total_distance > initial_distance)
{ {
const auto result = const auto result =
GetCorrectedCoordinate(turn_coordinate, coordinates[1], coordinates.back()); GetCorrectedCoordinate(turn_coordinate, coordinates[1], coordinates.back());
BOOST_ASSERT(is_valid_result(result)); BOOST_ASSERT(not_same_as_start(result));
return result; return result;
} }
else
{
BOOST_ASSERT(is_valid_result(coordinates.back()));
return coordinates.back();
} }
// TODO: possibly re-enable with
// https://github.com/Project-OSRM/osrm-backend/issues/3470
// BOOST_ASSERT(not_same_as_start(coordinates.back()));
return coordinates.back();
} }
const auto first_distance = const auto first_distance =
util::coordinate_calculation::haversineDistance(coordinates[0], coordinates[1]); util::coordinate_calculation::haversineDistance(coordinates[0], coordinates[1]);
// the lane count might not always be set. We need to assume a positive number, though. Here we
// select the number of lanes to operate on
const auto considered_lanes =
GetOffsetCorrectionFactor(node_based_graph.GetEdgeData(turn_edge).road_classification) *
((intersection_lanes == 0) ? ASSUMED_LANE_COUNT : intersection_lanes);
/* if the very first coordinate along the road is reasonably far away from the road, we assume /* 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 * 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 * information on the very first turn angle (requires knowledge about previous road) and the
@ -194,7 +202,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
if (first_coordinate_is_far_away) if (first_coordinate_is_far_away)
{ {
BOOST_ASSERT(is_valid_result(coordinates[1])); BOOST_ASSERT(not_same_as_start(coordinates[1]));
return coordinates[1]; return coordinates[1];
} }
@ -233,7 +241,8 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
// if we are now left with two, well than we don't have to worry, or the segment is very small // if we are now left with two, well than we don't have to worry, or the segment is very small
if (coordinates.size() == 2 || total_distance <= skipping_inaccuracies_distance) if (coordinates.size() == 2 || total_distance <= skipping_inaccuracies_distance)
{ {
BOOST_ASSERT(is_valid_result(coordinates.back())); // TODO: possibly re-enable with https://github.com/Project-OSRM/osrm-backend/issues/3470
// BOOST_ASSERT(not_same_as_start(coordinates.back()));
return coordinates.back(); return coordinates.back();
} }
@ -250,12 +259,12 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
{ {
const auto result = const auto result =
ExtractCoordinateAtLength(skipping_inaccuracies_distance, coordinates); ExtractCoordinateAtLength(skipping_inaccuracies_distance, coordinates);
BOOST_ASSERT(is_valid_result(result)); BOOST_ASSERT(not_same_as_start(result));
return result; return result;
} }
else else
{ {
BOOST_ASSERT(is_valid_result(coordinates.back())); BOOST_ASSERT(not_same_as_start(coordinates.back()));
return coordinates.back(); return coordinates.back();
} }
} }
@ -295,7 +304,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
{ {
// skip over repeated coordinates // skip over repeated coordinates
const auto result = ExtractCoordinateAtLength(5, coordinates, segment_distances); const auto result = ExtractCoordinateAtLength(5, coordinates, segment_distances);
BOOST_ASSERT(is_valid_result(result)); BOOST_ASSERT(not_same_as_start(result));
return result; return result;
} }
@ -329,7 +338,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
.second; .second;
const auto result = const auto result =
GetCorrectedCoordinate(turn_coordinate, coord_between_front, coord_between_back); GetCorrectedCoordinate(turn_coordinate, coord_between_front, coord_between_back);
BOOST_ASSERT(is_valid_result(result)); BOOST_ASSERT(not_same_as_start(result));
return result; return result;
} }
@ -350,7 +359,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
const auto result = GetCorrectedCoordinate( const auto result = GetCorrectedCoordinate(
turn_coordinate, coordinates[offset_index], coordinates[offset_index + 1]); turn_coordinate, coordinates[offset_index], coordinates[offset_index + 1]);
BOOST_ASSERT(is_valid_result(result)); BOOST_ASSERT(not_same_as_start(result));
return result; return result;
} }
@ -381,7 +390,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
BOOST_ASSERT(coordinates.size() >= 2); BOOST_ASSERT(coordinates.size() >= 2);
const auto result = const auto result =
GetCorrectedCoordinate(turn_coordinate, coordinates.back(), vector_head); GetCorrectedCoordinate(turn_coordinate, coordinates.back(), vector_head);
BOOST_ASSERT(is_valid_result(result)); BOOST_ASSERT(not_same_as_start(result));
return result; return result;
} }
@ -407,7 +416,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
{ {
const auto result = GetCorrectedCoordinate( const auto result = GetCorrectedCoordinate(
turn_coordinate, regression_line_trimmed.first, regression_line_trimmed.second); turn_coordinate, regression_line_trimmed.first, regression_line_trimmed.second);
BOOST_ASSERT(is_valid_result(result)); BOOST_ASSERT(not_same_as_start(result));
return result; return result;
} }
} }
@ -417,7 +426,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
// intersection. // intersection.
const auto result = const auto result =
ExtractCoordinateAtLength(LOOKAHEAD_DISTANCE_WITHOUT_LANES, coordinates, segment_distances); ExtractCoordinateAtLength(LOOKAHEAD_DISTANCE_WITHOUT_LANES, coordinates, segment_distances);
BOOST_ASSERT(is_valid_result(result)); BOOST_ASSERT(not_same_as_start(result));
return result; return result;
} }
@ -984,7 +993,9 @@ CoordinateExtractor::GetCorrectedCoordinate(const util::Coordinate fixpoint,
// we can use the end-coordinate // we can use the end-coordinate
if (util::coordinate_calculation::haversineDistance(vector_base, vector_head) < if (util::coordinate_calculation::haversineDistance(vector_base, vector_head) <
DESIRED_COORDINATE_DIFFERENCE) DESIRED_COORDINATE_DIFFERENCE)
{
return vector_head; return vector_head;
}
else else
{ {
/* to correct for the initial offset, we move the lookahead coordinate close /* to correct for the initial offset, we move the lookahead coordinate close

View File

@ -5,8 +5,10 @@
#include "util/bearing.hpp" #include "util/bearing.hpp"
#include "util/coordinate_calculation.hpp" #include "util/coordinate_calculation.hpp"
#include "util/log.hpp"
#include <algorithm> #include <algorithm>
#include <cmath>
#include <functional> // mem_fn #include <functional> // mem_fn
#include <limits> #include <limits>
#include <numeric> #include <numeric>
@ -106,6 +108,19 @@ IntersectionGenerator::ComputeIntersectionShape(const NodeID node_at_center_of_i
bearing = bearing =
util::coordinate_calculation::bearing(turn_coordinate, coordinate_along_edge_leaving); util::coordinate_calculation::bearing(turn_coordinate, coordinate_along_edge_leaving);
// OSM data sometimes contains duplicated nodes with identical coordinates, or
// because of coordinate precision rounding, end up at the same coordinate.
// It's impossible to calculate a bearing between these, so we log a warning
// that the data should be checked.
// The bearing calculation should return 0 in these cases, which may not be correct,
// but is at least not random.
if (turn_coordinate == coordinate_along_edge_leaving)
{
util::Log(logDEBUG) << "Zero length segment at " << coordinate_along_edge_leaving
<< " could cause invalid intersection exit bearing.";
BOOST_ASSERT(std::abs(bearing) <= 0.1);
}
intersection.push_back({edge_connected_to_intersection, bearing, segment_length}); intersection.push_back({edge_connected_to_intersection, bearing, segment_length});
} }

View File

@ -204,6 +204,20 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
result.push_back( result.push_back(
util::coordinate_calculation::bearing(src_coordinate, next_coordinate)); util::coordinate_calculation::bearing(src_coordinate, next_coordinate));
// OSM data sometimes contains duplicated nodes with identical coordinates, or
// because of coordinate precision rounding, end up at the same coordinate.
// It's impossible to calculate a bearing between these, so we log a warning
// that the data should be checked.
// The bearing calculation should return 0 in these cases, which may not be correct,
// but is at least not random.
if (src_coordinate == next_coordinate)
{
util::Log(logDEBUG) << "Zero length segment at " << next_coordinate
<< " could cause invalid roundabout exit bearings";
BOOST_ASSERT(std::abs(result.back()) <= 0.1);
}
break; break;
} }
} }

View File

@ -300,12 +300,18 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
// //
// Sliproad Not a Sliproad // Sliproad Not a Sliproad
{ {
const auto &extractor = intersection_generator.GetCoordinateExtractor(); const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor();
const NodeID start = intersection_node_id; // b const NodeID start = intersection_node_id; // b
const EdgeID edge = sliproad_edge; // bd const EdgeID edge = sliproad_edge; // bd
const auto coords = extractor.GetForwardCoordinatesAlongRoad(start, edge); const auto coords = coordinate_extractor.GetForwardCoordinatesAlongRoad(start, edge);
// due to filtering of duplicated coordinates, we can end up with empty segments.
// this can only happen as long as
// https://github.com/Project-OSRM/osrm-backend/issues/3470 persists
if (coords.size() < 2)
continue;
BOOST_ASSERT(coords.size() >= 2); BOOST_ASSERT(coords.size() >= 2);
// Now keep start and end coordinate fix and check for curvature // Now keep start and end coordinate fix and check for curvature
@ -543,14 +549,15 @@ bool SliproadHandler::nextIntersectionIsTooFarAway(const NodeID start, const Edg
BOOST_ASSERT(start != SPECIAL_NODEID); BOOST_ASSERT(start != SPECIAL_NODEID);
BOOST_ASSERT(onto != SPECIAL_EDGEID); BOOST_ASSERT(onto != SPECIAL_EDGEID);
const auto &extractor = intersection_generator.GetCoordinateExtractor(); const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor();
// Base max distance threshold on the current road class we're on // Base max distance threshold on the current road class we're on
const auto &data = node_based_graph.GetEdgeData(onto); const auto &data = node_based_graph.GetEdgeData(onto);
const auto threshold = scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, // <- scales down const auto threshold = scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, // <- scales down
data.road_classification); data.road_classification);
DistanceToNextIntersectionAccumulator accumulator{extractor, node_based_graph, threshold}; DistanceToNextIntersectionAccumulator accumulator{
coordinate_extractor, node_based_graph, threshold};
const SkipTrafficSignalBarrierRoadSelector selector{}; const SkipTrafficSignalBarrierRoadSelector selector{};
(void)graph_walker.TraverseRoad(start, onto, accumulator, selector); (void)graph_walker.TraverseRoad(start, onto, accumulator, selector);

View File

@ -145,6 +145,11 @@ double bearing(const Coordinate first_coordinate, const Coordinate second_coordi
{ {
result -= 360.0; result -= 360.0;
} }
// If someone gives us two identical coordinates, then the concept of a bearing
// makes no sense. However, because it sometimes happens, we'll at least
// return a consistent value of 0 so that the behaviour isn't random.
BOOST_ASSERT(first_coordinate != second_coordinate || result == 0.);
return result; return result;
} }

View File

@ -32,7 +32,7 @@ BOOST_AUTO_TEST_CASE(test_tile)
const auto rc = osrm.Tile(params, result); const auto rc = osrm.Tile(params, result);
BOOST_CHECK(rc == Status::Ok); BOOST_CHECK(rc == Status::Ok);
BOOST_CHECK_EQUAL(result.size(), 114091); BOOST_CHECK_EQUAL(result.size(), 114098);
protozero::pbf_reader tile_message(result); protozero::pbf_reader tile_message(result);
tile_message.next(); tile_message.next();

View File

@ -378,4 +378,15 @@ BOOST_AUTO_TEST_CASE(parallel_lines_slight_offset)
BOOST_CHECK(are_parallel); BOOST_CHECK(are_parallel);
} }
BOOST_AUTO_TEST_CASE(consistent_invalid_bearing_result)
{
const auto pos1 = Coordinate(util::FloatLongitude{0.}, util::FloatLatitude{0.});
const auto pos2 = Coordinate(util::FloatLongitude{5.}, util::FloatLatitude{5.});
const auto pos3 = Coordinate(util::FloatLongitude{-5.}, util::FloatLatitude{-5.});
BOOST_CHECK_EQUAL(0., util::coordinate_calculation::bearing(pos1, pos1));
BOOST_CHECK_EQUAL(0., util::coordinate_calculation::bearing(pos2, pos2));
BOOST_CHECK_EQUAL(0., util::coordinate_calculation::bearing(pos3, pos3));
}
BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE_END()