Skip to content

Commit 74611f9

Browse files
Moritz KobitzschTheMarex
authored andcommitted
Remove assertions that could be triggered by bad data. (#3469)
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 #3470.
1 parent eaff3b4 commit 74611f9

File tree

9 files changed

+136
-48
lines changed

9 files changed

+136
-48
lines changed

features/guidance/turn-angles.feature

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,3 +1260,29 @@ Feature: Simple Turns
12601260
When I route I should get
12611261
| waypoints | route | intersections |
12621262
| a,g | ab,bcdefgh,bcdefgh | true:90;true:45 false:180 false:270;true:180 |
1263+
1264+
#https://github.com/Project-OSRM/osrm-backend/pull/3469#issuecomment-270806580
1265+
Scenario: Oszillating Lower Priority Road
1266+
#Given the node map
1267+
# """
1268+
# a -db c
1269+
# f
1270+
# """
1271+
Given the node locations
1272+
| node | lat | lon | # |
1273+
| a | 1.0 | 1.0 | |
1274+
| b | 1.0000179813587253 | 1.0 | |
1275+
| c | 1.0000204580571323 | 1.0 | |
1276+
| d | 1.0000179813587253 | 1.0 | same as b |
1277+
| f | 1.0000179813587253 | 1.0000179813587253 | |
1278+
1279+
And the ways
1280+
| nodes | oneway | lanes | highway |
1281+
| ab | yes | 1 | primary |
1282+
| bf | yes | 1 | primary |
1283+
| bcd | yes | 1 | service |
1284+
1285+
# we don't care for turn instructions, this is a coordinate extraction bug check
1286+
When I route I should get
1287+
| waypoints | route |
1288+
| a,d | ab,ab |

include/extractor/guidance/coordinate_extractor.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class CoordinateExtractor
2828
/* Find a interpolated coordinate a long the compressed geometries. The desired coordinate
2929
* should be in a certain distance. This method is dedicated to find representative coordinates
3030
* at turns.
31-
* Since we are computing the length of the segment anyhow, we also return it.
31+
* Note: The segment between intersection and turn coordinate can be zero, if the OSM modelling
32+
* is unfortunate. See https://github.com/Project-OSRM/osrm-backend/issues/3470
3233
*/
3334
OSRM_ATTR_WARN_UNUSED
3435
util::Coordinate GetCoordinateAlongRoad(const NodeID intersection_node,

src/extractor/guidance/coordinate_extractor.cpp

Lines changed: 51 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -94,32 +94,30 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
9494
const std::uint8_t intersection_lanes,
9595
std::vector<util::Coordinate> coordinates) const
9696
{
97-
const auto is_valid_result = [&](const util::Coordinate coordinate) {
97+
// check if the coordinate is equal to the interseciton coordinate
98+
const auto not_same_as_start = [&](const util::Coordinate coordinate) {
9899
return util::Coordinate(traversed_in_reverse
99100
? node_coordinates[to_node]
100101
: node_coordinates[intersection_node]) != coordinate;
101102
};
102103
// this is only used for debug purposes in assertions. We don't want warnings about it
103-
(void)is_valid_result;
104-
105-
// the lane count might not always be set. We need to assume a positive number, though. Here we
106-
// select the number of lanes to operate on
107-
const auto considered_lanes =
108-
GetOffsetCorrectionFactor(node_based_graph.GetEdgeData(turn_edge).road_classification) *
109-
((intersection_lanes == 0) ? ASSUMED_LANE_COUNT : intersection_lanes);
104+
(void)not_same_as_start;
110105

111106
// Fallback. These roads are small broken self-loops that shouldn't be in the data at all
112107
if (intersection_node == to_node)
108+
{
109+
BOOST_ASSERT(coordinates.size() >= 2);
113110
return coordinates[1];
111+
}
114112

115113
/* if we are looking at a straight line, we don't care where exactly the coordinate
116114
* is. Simply return the final coordinate. Turn angles/turn vectors are the same no matter which
117115
* coordinate we look at.
118116
*/
119117
if (coordinates.size() <= 2)
120118
{
121-
// Here we can't check for validity, due to possible dead-ends with repeated coordinates
122-
// BOOST_ASSERT(is_valid_result(coordinates.back()));
119+
// TODO: possibly re-enable with https://github.com/Project-OSRM/osrm-backend/issues/3470
120+
// BOOST_ASSERT(not_same_as_start(result));
123121
return coordinates.back();
124122
}
125123

@@ -130,25 +128,20 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
130128
// fallback, mostly necessary for dead ends
131129
if (intersection_node == to_node)
132130
{
133-
const auto result = ExtractCoordinateAtLength(
134-
skipping_inaccuracies_distance, coordinates);
135-
BOOST_ASSERT(is_valid_result(result));
131+
const auto result = ExtractCoordinateAtLength(skipping_inaccuracies_distance, coordinates);
132+
// TODO: possibly re-enable with https://github.com/Project-OSRM/osrm-backend/issues/3470
133+
// BOOST_ASSERT(not_same_as_start(result));
136134
return result;
137135
}
138136

139-
// If this reduction leaves us with only two coordinates, the turns/angles are represented in a
140-
// valid way. Only curved roads and other difficult scenarios will require multiple coordinates.
141-
if (coordinates.size() == 2)
142-
return coordinates.back();
143-
144137
const auto &turn_edge_data = node_based_graph.GetEdgeData(turn_edge);
145138

146139
// roundabouts, check early to avoid other costly checks
147140
if (turn_edge_data.roundabout || turn_edge_data.circular)
148141
{
149-
const auto result = ExtractCoordinateAtLength(
150-
skipping_inaccuracies_distance, coordinates);
151-
BOOST_ASSERT(is_valid_result(result));
142+
const auto result = ExtractCoordinateAtLength(skipping_inaccuracies_distance, coordinates);
143+
// TODO: possibly re-enable with https://github.com/Project-OSRM/osrm-backend/issues/3470
144+
// BOOST_ASSERT(not_same_as_start(result));
152145
return result;
153146
}
154147

@@ -169,21 +162,34 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
169162
util::coordinate_calculation::haversineDistance(turn_coordinate, coordinates[1]) <
170163
ASSUMED_LANE_WIDTH)
171164
{
172-
const auto result =
173-
GetCorrectedCoordinate(turn_coordinate, coordinates[1], coordinates.back());
174-
BOOST_ASSERT(is_valid_result(result));
175-
return result;
176-
}
177-
else
178-
{
179-
BOOST_ASSERT(is_valid_result(coordinates.back()));
180-
return coordinates.back();
165+
const auto initial_distance =
166+
util::coordinate_calculation::haversineDistance(turn_coordinate, coordinates[1]);
167+
const auto total_distance = util::coordinate_calculation::haversineDistance(
168+
turn_coordinate, coordinates.back());
169+
170+
if (initial_distance > ASSUMED_LANE_WIDTH && total_distance > initial_distance)
171+
{
172+
const auto result =
173+
GetCorrectedCoordinate(turn_coordinate, coordinates[1], coordinates.back());
174+
BOOST_ASSERT(not_same_as_start(result));
175+
return result;
176+
}
181177
}
178+
// TODO: possibly re-enable with
179+
// https://github.com/Project-OSRM/osrm-backend/issues/3470
180+
// BOOST_ASSERT(not_same_as_start(coordinates.back()));
181+
return coordinates.back();
182182
}
183183

184184
const auto first_distance =
185185
util::coordinate_calculation::haversineDistance(coordinates[0], coordinates[1]);
186186

187+
// the lane count might not always be set. We need to assume a positive number, though. Here we
188+
// select the number of lanes to operate on
189+
const auto considered_lanes =
190+
GetOffsetCorrectionFactor(node_based_graph.GetEdgeData(turn_edge).road_classification) *
191+
((intersection_lanes == 0) ? ASSUMED_LANE_COUNT : intersection_lanes);
192+
187193
/* if the very first coordinate along the road is reasonably far away from the road, we assume
188194
* the coordinate to correctly represent the turn. This could probably be improved using
189195
* information on the very first turn angle (requires knowledge about previous road) and the
@@ -197,7 +203,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
197203

198204
if (first_coordinate_is_far_away)
199205
{
200-
BOOST_ASSERT(is_valid_result(coordinates[1]));
206+
BOOST_ASSERT(not_same_as_start(coordinates[1]));
201207
return coordinates[1];
202208
}
203209

@@ -237,7 +243,8 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
237243
if (coordinates.size() == 2 ||
238244
total_distance <= skipping_inaccuracies_distance)
239245
{
240-
BOOST_ASSERT(is_valid_result(coordinates.back()));
246+
// TODO: possibly re-enable with https://github.com/Project-OSRM/osrm-backend/issues/3470
247+
// BOOST_ASSERT(not_same_as_start(coordinates.back()));
241248
return coordinates.back();
242249
}
243250

@@ -252,14 +259,14 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
252259
// As a back-up, we have to check for this case
253260
if (coordinates.front() == coordinates.back())
254261
{
255-
const auto result = ExtractCoordinateAtLength(
256-
skipping_inaccuracies_distance, coordinates);
257-
BOOST_ASSERT(is_valid_result(result));
262+
const auto result =
263+
ExtractCoordinateAtLength(skipping_inaccuracies_distance, coordinates);
264+
BOOST_ASSERT(not_same_as_start(result));
258265
return result;
259266
}
260267
else
261268
{
262-
BOOST_ASSERT(is_valid_result(coordinates.back()));
269+
BOOST_ASSERT(not_same_as_start(coordinates.back()));
263270
return coordinates.back();
264271
}
265272
}
@@ -299,7 +306,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
299306
{
300307
// skip over repeated coordinates
301308
const auto result = ExtractCoordinateAtLength(5, coordinates, segment_distances);
302-
BOOST_ASSERT(is_valid_result(result));
309+
BOOST_ASSERT(not_same_as_start(result));
303310
return result;
304311
}
305312

@@ -333,7 +340,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
333340
.second;
334341
const auto result =
335342
GetCorrectedCoordinate(turn_coordinate, coord_between_front, coord_between_back);
336-
BOOST_ASSERT(is_valid_result(result));
343+
BOOST_ASSERT(not_same_as_start(result));
337344
return result;
338345
}
339346

@@ -354,7 +361,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
354361
const auto result = GetCorrectedCoordinate(
355362
turn_coordinate, coordinates[offset_index], coordinates[offset_index + 1]);
356363

357-
BOOST_ASSERT(is_valid_result(result));
364+
BOOST_ASSERT(not_same_as_start(result));
358365
return result;
359366
}
360367

@@ -388,7 +395,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
388395
BOOST_ASSERT(coordinates.size() >= 2);
389396
const auto result =
390397
GetCorrectedCoordinate(turn_coordinate, coordinates.back(), vector_head);
391-
BOOST_ASSERT(is_valid_result(result));
398+
BOOST_ASSERT(not_same_as_start(result));
392399
return result;
393400
}
394401

@@ -414,7 +421,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
414421
{
415422
const auto result = GetCorrectedCoordinate(
416423
turn_coordinate, regression_line_trimmed.first, regression_line_trimmed.second);
417-
BOOST_ASSERT(is_valid_result(result));
424+
BOOST_ASSERT(not_same_as_start(result));
418425
return result;
419426
}
420427
}
@@ -424,7 +431,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate(
424431
// intersection.
425432
const auto result =
426433
ExtractCoordinateAtLength(LOOKAHEAD_DISTANCE_WITHOUT_LANES, coordinates, segment_distances);
427-
BOOST_ASSERT(is_valid_result(result));
434+
BOOST_ASSERT(not_same_as_start(result));
428435
return result;
429436
}
430437

@@ -989,7 +996,9 @@ CoordinateExtractor::GetCorrectedCoordinate(const util::Coordinate fixpoint,
989996
// we can use the end-coordinate
990997
if (util::coordinate_calculation::haversineDistance(vector_base, vector_head) <
991998
DESIRED_COORDINATE_DIFFERENCE)
999+
{
9921000
return vector_head;
1001+
}
9931002
else
9941003
{
9951004
/* to correct for the initial offset, we move the lookahead coordinate close

src/extractor/guidance/intersection_generator.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@
22

33
#include "util/bearing.hpp"
44
#include "util/coordinate_calculation.hpp"
5+
#include "util/log.hpp"
56

67
#include <algorithm>
8+
#include <cmath>
79
#include <functional> // mem_fn
810
#include <limits>
911
#include <numeric>
@@ -101,6 +103,19 @@ IntersectionGenerator::ComputeIntersectionShape(const NodeID node_at_center_of_i
101103
bearing =
102104
util::coordinate_calculation::bearing(turn_coordinate, coordinate_along_edge_leaving);
103105

106+
// OSM data sometimes contains duplicated nodes with identical coordinates, or
107+
// because of coordinate precision rounding, end up at the same coordinate.
108+
// It's impossible to calculate a bearing between these, so we log a warning
109+
// that the data should be checked.
110+
// The bearing calculation should return 0 in these cases, which may not be correct,
111+
// but is at least not random.
112+
if (turn_coordinate == coordinate_along_edge_leaving)
113+
{
114+
util::Log(logDEBUG) << "Zero length segment at " << coordinate_along_edge_leaving
115+
<< " could cause invalid intersection exit bearing.";
116+
BOOST_ASSERT(std::abs(bearing) <= 0.1);
117+
}
118+
104119
intersection.push_back({edge_connected_to_intersection, bearing, segment_length});
105120
}
106121

src/extractor/guidance/roundabout_handler.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,20 @@ bool RoundaboutHandler::qualifiesAsRoundaboutIntersection(
204204

205205
result.push_back(
206206
util::coordinate_calculation::bearing(src_coordinate, next_coordinate));
207+
208+
// OSM data sometimes contains duplicated nodes with identical coordinates, or
209+
// because of coordinate precision rounding, end up at the same coordinate.
210+
// It's impossible to calculate a bearing between these, so we log a warning
211+
// that the data should be checked.
212+
// The bearing calculation should return 0 in these cases, which may not be correct,
213+
// but is at least not random.
214+
if (src_coordinate == next_coordinate)
215+
{
216+
util::Log(logDEBUG) << "Zero length segment at " << next_coordinate
217+
<< " could cause invalid roundabout exit bearings";
218+
BOOST_ASSERT(std::abs(result.back()) <= 0.1);
219+
}
220+
207221
break;
208222
}
209223
}

src/extractor/guidance/sliproad_handler.cpp

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -301,12 +301,18 @@ operator()(const NodeID /*nid*/, const EdgeID source_edge_id, Intersection inter
301301
//
302302
// Sliproad Not a Sliproad
303303
{
304-
const auto &extractor = intersection_generator.GetCoordinateExtractor();
304+
const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor();
305305

306306
const NodeID start = intersection_node_id; // b
307307
const EdgeID edge = sliproad_edge; // bd
308308

309-
const auto coords = extractor.GetForwardCoordinatesAlongRoad(start, edge);
309+
const auto coords = coordinate_extractor.GetForwardCoordinatesAlongRoad(start, edge);
310+
311+
// due to filtering of duplicated coordinates, we can end up with empty segments.
312+
// this can only happen as long as
313+
// https://github.com/Project-OSRM/osrm-backend/issues/3470 persists
314+
if (coords.size() < 2)
315+
continue;
310316
BOOST_ASSERT(coords.size() >= 2);
311317

312318
// Now keep start and end coordinate fix and check for curvature
@@ -544,14 +550,15 @@ bool SliproadHandler::nextIntersectionIsTooFarAway(const NodeID start, const Edg
544550
BOOST_ASSERT(start != SPECIAL_NODEID);
545551
BOOST_ASSERT(onto != SPECIAL_EDGEID);
546552

547-
const auto &extractor = intersection_generator.GetCoordinateExtractor();
553+
const auto &coordinate_extractor = intersection_generator.GetCoordinateExtractor();
548554

549555
// Base max distance threshold on the current road class we're on
550556
const auto &data = node_based_graph.GetEdgeData(onto);
551557
const auto threshold = scaledThresholdByRoadClass(MAX_SLIPROAD_THRESHOLD, // <- scales down
552558
data.road_classification);
553559

554-
DistanceToNextIntersectionAccumulator accumulator{extractor, node_based_graph, threshold};
560+
DistanceToNextIntersectionAccumulator accumulator{
561+
coordinate_extractor, node_based_graph, threshold};
555562
const SkipTrafficSignalBarrierRoadSelector selector{};
556563

557564
(void)graph_walker.TraverseRoad(start, onto, accumulator, selector);

src/util/coordinate_calculation.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,11 @@ double bearing(const Coordinate first_coordinate, const Coordinate second_coordi
152152
{
153153
result -= 360.0;
154154
}
155+
// If someone gives us two identical coordinates, then the concept of a bearing
156+
// makes no sense. However, because it sometimes happens, we'll at least
157+
// return a consistent value of 0 so that the behaviour isn't random.
158+
BOOST_ASSERT(first_coordinate != second_coordinate || result == 0.);
159+
155160
return result;
156161
}
157162

unit_tests/library/tile.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ BOOST_AUTO_TEST_CASE(test_tile)
3232
const auto rc = osrm.Tile(params, result);
3333
BOOST_CHECK(rc == Status::Ok);
3434

35-
BOOST_CHECK_EQUAL(result.size(), 114091);
35+
BOOST_CHECK_EQUAL(result.size(), 114098);
3636

3737
protozero::pbf_reader tile_message(result);
3838
tile_message.next();

unit_tests/util/coordinate_calculation.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,4 +311,15 @@ BOOST_AUTO_TEST_CASE(circleCenter)
311311
BOOST_CHECK(!result);
312312
}
313313

314+
BOOST_AUTO_TEST_CASE(consistent_invalid_bearing_result)
315+
{
316+
const auto pos1 = Coordinate(util::FloatLongitude{0.}, util::FloatLatitude{0.});
317+
const auto pos2 = Coordinate(util::FloatLongitude{5.}, util::FloatLatitude{5.});
318+
const auto pos3 = Coordinate(util::FloatLongitude{-5.}, util::FloatLatitude{-5.});
319+
320+
BOOST_CHECK_EQUAL(0., util::coordinate_calculation::bearing(pos1, pos1));
321+
BOOST_CHECK_EQUAL(0., util::coordinate_calculation::bearing(pos2, pos2));
322+
BOOST_CHECK_EQUAL(0., util::coordinate_calculation::bearing(pos3, pos3));
323+
}
324+
314325
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)