diff --git a/CHANGELOG.md b/CHANGELOG.md index 23b7f171f..21feadd56 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,12 +4,10 @@ - API: - new parameter `annotate` for `route` and `match` requests. Returns additional data about each coordinate along the selected/matched route line. - - Introducing Intersections for Route Steps. This breaks the API format in multiple ways. - - `bearing_before`/`bearing_after` are no longer supplied with a StepManeuver - - `exit` is no longer supplied for turns other than roundabouts - - `location` is no longer supplied for StepManeuvers - - every RouteStep is supplied with a list of at least one `Intersection`. - - Intersections offer the removed values from StepManeuver + - Introducing Intersections for Route Steps. This changes the API format in multiple ways. + - `bearing_before`/`bearing_after` of `StepManeuver` are now deprecated and will be removed in the next major release + - `location` of `StepManeuvers` is now deprecated and will be removed in the next major release + - every `RouteStep` now has property `intersections` containing a list of `Intersection` objects. - Profile changes: - duration parser now accepts P[n]DT[n]H[n]M[n]S, P[n]W, PTHHMMSS and PTHH:MM:SS ISO8601 formats. diff --git a/docs/http.md b/docs/http.md index c874e5604..4c68bd640 100644 --- a/docs/http.md +++ b/docs/http.md @@ -377,7 +377,7 @@ Represents a route between two waypoints. - `duration`: The estimated travel time, in `float` number of seconds. - `summary`: Summary of the route taken as `string`. Depends on the `steps` parameter: - | summary | | + | steps | | |--------------|-----------------------------------------------------------------------| | true | Names of the two major roads used. Can be empty if route is too short.| | false | empty `string` | @@ -464,6 +464,12 @@ step. ### StepManeuver #### Properties + +- `location`: A `[longitude, latitude]` pair describing the location of the turn. +- `bearing_before`: The clockwise angle from true north to the + direction of travel immediately before the maneuver. +- `bearing_after`: The clockwise angle from true north to the + direction of travel immediately after the maneuver. - `type` A string indicating the type of maneuver. **new identifiers might be introduced without API change** Types unknown to the client should be handled like the `turn` type, the existance of correct `modifier` values is guranteed. @@ -517,6 +523,7 @@ step. | `type` | Description | |------------------------|---------------------------------------------------------------------------------------------------------------------------| | `roundabout` | Number of the roundabout exit to take. If exit is `undefined` the destination is on the roundabout. | + | else | Indicates the number of intersections passed until the turn. Example instruction: `at the fourth intersection, turn left` | New properties (potentially depending on `type`) may be introduced in the future without an API version change. diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp index f094110fa..819d92dfc 100644 --- a/include/engine/guidance/assemble_steps.hpp +++ b/include/engine/guidance/assemble_steps.hpp @@ -28,12 +28,10 @@ namespace guidance { namespace detail { - -Intersection intersectionFromGeometry(const WaypointType waypoint_type, - const double segment_duration, - const LegGeometry &leg_geometry, - const std::size_t segment_index); - +std::pair getDepartBearings(const LegGeometry &leg_geometry); +std::pair getArriveBearings(const LegGeometry &leg_geometry); +std::pair getIntermediateBearings(const LegGeometry &leg_geometry, + const std::size_t segment_index); } // ns detail template @@ -65,16 +63,19 @@ std::vector assembleSteps(const DataFacadeT &facade, std::size_t segment_index = 0; BOOST_ASSERT(leg_geometry.locations.size() >= 2); + auto bearings = detail::getDepartBearings(leg_geometry); + std::cout << "depart bearings: " << bearings.first << "->" << bearings.second << std::endl; + + StepManeuver maneuver{source_node.location, bearings.first, + bearings.second, extractor::guidance::TurnInstruction::NO_TURN(), + WaypointType::Depart, 0}; + Intersection intersection{ + source_node.location, + std::vector({bearings.second}), + std::vector({true}), Intersection::NO_INDEX, 0}; + if (leg_data.size() > 0) { - StepManeuver maneuver = {extractor::guidance::TurnInstruction::NO_TURN(), - WaypointType::Depart, 0}; - - auto intersection = - detail::intersectionFromGeometry(WaypointType::Depart, 0, leg_geometry, 0); - intersection = util::guidance::setIntersectionClasses(std::move(intersection), source_node); - // maneuver.location = source_node.location; - // PathData saves the information we need of the segment _before_ the turn, // but a RouteStep is with regard to the segment after the turn. // We need to skip the first segment because it is already covered by the @@ -95,16 +96,7 @@ std::vector assembleSteps(const DataFacadeT &facade, BOOST_ASSERT(segment_duration >= 0); const auto name = facade.GetNameForID(step_name_id); const auto distance = leg_geometry.segment_distances[segment_index]; - std::vector intersections(1, intersection); - intersection = detail::intersectionFromGeometry( - WaypointType::None, segment_duration / 10., leg_geometry, segment_index); - intersection.entry_class = facade.GetEntryClass(path_point.entry_classid); - intersection.bearing_class = - facade.GetBearingClass(facade.GetBearingClassID(path_point.turn_via_node)); - steps.push_back(RouteStep{ - step_name_id, name, NO_ROTARY_NAME, segment_duration / 10.0, distance, - path_point.travel_mode, maneuver, leg_geometry.FrontIndex(segment_index), - leg_geometry.BackIndex(segment_index) + 1, std::move(intersections)}); + if (leg_data_index + 1 < leg_data.size()) { step_name_id = leg_data[leg_data_index + 1].name_id; @@ -113,7 +105,26 @@ std::vector assembleSteps(const DataFacadeT &facade, { step_name_id = target_node.name_id; } - maneuver = {path_point.turn_instruction, WaypointType::None, 0}; + steps.push_back(RouteStep{ + step_name_id, name, NO_ROTARY_NAME, segment_duration / 10.0, distance, + path_point.travel_mode, maneuver, leg_geometry.FrontIndex(segment_index), + leg_geometry.BackIndex(segment_index) + 1, {intersection}}); + + bearings = detail::getIntermediateBearings(leg_geometry, segment_index); + const auto entry_class = facade.GetEntryClass(path_point.entry_classid); + const auto bearing_class = facade.GetBearingClass(facade.GetBearingClassID(path_point.turn_via_node)); + intersection.in = bearing_class.findMatchingBearing(util::bearing::reverseBearing(bearings.first)); + intersection.out = bearing_class.findMatchingBearing(bearings.second); + intersection.location = facade.GetCoordinateOfNode(path_point.turn_via_node); + intersection.bearings.clear(); + std::copy(bearing_class.getAvailableBearings().begin(), bearing_class.getAvailableBearings().end(), + std::back_inserter(intersection.bearings)); + intersection.entry.clear(); + for (auto idx : util::irange(0, intersection.bearings.size())) + { + intersection.entry.push_back(entry_class.allowsEntry(idx)); + } + maneuver = {intersection.location, bearings.first, bearings.second, path_point.turn_instruction, WaypointType::None, 0}; segment_index++; segment_duration = 0; } @@ -124,7 +135,7 @@ std::vector assembleSteps(const DataFacadeT &facade, steps.push_back(RouteStep{ step_name_id, facade.GetNameForID(step_name_id), NO_ROTARY_NAME, duration / 10., distance, target_mode, maneuver, leg_geometry.FrontIndex(segment_index), - leg_geometry.BackIndex(segment_index) + 1, std::vector(1, intersection)}); + leg_geometry.BackIndex(segment_index) + 1, {intersection}}); } // In this case the source + target are on the same edge segment else @@ -135,38 +146,42 @@ std::vector assembleSteps(const DataFacadeT &facade, // |---| source_duration // |---------| target_duration - StepManeuver maneuver = {extractor::guidance::TurnInstruction::NO_TURN(), - WaypointType::Depart, 0}; int duration = target_duration - source_duration; BOOST_ASSERT(duration >= 0); - auto intersection = detail::intersectionFromGeometry(WaypointType::Depart, duration / 10., - leg_geometry, segment_index); - intersection = util::guidance::setIntersectionClasses(std::move(intersection), source_node); - steps.push_back(RouteStep{ source_node.name_id, facade.GetNameForID(source_node.name_id), NO_ROTARY_NAME, duration / 10., leg_geometry.segment_distances[segment_index], source_mode, std::move(maneuver), leg_geometry.FrontIndex(segment_index), - leg_geometry.BackIndex(segment_index) + 1, std::vector(1, intersection)}); + leg_geometry.BackIndex(segment_index) + 1, {intersection}}); } BOOST_ASSERT(segment_index == number_of_segments - 1); + bearings = detail::getArriveBearings(leg_geometry); // This step has length zero, the only reason we need it is the target location - StepManeuver final_maneuver = {extractor::guidance::TurnInstruction::NO_TURN(), - WaypointType::Arrive, 0}; - - auto intersection = - detail::intersectionFromGeometry(WaypointType::Arrive, 0, leg_geometry, segment_index); - intersection = util::guidance::setIntersectionClasses(std::move(intersection), target_node); + maneuver = {intersection.location, bearings.first, bearings.second, extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, 0}; + intersection = { + target_node.location, + std::vector({static_cast(util::bearing::reverseBearing(bearings.first))}), + std::vector({true}), 0, Intersection::NO_INDEX}; BOOST_ASSERT(!leg_geometry.locations.empty()); steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id), NO_ROTARY_NAME, ZERO_DURATION, ZERO_DISTANCE, target_mode, - std::move(final_maneuver), leg_geometry.locations.size() - 1, + std::move(maneuver), leg_geometry.locations.size() - 1, leg_geometry.locations.size(), - std::vector(1, intersection)}); + {intersection}}); + + BOOST_ASSERT(steps.front().intersections.size() == 1); + BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart); + + BOOST_ASSERT(steps.back().intersections.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive); return steps; } diff --git a/include/engine/guidance/route_step.hpp b/include/engine/guidance/route_step.hpp index 68ff06368..736a7a0a6 100644 --- a/include/engine/guidance/route_step.hpp +++ b/include/engine/guidance/route_step.hpp @@ -28,24 +28,21 @@ namespace guidance // A represenetation of intermediate intersections struct Intersection { - double duration; - double distance; + static const constexpr std::size_t NO_INDEX = std::numeric_limits::max(); util::Coordinate location; - double bearing_before; - double bearing_after; - util::guidance::EntryClass entry_class; - util::guidance::BearingClass bearing_class; + std::vector bearings; + std::vector entry; + std::size_t in; + std::size_t out; }; inline Intersection getInvalidIntersection() { - return {0, - 0, - util::Coordinate{util::FloatLongitude{0.0}, util::FloatLatitude{0.0}}, - 0, - 0, - util::guidance::EntryClass(), - util::guidance::BearingClass()}; + return {util::Coordinate{util::FloatLongitude{0.0}, util::FloatLatitude{0.0}}, + {}, + {}, + Intersection::NO_INDEX, + Intersection::NO_INDEX}; } struct RouteStep @@ -74,7 +71,7 @@ inline RouteStep getInvalidRouteStep() getInvalidStepManeuver(), 0, 0, - std::vector(1, getInvalidIntersection())}; + {getInvalidIntersection()}}; } } } diff --git a/include/engine/guidance/step_maneuver.hpp b/include/engine/guidance/step_maneuver.hpp index 757a3b069..1c2b55a2c 100644 --- a/include/engine/guidance/step_maneuver.hpp +++ b/include/engine/guidance/step_maneuver.hpp @@ -23,6 +23,9 @@ enum class WaypointType : std::uint8_t struct StepManeuver { + util::Coordinate location; + short bearing_before; + short bearing_after; extractor::guidance::TurnInstruction instruction; WaypointType waypoint_type; unsigned exit; @@ -30,7 +33,12 @@ struct StepManeuver inline StepManeuver getInvalidStepManeuver() { - return {extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::None, 0}; + return {util::Coordinate{util::FloatLongitude{0.0}, util::FloatLatitude{0.0}}, + 0, + 0, + extractor::guidance::TurnInstruction::NO_TURN(), + WaypointType::None, + 0}; } } // namespace guidance diff --git a/include/util/bearing.hpp b/include/util/bearing.hpp index f1ba6244f..ad2d9816e 100644 --- a/include/util/bearing.hpp +++ b/include/util/bearing.hpp @@ -89,6 +89,14 @@ inline bool CheckInBounds(const int A, const int B, const int range) return normalized_B - range <= normalized_A && normalized_A <= normalized_B + range; } } + +inline double reverseBearing(const double bearing) +{ + if (bearing >= 180) + return bearing - 180.; + return bearing + 180; +} + } } } diff --git a/include/util/guidance/toolkit.hpp b/include/util/guidance/toolkit.hpp index 8a029750e..eec7adbb3 100644 --- a/include/util/guidance/toolkit.hpp +++ b/include/util/guidance/toolkit.hpp @@ -49,36 +49,6 @@ inline extractor::guidance::DirectionModifier getTurnDirection(const double angl return extractor::guidance::DirectionModifier::UTurn; } -inline engine::guidance::Intersection -setIntersectionClasses(engine::guidance::Intersection intersection, - const engine::PhantomNode &phantom) -{ - BOOST_ASSERT(intersection.bearing_before == 0 || intersection.bearing_after == 0); - const double bearing = std::max(intersection.bearing_before, intersection.bearing_after); - - intersection.bearing_class = {}; - intersection.entry_class = {}; - if (bearing >= 180.) - { - intersection.bearing_class.add(std::round(bearing - 180.)); - if (phantom.forward_segment_id.id != SPECIAL_SEGMENTID && - phantom.reverse_segment_id.id != SPECIAL_SEGMENTID) - intersection.entry_class.activate(0); - intersection.bearing_class.add(std::round(bearing)); - intersection.entry_class.activate(1); - } - else - { - intersection.bearing_class.add(std::round(bearing)); - intersection.entry_class.activate(0); - intersection.bearing_class.add(std::round(bearing + 180.)); - if (phantom.forward_segment_id.id != SPECIAL_SEGMENTID && - phantom.reverse_segment_id.id != SPECIAL_SEGMENTID) - intersection.entry_class.activate(1); - } - return intersection; -} - } // namespace guidance } // namespace util } // namespace osrm diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp index a7afd2813..12b059d70 100644 --- a/src/engine/api/json_factory.cpp +++ b/src/engine/api/json_factory.cpp @@ -78,63 +78,6 @@ util::json::Array coordinateToLonLat(const util::Coordinate coordinate) return array; } -util::json::Object -getIntersection(const guidance::Intersection &intersection, bool locate_before, bool locate_after) -{ - util::json::Object result; - util::json::Array bearings; - util::json::Array entry; - - const auto &available_bearings = intersection.bearing_class.getAvailableBearings(); - for (std::size_t i = 0; i < available_bearings.size(); ++i) - { - bearings.values.push_back(available_bearings[i]); - entry.values.push_back(intersection.entry_class.allowsEntry(i) ? "true" : "false"); - } - result.values["location"] = detail::coordinateToLonLat(intersection.location); - - if (locate_before) - { - // bearings are oriented in the direction of driving. For the in-bearing, we actually need - // to - // find the bearing from the view of the intersection. This means we have to rotate the - // bearing - // by 180 degree. - const auto rotated_bearing_before = (intersection.bearing_before >= 180.0) - ? (intersection.bearing_before - 180.0) - : (intersection.bearing_before + 180.0); - result.values["in"] = - intersection.bearing_class.findMatchingBearing(rotated_bearing_before); - } - - if (locate_after) - { - result.values["out"] = - intersection.bearing_class.findMatchingBearing(intersection.bearing_after); - } - - result.values["bearings"] = bearings; - result.values["entry"] = entry; - - return result; -} - -util::json::Array getIntersection(const guidance::RouteStep &step) -{ - util::json::Array result; - bool first = true; - for (const auto &intersection : step.intersections) - { - // on waypoints, the first/second bearing is invalid. In these cases, we cannot locate the - // bearing as part of the available bearings at the intersection. - result.values.push_back(getIntersection( - intersection, !first || step.maneuver.waypoint_type != guidance::WaypointType::Depart, - !first || step.maneuver.waypoint_type != guidance::WaypointType::Arrive)); - first = false; - } - return result; -} - // FIXME this actually needs to be configurable from the profiles std::string modeToString(const extractor::TravelMode mode) { @@ -198,12 +141,46 @@ util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver) step_maneuver.values["modifier"] = detail::instructionModifierToString(maneuver.instruction.direction_modifier); + step_maneuver.values["location"] = detail::coordinateToLonLat(maneuver.location); + step_maneuver.values["bearing_before"] = std::round(maneuver.bearing_before); + step_maneuver.values["bearing_after"] = std::round(maneuver.bearing_after); if (maneuver.exit != 0) step_maneuver.values["exit"] = maneuver.exit; return step_maneuver; } +util::json::Object +makeIntersection(const guidance::Intersection &intersection) +{ + util::json::Object result; + util::json::Array bearings; + util::json::Array entry; + + bearings.values.reserve(intersection.bearings.size()); + std::copy(intersection.bearings.begin(), intersection.bearings.end(), std::back_inserter(bearings.values)); + + entry.values.reserve(intersection.entry.size()); + std::transform(intersection.entry.begin(), intersection.entry.end(), + std::back_inserter(entry.values), [](const bool has_entry) -> util::json::Value + { + if (has_entry) + return util::json::True(); + else + return util::json::False(); + }); + + result.values["location"] = detail::coordinateToLonLat(intersection.location); + result.values["bearings"] = bearings; + result.values["entry"] = entry; + if (intersection.in != guidance::Intersection::NO_INDEX) + result.values["in"] = intersection.in; + if (intersection.out != guidance::Intersection::NO_INDEX) + result.values["out"] = intersection.out; + + return result; +} + util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geometry) { util::json::Object route_step; @@ -216,7 +193,13 @@ util::json::Object makeRouteStep(guidance::RouteStep step, util::json::Value geo route_step.values["mode"] = detail::modeToString(std::move(step.mode)); route_step.values["maneuver"] = makeStepManeuver(std::move(step.maneuver)); route_step.values["geometry"] = std::move(geometry); - route_step.values["intersections"] = detail::getIntersection(step); + + util::json::Array intersections; + intersections.values.reserve(step.intersections.size()); + std::transform(step.intersections.begin(), step.intersections.end(), + std::back_inserter(intersections.values), makeIntersection); + route_step.values["intersections"] = std::move(intersections); + return route_step; } diff --git a/src/engine/guidance/assemble_steps.cpp b/src/engine/guidance/assemble_steps.cpp index 6c6b4c8d6..c23efc49e 100644 --- a/src/engine/guidance/assemble_steps.cpp +++ b/src/engine/guidance/assemble_steps.cpp @@ -13,11 +13,8 @@ namespace guidance { namespace detail { -namespace -{ -void fillInIntermediate(Intersection &intersection, - const LegGeometry &leg_geometry, - const std::size_t segment_index) +std::pair getIntermediateBearings(const LegGeometry &leg_geometry, + const std::size_t segment_index) { auto turn_index = leg_geometry.BackIndex(segment_index); BOOST_ASSERT(turn_index > 0); @@ -28,58 +25,27 @@ void fillInIntermediate(Intersection &intersection, const auto turn_coordinate = leg_geometry.locations[turn_index]; const auto post_turn_coordinate = leg_geometry.locations[turn_index + 1]; - intersection.bearing_before = - util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate); - intersection.bearing_after = - util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate); - - intersection.location = turn_coordinate; + return std::make_pair( + std::round(util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate)), + std::round(util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate))); } -void fillInDepart(Intersection &intersection, const LegGeometry &leg_geometry) +std::pair getDepartBearings(const LegGeometry &leg_geometry) { BOOST_ASSERT(leg_geometry.locations.size() >= 2); const auto turn_coordinate = leg_geometry.locations.front(); const auto post_turn_coordinate = *(leg_geometry.locations.begin() + 1); - intersection.location = turn_coordinate; - intersection.bearing_before = 0; - intersection.bearing_after = - util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate); + return std::make_pair( + 0, std::round(util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate))); } -void fillInArrive(Intersection &intersection, const LegGeometry &leg_geometry) +std::pair getArriveBearings(const LegGeometry &leg_geometry) { BOOST_ASSERT(leg_geometry.locations.size() >= 2); const auto turn_coordinate = leg_geometry.locations.back(); const auto pre_turn_coordinate = *(leg_geometry.locations.end() - 2); - intersection.location = turn_coordinate; - intersection.bearing_before = - util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate); - intersection.bearing_after = 0; -} -} // namespace - -Intersection intersectionFromGeometry(const WaypointType waypoint_type, - const double segment_duration, - const LegGeometry &leg_geometry, - const std::size_t segment_index) -{ - Intersection intersection; - intersection.duration = segment_duration; - intersection.distance = leg_geometry.segment_distances[segment_index]; - switch (waypoint_type) - { - case WaypointType::None: - fillInIntermediate(intersection, leg_geometry, segment_index); - break; - case WaypointType::Depart: - fillInDepart(intersection, leg_geometry); - break; - case WaypointType::Arrive: - fillInArrive(intersection, leg_geometry); - break; - } - return intersection; + return std::make_pair( + std::round(util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate)), 0); } } // ns detail } // ns engine diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp index 677163c9e..6801cebd2 100644 --- a/src/engine/guidance/post_processing.cpp +++ b/src/engine/guidance/post_processing.cpp @@ -54,11 +54,12 @@ void print(const std::vector &steps) for (const auto &intersection : step.intersections) { - std::cout << "(" << intersection.duration << " " << intersection.distance << " " - << " Bearings: " << intersection.bearing_before << " " - << intersection.bearing_after << " -- "; - for( auto bearing : intersection.bearing_class.getAvailableBearings() ) + std::cout << "(bearings:"; + for( auto bearing : intersection.bearings) std:: cout << " " << bearing; + std::cout << ", entry: "; + for( auto entry : intersection.entry) + std:: cout << " " << entry; std::cout << ")"; } @@ -86,6 +87,7 @@ RouteStep forwardInto(RouteStep destination, const RouteStep &source) destination.geometry_begin = std::min(destination.geometry_begin, source.geometry_begin); destination.geometry_end = std::max(destination.geometry_end, source.geometry_end); + destination.maneuver.exit = destination.intersections.size() - 1; return destination; } @@ -205,7 +207,8 @@ void closeOffRoundabout(const bool on_roundabout, // removal. std::vector intermediate_steps; BOOST_ASSERT(!steps[step_index].intersections.empty()); - const auto exit_bearing = steps[step_index].intersections.back().bearing_after; + const auto exit_intersection = steps[step_index].intersections.back(); + const auto exit_bearing = exit_intersection.bearings[exit_intersection.out]; if (step_index > 1) { // The very first route-step is head, so we cannot iterate past that one @@ -218,6 +221,7 @@ void closeOffRoundabout(const bool on_roundabout, { propagation_step.maneuver.exit = step.maneuver.exit; propagation_step.geometry_end = step.geometry_end; + const auto entry_intersection = propagation_step.intersections.front(); // remember rotary name if (propagation_step.maneuver.instruction.type == TurnType::EnterRotary || @@ -259,7 +263,7 @@ void closeOffRoundabout(const bool on_roundabout, const auto angle = 540 - rotated_exit; return angle > 360 ? angle - 360 : angle; - }(propagation_step.intersections.front().bearing_before, exit_bearing); + }(util::bearing::reverseBearing(entry_intersection.bearings[entry_intersection.in]), exit_bearing); propagation_step.maneuver.instruction.direction_modifier = ::osrm::util::guidance::getTurnDirection(angle); @@ -376,8 +380,8 @@ void collapseTurnAt(std::vector &steps, } } // Potential U-Turn - else if (bearingsAreReversed(one_back_step.intersections.front().bearing_before, - current_step.intersections.front().bearing_after)) + else if (bearingsAreReversed(util::bearing::reverseBearing(one_back_step.intersections.front().bearings[one_back_step.intersections.front().in]), + current_step.intersections.front().bearings[current_step.intersections.front().out])) { BOOST_ASSERT(two_back_index < steps.size()); @@ -433,6 +437,17 @@ std::vector removeNoTurnInstructions(std::vector steps) }; boost::remove_erase_if(steps, not_is_valid); + + BOOST_ASSERT(steps.front().intersections.size() >= 1); + BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart); + + BOOST_ASSERT(steps.back().intersections.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive); + return steps; } @@ -442,7 +457,6 @@ std::vector removeNoTurnInstructions(std::vector steps) // They are required for maintenance purposes. We can calculate the number // of exits to pass in a roundabout and the number of intersections // that we come across. - std::vector postProcess(std::vector steps) { // the steps should always include the first/last step in form of a location @@ -507,6 +521,7 @@ std::vector postProcess(std::vector steps) last_valid_instruction = step_index; } } + // unterminated roundabout // Move backwards through the instructions until the start and remove the exit number // A roundabout without exit translates to enter-roundabout. @@ -515,6 +530,16 @@ std::vector postProcess(std::vector steps) fixFinalRoundabout(steps); } + BOOST_ASSERT(steps.front().intersections.size() >= 1); + BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart); + + BOOST_ASSERT(steps.back().intersections.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive); + return removeNoTurnInstructions(std::move(steps)); } @@ -603,18 +628,29 @@ std::vector collapseTurns(std::vector steps) collapseTurnAt(steps, two_back_index, one_back_index, step_index); } } + + BOOST_ASSERT(steps.front().intersections.size() >= 1); + BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart); + + BOOST_ASSERT(steps.back().intersections.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive); + return removeNoTurnInstructions(std::move(steps)); } +// Doing this step in post-processing provides a few challenges we cannot overcome. +// The removal of an initial step imposes some copy overhead in the steps, moving all later +// steps to the front. In addition, we cannot reduce the travel time that is accumulated at a +// different location. +// As a direct implication, we have to keep the time of the initial/final turns (which adds a +// few seconds of inaccuracy at both ends. This is acceptable, however, since the turn should +// usually not be as relevant. void trimShortSegments(std::vector &steps, LegGeometry &geometry) { - // Doing this step in post-processing provides a few challenges we cannot overcome. - // The removal of an initial step imposes some copy overhead in the steps, moving all later - // steps to the front. In addition, we cannot reduce the travel time that is accumulated at a - // different location. - // As a direct implication, we have to keep the time of the initial/final turns (which adds a - // few seconds of inaccuracy at both ends. This is acceptable, however, since the turn should - // usually not be as relevant. if (steps.size() < 2 || geometry.locations.size() <= 2) return; @@ -669,20 +705,29 @@ void trimShortSegments(std::vector &steps, LegGeometry &geometry) // update initial turn direction/bearings. Due to the duplicated first coordinate, // the initial bearing is invalid - designated_depart.maneuver = {TurnInstruction::NO_TURN(), WaypointType::Depart, 0}; + designated_depart.maneuver.waypoint_type = WaypointType::Depart; + designated_depart.maneuver.bearing_before = 0; + designated_depart.maneuver.instruction = TurnInstruction::NO_TURN(); + // we need to make this conform with the intersection format for the first intersection + auto& first_intersection = designated_depart.intersections.front(); + first_intersection.bearings = {first_intersection.bearings[first_intersection.out]}; + first_intersection.entry = {true}; + first_intersection.in = Intersection::NO_INDEX; + first_intersection.out = 0; // finally remove the initial (now duplicated move) steps.erase(steps.begin()); } else { + // we need to make this at least 1 because we will substract 1 + // from all offsets at the end of the loop. steps.front().geometry_begin = 1; + // reduce all offsets by one (inplace) std::transform(geometry.segment_offsets.begin(), geometry.segment_offsets.end(), geometry.segment_offsets.begin(), [](const std::size_t val) { return val - 1; }); - - steps.front().maneuver = {TurnInstruction::NO_TURN(), WaypointType::Depart, 0}; } // and update the leg geometry indices for the removed entry @@ -690,12 +735,32 @@ void trimShortSegments(std::vector &steps, LegGeometry &geometry) --step.geometry_begin; --step.geometry_end; }); + + auto& first_step = steps.front(); + // we changed the geometry, we need to recalculate the bearing + auto bearing = std::round(util::coordinate_calculation::bearing( + geometry.locations[first_step.geometry_begin], + geometry.locations[first_step.geometry_begin+1])); + std::cout << geometry.locations[first_step.geometry_begin] << geometry.locations[first_step.geometry_begin+1] << std::endl; + first_step.maneuver.bearing_after = bearing; + first_step.intersections.front().bearings.front() = bearing; } + BOOST_ASSERT(steps.front().intersections.size() >= 1); + BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart); + + BOOST_ASSERT(steps.back().intersections.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive); + // make sure we still have enough segments if (steps.size() < 2 || geometry.locations.size() == 2) return; + BOOST_ASSERT(geometry.locations.size() >= steps.size()); auto &next_to_last_step = *(steps.end() - 2); // in the end, the situation with the roundabout cannot occur. As a result, we can remove @@ -708,9 +773,15 @@ void trimShortSegments(std::vector &steps, LegGeometry &geometry) BOOST_ASSERT(geometry.segment_distances.back() < 1); geometry.segment_distances.pop_back(); - next_to_last_step.maneuver = {TurnInstruction::NO_TURN(), WaypointType::Arrive, 0}; - BOOST_ASSERT(!next_to_last_step.intersections.empty()); - next_to_last_step.intersections.front().bearing_after = 0; + next_to_last_step.maneuver.waypoint_type = WaypointType::Arrive; + next_to_last_step.maneuver.instruction = TurnInstruction::NO_TURN(); + next_to_last_step.maneuver.bearing_after = 0; + BOOST_ASSERT(next_to_last_step.intersections.size() == 1); + auto& last_intersection = next_to_last_step.intersections.back(); + last_intersection.bearings = {last_intersection.bearings[last_intersection.in]}; + last_intersection.entry = {true}; + last_intersection.out = Intersection::NO_INDEX; + last_intersection.in = 0; steps.pop_back(); // Because we eliminated a really short segment, it was probably @@ -738,10 +809,29 @@ void trimShortSegments(std::vector &steps, LegGeometry &geometry) BOOST_ASSERT(next_to_last_step.geometry_end == steps.back().geometry_begin + 1); BOOST_ASSERT(next_to_last_step.geometry_begin < next_to_last_step.geometry_end); next_to_last_step.geometry_end--; - steps.back().geometry_begin--; - steps.back().geometry_end--; - steps.back().maneuver = {TurnInstruction::NO_TURN(), WaypointType::Arrive, 0}; + auto& last_step = steps.back(); + last_step.geometry_begin--; + last_step.geometry_end--; + BOOST_ASSERT(next_to_last_step.geometry_end == last_step.geometry_begin + 1); + BOOST_ASSERT(last_step.geometry_begin == last_step.geometry_end-1); + BOOST_ASSERT(next_to_last_step.geometry_end >= 2); + // we changed the geometry, we need to recalculate the bearing + auto bearing = std::round(util::coordinate_calculation::bearing( + 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::bearing::reverseBearing(bearing); } + + BOOST_ASSERT(steps.front().intersections.size() >= 1); + BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart); + + BOOST_ASSERT(steps.back().intersections.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive); } // assign relative locations to depart/arrive instructions @@ -765,12 +855,6 @@ std::vector assignRelativeLocations(std::vector steps, : extractor::guidance::DirectionModifier::UTurn; steps.front().maneuver.instruction.direction_modifier = initial_modifier; - BOOST_ASSERT(!steps.front().intersections.empty()); - steps.front().intersections.front().bearing_before = 0; - steps.front().intersections.front().bearing_after = - util::coordinate_calculation::bearing(leg_geometry.locations[0], leg_geometry.locations[1]); - steps.front().intersections.front() = util::guidance::setIntersectionClasses( - std::move(steps.front().intersections.front()), source_node); const auto distance_from_end = util::coordinate_calculation::haversineDistance( target_node.input_location, leg_geometry.locations.back()); @@ -784,15 +868,16 @@ std::vector assignRelativeLocations(std::vector steps, : extractor::guidance::DirectionModifier::UTurn; steps.back().maneuver.instruction.direction_modifier = final_modifier; - BOOST_ASSERT(steps.back().intersections.size() == 1); - BOOST_ASSERT(!steps.back().intersections.empty()); - steps.back().intersections.front().bearing_before = util::coordinate_calculation::bearing( - leg_geometry.locations[leg_geometry.locations.size() - 2], - leg_geometry.locations[leg_geometry.locations.size() - 1]); - steps.back().intersections.front().bearing_after = 0; - steps.back().intersections.front() = util::guidance::setIntersectionClasses( - std::move(steps.back().intersections.front()), target_node); + BOOST_ASSERT(steps.front().intersections.size() >= 1); + BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart); + + BOOST_ASSERT(steps.back().intersections.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1); + BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1); + BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive); return steps; }