From a28a20a1bac5e0df0e87c97afe1825d95bf7cae2 Mon Sep 17 00:00:00 2001 From: Moritz Kobitzsch Date: Mon, 5 Dec 2016 12:02:07 +0100 Subject: [PATCH] fix errors in coordinate extractor due to duplicated coordinates fix offset calculation in curve detection --- .../guidance/dedicated-turn-roads.feature | 10 +- features/guidance/turn-angles.feature | 152 ++++++++++++++---- .../guidance/coordinate_extractor.cpp | 103 ++++++------ 3 files changed, 183 insertions(+), 82 deletions(-) diff --git a/features/guidance/dedicated-turn-roads.feature b/features/guidance/dedicated-turn-roads.feature index 8bb11bfad..ce78a6187 100644 --- a/features/guidance/dedicated-turn-roads.feature +++ b/features/guidance/dedicated-turn-roads.feature @@ -271,11 +271,11 @@ Feature: Slipways and Dedicated Turn Lanes | jcghf | primary | Brauerstrasse | yes | When I route I should get - | waypoints | route | turns | - | a,i | Ebertstrasse,Ebertstrasse | depart,arrive | - | a,l | Ebertstrasse,Ebertstrasse | depart,arrive | - | a,f | Ebertstrasse,Brauerstrasse,Brauerstrasse | depart,turn right,arrive | - | a,1 | Ebertstrasse,, | depart,turn right,arrive | + | waypoints | route | turns | + | a,i | Ebertstrasse,Ebertstrasse | depart,arrive | + | a,l | Ebertstrasse,Ebertstrasse | depart,arrive | + | a,f | Ebertstrasse,Brauerstrasse,Brauerstrasse | depart,turn right,arrive | + | a,1 | Ebertstrasse,, | depart,turn slight right,arrive | #2839 Scenario: Self-Loop diff --git a/features/guidance/turn-angles.feature b/features/guidance/turn-angles.feature index c177c2b54..2584971cf 100644 --- a/features/guidance/turn-angles.feature +++ b/features/guidance/turn-angles.feature @@ -317,30 +317,30 @@ Feature: Simple Turns Scenario: Curved Turn At Cross Given the node map """ - h - | - | - | - | - | . b - - - - - - - - - - - - - - - - - - a - | . - | . - | c - | - | - | - d - | - | - | - e | - . | - . | - g - - - - - - - - - - - - - f | - | - | - | - i + h + | + | + | + | + | . b - - - - - - - - - - - - - - - - - - - - - - - a + | . + | . + | c + | + | + | + d + | + | + | + e | + . | + . | + g - - - - - - - - - - - - - - - - - - f | + | + | + | + i """ And the ways @@ -443,9 +443,9 @@ Feature: Simple Turns | ef | residential | road | 2 | yes | When I route I should get - | waypoints | route | turns | locations | - | g,f | turn,road,road | depart,turn left,arrive | g,e,f | - | c,f | road,road,road | depart,continue right,arrive | c,b,f | + | waypoints | route | turns | locations | # | + | g,f | turn,road | depart,arrive | g,f | #could offer an additional turn at `e` if you don't detect the turn in between as curve | + | c,f | road,road,road | depart,continue right,arrive | c,b,f | | #http://www.openstreetmap.org/search?query=52.479264%2013.295617#map=19/52.47926/13.29562 Scenario: Splitting Roads with curved split @@ -627,12 +627,14 @@ Feature: Simple Turns | 1,h | ,allee,allee | depart,turn left,arrive | | 2,h | ,allee,allee | depart,turn left,arrive | + #http://www.openstreetmap.org/#map=18/52.56251/13.32650 + @todo Scenario: Curved Turn on Separated Directions Given the node map """ e d - f - - - - - - - - - - - - - - - c - - - - - - - - - - - j + f c - - - - - - - - - - - j | l ' | | ' | | ' | @@ -680,6 +682,58 @@ Feature: Simple Turns | j,o | Kapweg,Kapweg,Kapweg | depart,continue uturn,arrive | | a,i | Kurt,Kurt,Kurt | depart,continue uturn,arrive | + #http://www.openstreetmap.org/#map=18/52.56251/13.32650 + Scenario: Curved Turn on Separated Directions + Given the node map + """ + e d + f c - - - - - - - - - - - j + | l ' | + | ' | + | ' | + | ' | + | ' | + | n | + | | + | ' | + | | + | ' | + | | + | | + | | + |' | + | | + | | + | | + | | + g | + h - - - - - - - - - - - - - - - b - - - - - - - - - - - o + | | + | | + | | + | | + | | + | | + i a + """ + + And the ways + | nodes | name | oneway | lanes | highway | + | jc | Kapweg | yes | 3 | primary | + | clngh | Kapweg | yes | | primary_link | + | hbo | Kapweg | yes | 2 | primary | + | efh | Kurt | yes | 4 | secondary | + | hi | Kurt | yes | 3 | primary | + | ab | Kurt | yes | 4 | primary | + | cd | Kurt | yes | 3 | secondary | + | bc | Kurt | yes | 2 | primary | + + When I route I should get + | waypoints | route | turns | + | j,i | Kapweg,Kurt,Kurt | depart,turn left,arrive | + | j,o | Kapweg,Kapweg,Kapweg | depart,continue uturn,arrive | + | a,i | Kurt,Kurt,Kurt | depart,continue uturn,arrive | + #http://www.openstreetmap.org/#map=19/52.53731/13.36033 Scenario: Splitting Road to Left Given the node map @@ -1112,8 +1166,9 @@ Feature: Simple Turns """ And the ways - | nodes | name | highway | lanes | - | abcdefghijklmnopc | circled | residential | 1 | + | nodes | name | highway | lanes | oneway | + | abc | circled | residential | 1 | no | + | cdefghijklmnopc | circled | residential | 1 | yes | When I route I should get | waypoints | bearings | route | turns | @@ -1168,3 +1223,40 @@ Feature: Simple Turns When I route I should get | waypoints | route | | a,e | ab,bcde,bcde | + + + @3401 + Scenario: Curve With Duplicated Coordinates + Given the node locations + | node | lat | lon | # | + | a | 0.9999280745650984 | 1.0 | | + | b | 0.9999280745650984 | 1.0000179813587253 | | + | c | 0.9999280745650984 | 1.0000359627174509 | | + | d | 0.9999460559238238 | 1.0000674300952204 | | + | e | 0.9999640372825492 | 1.0000809161142643 | | + | f | 0.9999820186412746 | 1.0000854114539457 | | + | g | 1.0 | 1.0000854114539457 | | + | h | 1.0 | 1.0000854114539457 | #same as g | + | z | 0.9999100932063729 | 1.0000179813587253 | | + # g + # | + # f + # ' + # e + # ' + # d + # ' + #a - b - c + # | + # z + + And the ways + | nodes | oneway | lanes | # | + | ab | yes | 1 | | + | zb | yes | 1 | | + | bcdefgh | yes | 1 | #intentional duplication | + + # we don't care for turn instructions, this is a coordinate extraction bug check + When I route I should get + | waypoints | route | intersections | + | a,g | ab,bcdefgh,bcdefgh | true:90;true:45 false:180 false:270;true:180 | diff --git a/src/extractor/guidance/coordinate_extractor.cpp b/src/extractor/guidance/coordinate_extractor.cpp index 96a2bc0dd..3eaf1cb51 100644 --- a/src/extractor/guidance/coordinate_extractor.cpp +++ b/src/extractor/guidance/coordinate_extractor.cpp @@ -125,13 +125,13 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate( // due to repeated coordinates / smaller offset errors we skip over the very first parts of the // coordinate set to add a small level of fault tolerance - const constexpr double distance_to_skip_over_due_to_coordinate_inaccuracies = 2; + const constexpr double skipping_inaccuracies_distance = 2; // fallback, mostly necessary for dead ends if (intersection_node == to_node) { const auto result = ExtractCoordinateAtLength( - distance_to_skip_over_due_to_coordinate_inaccuracies, coordinates); + skipping_inaccuracies_distance, coordinates); BOOST_ASSERT(is_valid_result(coordinates.back())); return result; } @@ -147,7 +147,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate( if (turn_edge_data.roundabout || turn_edge_data.circular) { const auto result = ExtractCoordinateAtLength( - distance_to_skip_over_due_to_coordinate_inaccuracies, coordinates); + skipping_inaccuracies_distance, coordinates); BOOST_ASSERT(is_valid_result(result)); return result; } @@ -235,7 +235,7 @@ 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 (coordinates.size() == 2 || - total_distance <= distance_to_skip_over_due_to_coordinate_inaccuracies) + total_distance <= skipping_inaccuracies_distance) { BOOST_ASSERT(is_valid_result(coordinates.back())); return coordinates.back(); @@ -253,7 +253,7 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate( if (coordinates.front() == coordinates.back()) { const auto result = ExtractCoordinateAtLength( - distance_to_skip_over_due_to_coordinate_inaccuracies, coordinates); + skipping_inaccuracies_distance, coordinates); BOOST_ASSERT(is_valid_result(result)); return result; } @@ -337,38 +337,9 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate( return result; } - if (IsCurve(coordinates, - segment_distances, - total_distance, - considered_lanes * 0.5 * ASSUMED_LANE_WIDTH, - turn_edge_data)) - { - if (total_distance <= distance_to_skip_over_due_to_coordinate_inaccuracies) - return coordinates.back(); - /* - * In curves we now have to distinguish between larger curves and tiny curves modelling the - * actual turn in the beginnig. - * - * We distinguish between turns that simply model the initial way of getting onto the - * destination lanes and the ones that performa a larger turn. - */ - const double offset = - std::min(0.5 * considered_lanes * ASSUMED_LANE_WIDTH, 0.2 * segment_distances.back()); - coordinates = TrimCoordinatesToLength(std::move(coordinates), offset, segment_distances); - BOOST_ASSERT(coordinates.size() >= 2); - segment_distances.resize(coordinates.size()); - segment_distances.back() = util::coordinate_calculation::haversineDistance( - *(coordinates.end() - 2), coordinates.back()); - const auto vector_head = coordinates.back(); - coordinates = - TrimCoordinatesToLength(std::move(coordinates), 0.5 * offset, segment_distances); - BOOST_ASSERT(coordinates.size() >= 2); - const auto result = - GetCorrectedCoordinate(turn_coordinate, coordinates.back(), vector_head); - BOOST_ASSERT(is_valid_result(result)); - return result; - } - + // We check offsets before curves to avoid detecting lane offsets due to large roads as curves + // (think major highway here). If the road is wide, it can have quite a few coordinates in the + // beginning. if (IsDirectOffset(coordinates, straight_index, straight_distance, @@ -387,6 +358,40 @@ util::Coordinate CoordinateExtractor::ExtractRepresentativeCoordinate( return result; } + if (IsCurve(coordinates, + segment_distances, + total_distance, + considered_lanes * 0.5 * ASSUMED_LANE_WIDTH, + turn_edge_data)) + { + if (total_distance <= skipping_inaccuracies_distance) + return coordinates.back(); + /* + * In curves we now have to distinguish between larger curves and tiny curves modelling the + * actual turn in the beginnig. + * + * We distinguish between turns that simply model the initial way of getting onto the + * destination lanes and the ones that performa a larger turn. + */ + coordinates = + TrimCoordinatesToLength(std::move(coordinates), + 2 * skipping_inaccuracies_distance, + segment_distances); + BOOST_ASSERT(coordinates.size() >= 2); + segment_distances.resize(coordinates.size()); + segment_distances.back() = util::coordinate_calculation::haversineDistance( + *(coordinates.end() - 2), coordinates.back()); + const auto vector_head = coordinates.back(); + coordinates = TrimCoordinatesToLength(std::move(coordinates), + skipping_inaccuracies_distance, + segment_distances); + BOOST_ASSERT(coordinates.size() >= 2); + const auto result = + GetCorrectedCoordinate(turn_coordinate, coordinates.back(), vector_head); + BOOST_ASSERT(is_valid_result(result)); + return result; + } + { // skip over the first coordinates, in specific the assumed lane count. We add a small // safety factor, to not overshoot on the regression @@ -600,6 +605,9 @@ CoordinateExtractor::GetCoordinatesAlongRoad(const NodeID intersection_node, std::back_inserter(result), compressedGeometryToCoordinate); } + // filter duplicated coordinates + auto end = std::unique(result.begin(), result.end()); + result.erase(end, result.end()); return result; } } @@ -693,12 +701,12 @@ bool CoordinateExtractor::IsCurve(const std::vector &coordinat std::tie(has_up_down_deviation, maximum_deviation_index, maximum_deviation) = [&coordinates, get_deviation]() -> std::tuple { const auto increasing = [&](const util::Coordinate lhs, const util::Coordinate rhs) { - return get_deviation(coordinates.front(), coordinates.back(), lhs) <= + return get_deviation(coordinates.front(), coordinates.back(), lhs) < get_deviation(coordinates.front(), coordinates.back(), rhs); }; const auto decreasing = [&](const util::Coordinate lhs, const util::Coordinate rhs) { - return get_deviation(coordinates.front(), coordinates.back(), lhs) >= + return get_deviation(coordinates.front(), coordinates.back(), lhs) > get_deviation(coordinates.front(), coordinates.back(), rhs); }; @@ -709,16 +717,17 @@ bool CoordinateExtractor::IsCurve(const std::vector &coordinat return std::make_tuple( true, 1, get_deviation(coordinates.front(), coordinates.back(), coordinates[1])); - const auto maximum_itr = + const auto one_past_maximum_iter = std::is_sorted_until(coordinates.begin() + 1, coordinates.end(), increasing); - if (maximum_itr == coordinates.end()) + if (one_past_maximum_iter == coordinates.end()) return std::make_tuple(true, coordinates.size() - 1, 0.); - else if (std::is_sorted(maximum_itr, coordinates.end(), decreasing)) - return std::make_tuple( - true, - std::distance(coordinates.begin(), maximum_itr), - get_deviation(coordinates.front(), coordinates.back(), *maximum_itr)); + else if (std::is_sorted(one_past_maximum_iter, coordinates.end(), decreasing)) + return std::make_tuple(true, + std::distance(coordinates.begin(), one_past_maximum_iter) - 1, + get_deviation(coordinates.front(), + coordinates.back(), + *(one_past_maximum_iter - 1))); else return std::make_tuple(false, 0, 0.); }(); @@ -731,7 +740,7 @@ bool CoordinateExtractor::IsCurve(const std::vector &coordinat // if the maximum deviation is at a quarter of the total curve, we are probably looking at a // normal turn const auto distance_to_max_deviation = std::accumulate( - segment_distances.begin(), segment_distances.begin() + maximum_deviation_index, 0.); + segment_distances.begin(), segment_distances.begin() + maximum_deviation_index + 1, 0.); if ((distance_to_max_deviation <= 0.35 * segment_length || maximum_deviation < std::max(0.3 * considered_lane_width, 0.5 * ASSUMED_LANE_WIDTH)) &&