From ef1e0e14ec67fd41372bbeaef5025b7f23fee45f Mon Sep 17 00:00:00 2001 From: Moritz Kobitzsch Date: Wed, 24 Feb 2016 10:29:23 +0100 Subject: [PATCH] advanced guidance on 5.0 --- CMakeLists.txt | 6 +- include/engine/api/json_factory.hpp | 5 +- include/engine/api/route_api.hpp | 6 +- include/engine/datafacade/datafacade_base.hpp | 4 +- .../engine/datafacade/internal_datafacade.hpp | 4 +- .../engine/datafacade/shared_datafacade.hpp | 8 +- include/engine/guidance/assemble_geometry.hpp | 4 +- include/engine/guidance/assemble_leg.hpp | 2 +- include/engine/guidance/assemble_steps.hpp | 49 +- .../engine/guidance/classification_data.hpp | 114 ++++ include/engine/guidance/guidance_toolkit.hpp | 416 +++++++++++++ .../guidance/instruction_symbols.hpp} | 77 ++- include/engine/guidance/post_processing.hpp | 21 + .../engine/guidance/segment_compression.hpp | 66 --- include/engine/guidance/step_maneuver.hpp | 11 +- .../engine/guidance/turn_classification.hpp | 104 ++++ include/engine/guidance/turn_instruction.hpp | 154 +++++ include/engine/internal_route_result.hpp | 8 +- include/engine/polyline_compressor.hpp | 6 +- .../routing_algorithms/routing_base.hpp | 22 +- .../extractor/edge_based_graph_factory.hpp | 53 +- include/extractor/extraction_way.hpp | 3 + include/extractor/internal_extractor_edge.hpp | 16 +- include/extractor/node_based_edge.hpp | 43 +- include/extractor/original_edge_data.hpp | 9 +- include/util/coordinate_calculation.hpp | 6 +- include/util/node_based_graph.hpp | 3 + src/contractor/contractor.cpp | 2 +- src/engine/api/json_factory.cpp | 78 +-- src/engine/guidance/classification_data.cpp | 21 + src/engine/guidance/post_processing.cpp | 150 +++++ src/engine/polyline_compressor.cpp | 1 + src/extractor/edge_based_graph_factory.cpp | 551 +++++++++++++----- src/extractor/extractor.cpp | 1 + src/extractor/extractor_callbacks.cpp | 6 +- src/storage/storage.cpp | 10 +- src/util/coordinate_calculation.cpp | 10 + 37 files changed, 1638 insertions(+), 412 deletions(-) create mode 100644 include/engine/guidance/classification_data.hpp create mode 100644 include/engine/guidance/guidance_toolkit.hpp rename include/{extractor/turn_instructions.hpp => engine/guidance/instruction_symbols.hpp} (69%) create mode 100644 include/engine/guidance/post_processing.hpp delete mode 100644 include/engine/guidance/segment_compression.hpp create mode 100644 include/engine/guidance/turn_classification.hpp create mode 100644 include/engine/guidance/turn_instruction.hpp create mode 100644 src/engine/guidance/classification_data.cpp create mode 100644 src/engine/guidance/post_processing.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6893abd1f..5b2b50dee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,11 +55,12 @@ configure_file( ${CMAKE_CURRENT_BINARY_DIR}/include/util/version.hpp ) file(GLOB UtilGlob src/util/*.cpp) -file(GLOB ExtractorGlob src/extractor/*.cpp) +file(GLOB ExtractorGlob src/extractor/*.cpp src/engine/guidance/classification_data.cpp) file(GLOB ContractorGlob src/contractor/*.cpp) file(GLOB StorageGlob src/storage/*.cpp) file(GLOB ServerGlob src/server/*.cpp src/server/**/*.cpp) file(GLOB EngineGlob src/engine/*.cpp src/engine/**/*.cpp) +file(GLOB GuidanceGlob src/guidance/*.cpp src/guidance/**/*.cpp) file(GLOB ExtractorTestsGlob unit_tests/extractor/*.cpp) file(GLOB EngineTestsGlob unit_tests/engine/*.cpp) file(GLOB UtilTestsGlob unit_tests/util/*.cpp) @@ -71,6 +72,7 @@ add_library(EXTRACTOR OBJECT ${ExtractorGlob}) add_library(CONTRACTOR OBJECT ${ContractorGlob}) add_library(STORAGE OBJECT ${StorageGlob}) add_library(ENGINE OBJECT ${EngineGlob}) +add_library(GUIDANCE OBJECT ${GuidanceGlob}) add_library(SERVER OBJECT ${ServerGlob}) add_dependencies(UTIL FingerPrintConfigure) @@ -80,7 +82,7 @@ add_executable(osrm-extract src/tools/extract.cpp) add_executable(osrm-contract src/tools/contract.cpp) add_executable(osrm-routed src/tools/routed.cpp $ $) add_executable(osrm-datastore src/tools/store.cpp $) -add_library(osrm src/osrm/osrm.cpp $ $) +add_library(osrm src/osrm/osrm.cpp $ $ $) add_library(osrm_extract $ $) add_library(osrm_contract $ $) add_library(osrm_store $ $) diff --git a/include/engine/api/json_factory.hpp b/include/engine/api/json_factory.hpp index f3e4dcb03..e2801c7c6 100644 --- a/include/engine/api/json_factory.hpp +++ b/include/engine/api/json_factory.hpp @@ -1,7 +1,7 @@ #ifndef ENGINE_RESPONSE_OBJECTS_HPP_ #define ENGINE_RESPONSE_OBJECTS_HPP_ -#include "extractor/turn_instructions.hpp" +#include "engine/guidance/turn_instruction.hpp" #include "extractor/travel_mode.hpp" #include "engine/polyline_compressor.hpp" #include "engine/guidance/route_step.hpp" @@ -32,7 +32,8 @@ namespace json namespace detail { -std::string instructionToString(extractor::TurnInstruction instruction); +std::string instructionTypeToString(guidance::TurnType type); +std::string instructionModifierToString(guidance::DirectionModifier modifier); util::json::Array coordinateToLonLat(const util::Coordinate coordinate); diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index c240fa29c..0838a7368 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -12,6 +12,7 @@ #include "engine/guidance/assemble_geometry.hpp" #include "engine/guidance/assemble_overview.hpp" #include "engine/guidance/assemble_steps.hpp" +#include "engine/guidance/post_processing.hpp" #include "engine/internal_route_result.hpp" @@ -67,7 +68,7 @@ class RouteAPI : public BaseAPI } util::json::Object MakeRoute(const std::vector &segment_end_coordinates, - const std::vector> &unpacked_path_segments, + std::vector> unpacked_path_segments, const std::vector &source_traversed_in_reverse, const std::vector &target_traversed_in_reverse) const { @@ -76,10 +77,13 @@ class RouteAPI : public BaseAPI auto number_of_legs = segment_end_coordinates.size(); legs.reserve(number_of_legs); leg_geometries.reserve(number_of_legs); + + unpacked_path_segments = guidance::postProcess( std::move(unpacked_path_segments) ); for (auto idx : util::irange(0UL, number_of_legs)) { const auto &phantoms = segment_end_coordinates[idx]; const auto &path_data = unpacked_path_segments[idx]; + const bool reversed_source = source_traversed_in_reverse[idx]; const bool reversed_target = target_traversed_in_reverse[idx]; diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index 1dc2e77cd..3c736b8ea 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -7,7 +7,7 @@ #include "extractor/external_memory_node.hpp" #include "contractor/query_edge.hpp" #include "engine/phantom_node.hpp" -#include "extractor/turn_instructions.hpp" +#include "engine/guidance/turn_instruction.hpp" #include "util/integer_range.hpp" #include "util/exception.hpp" #include "util/string_util.hpp" @@ -76,7 +76,7 @@ class BaseDataFacade virtual void GetUncompressedWeights(const EdgeID id, std::vector &result_weights) const = 0; - virtual extractor::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const = 0; + virtual guidance::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const = 0; virtual extractor::TravelMode GetTravelModeForEdgeID(const unsigned id) const = 0; diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp index 02784fc2e..1346b9a5b 100644 --- a/include/engine/datafacade/internal_datafacade.hpp +++ b/include/engine/datafacade/internal_datafacade.hpp @@ -68,7 +68,7 @@ class InternalDataFacade final : public BaseDataFacade std::shared_ptr::vector> m_coordinate_list; util::ShM::vector m_via_node_list; util::ShM::vector m_name_ID_list; - util::ShM::vector m_turn_instruction_list; + util::ShM::vector m_turn_instruction_list; util::ShM::vector m_travel_mode_list; util::ShM::vector m_names_char_list; util::ShM::vector m_geometry_indices; @@ -327,7 +327,7 @@ class InternalDataFacade final : public BaseDataFacade return m_coordinate_list->at(id); } - extractor::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const override final + guidance::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const override final { return m_turn_instruction_list.at(id); } diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp index 74973cd60..e7000a6c7 100644 --- a/include/engine/datafacade/shared_datafacade.hpp +++ b/include/engine/datafacade/shared_datafacade.hpp @@ -70,7 +70,7 @@ class SharedDataFacade final : public BaseDataFacade std::shared_ptr::vector> m_coordinate_list; util::ShM::vector m_via_node_list; util::ShM::vector m_name_ID_list; - util::ShM::vector m_turn_instruction_list; + util::ShM::vector m_turn_instruction_list; util::ShM::vector m_travel_mode_list; util::ShM::vector m_names_char_list; util::ShM::vector m_name_begin_indices; @@ -145,9 +145,9 @@ class SharedDataFacade final : public BaseDataFacade travel_mode_list_ptr, data_layout->num_entries[storage::SharedDataLayout::TRAVEL_MODE]); m_travel_mode_list = std::move(travel_mode_list); - auto turn_instruction_list_ptr = data_layout->GetBlockPtr( + auto turn_instruction_list_ptr = data_layout->GetBlockPtr( shared_memory, storage::SharedDataLayout::TURN_INSTRUCTION); - typename util::ShM::vector turn_instruction_list( + typename util::ShM::vector turn_instruction_list( turn_instruction_list_ptr, data_layout->num_entries[storage::SharedDataLayout::TURN_INSTRUCTION]); m_turn_instruction_list = std::move(turn_instruction_list); @@ -398,7 +398,7 @@ class SharedDataFacade final : public BaseDataFacade return m_via_node_list.at(id); } - extractor::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const override final + guidance::TurnInstruction GetTurnInstructionForEdgeID(const unsigned id) const override final { return m_turn_instruction_list.at(id); } diff --git a/include/engine/guidance/assemble_geometry.hpp b/include/engine/guidance/assemble_geometry.hpp index e123c2ee2..bc8d1a77d 100644 --- a/include/engine/guidance/assemble_geometry.hpp +++ b/include/engine/guidance/assemble_geometry.hpp @@ -7,7 +7,7 @@ #include "engine/guidance/leg_geometry.hpp" #include "util/coordinate_calculation.hpp" #include "util/coordinate.hpp" -#include "extractor/turn_instructions.hpp" +#include "engine/guidance/turn_instruction.hpp" #include "extractor/travel_mode.hpp" #include @@ -49,7 +49,7 @@ LegGeometry assembleGeometry(const DataFacadeT &facade, current_distance += util::coordinate_calculation::haversineDistance(prev_coordinate, coordinate); - if (path_point.turn_instruction != extractor::TurnInstruction::NoTurn) + if (path_point.turn_instruction != TurnInstruction::NO_TURN()) { geometry.segment_distances.push_back(current_distance); geometry.segment_offsets.push_back(geometry.locations.size()); diff --git a/include/engine/guidance/assemble_leg.hpp b/include/engine/guidance/assemble_leg.hpp index fc91aac7b..f0f855e71 100644 --- a/include/engine/guidance/assemble_leg.hpp +++ b/include/engine/guidance/assemble_leg.hpp @@ -134,7 +134,7 @@ RouteLeg assembleLeg(const DataFacadeT &facade, // `forward_weight`: duration of (d,t) // `forward_offset`: duration of (c, d) // - // The PathData will contain entries of b, c and d. But only c will contain + // The PathData will contain entries of b, c and d. But only c will contain //TODO discuss, this should not be the case after danpats fixes // a duration value since its the only point associated with a turn. // As such we want to slice of the duration for (a,s) and add the duration for // (c,d,t) diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp index 6beeb69da..0becb5e88 100644 --- a/include/engine/guidance/assemble_steps.hpp +++ b/include/engine/guidance/assemble_steps.hpp @@ -4,11 +4,13 @@ #include "engine/guidance/route_step.hpp" #include "engine/guidance/step_maneuver.hpp" #include "engine/guidance/leg_geometry.hpp" +#include "engine/guidance/guidance_toolkit.hpp" +#include "engine/guidance/turn_instruction.hpp" #include "engine/internal_route_result.hpp" #include "engine/phantom_node.hpp" #include "util/coordinate_calculation.hpp" #include "util/coordinate.hpp" -#include "extractor/turn_instructions.hpp" +#include "util/bearing.hpp" #include "extractor/travel_mode.hpp" #include @@ -22,9 +24,10 @@ namespace guidance namespace detail { // FIXME move implementation to cpp -inline StepManeuver stepManeuverFromGeometry(const extractor::TurnInstruction instruction, +inline StepManeuver stepManeuverFromGeometry(const TurnInstruction instruction, const LegGeometry &leg_geometry, - std::size_t segment_index) + const std::size_t segment_index, + const unsigned exit) { auto turn_index = leg_geometry.BackIndex(segment_index); BOOST_ASSERT(turn_index > 0); @@ -40,7 +43,7 @@ inline StepManeuver stepManeuverFromGeometry(const extractor::TurnInstruction in const double post_turn_bearing = util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate); - return StepManeuver{turn_coordinate, pre_turn_bearing, post_turn_bearing, instruction}; + return StepManeuver{turn_coordinate, pre_turn_bearing, post_turn_bearing, instruction, exit}; } } @@ -75,28 +78,31 @@ std::vector assembleSteps(const DataFacadeT &facade, auto segment_index = 0; if (leg_data.size() > 0) { - StepManeuver maneuver = detail::stepManeuverFromGeometry(extractor::TurnInstruction::StartAtEndOfStreet, - leg_geometry, segment_index); + StepManeuver maneuver = detail::stepManeuverFromGeometry( + TurnInstruction{TurnType::Location, DirectionModifier::Straight}, leg_geometry, + segment_index, INVALID_EXIT_NR); + // TODO fix this: it makes no sense // 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 + // initial start of a route for (const auto &path_point : leg_data) { - if (path_point.turn_instruction != extractor::TurnInstruction::NoTurn) + if (path_point.turn_instruction != TurnInstruction::NO_TURN()) { - auto name = facade.get_name_for_id(path_point.name_id); + const auto name = facade.get_name_for_id(path_point.name_id); const auto distance = leg_geometry.segment_distances[segment_index]; - steps.push_back(RouteStep{path_point.name_id, std::move(name), - path_point.duration_until_turn / 10.0, distance, - path_point.travel_mode, maneuver, - leg_geometry.FrontIndex(segment_index), - leg_geometry.BackIndex(segment_index) + 1}); - maneuver = detail::stepManeuverFromGeometry(path_point.turn_instruction, - leg_geometry, segment_index); + steps.push_back(RouteStep{ + path_point.name_id, name, path_point.duration_until_turn / 10.0, distance, + path_point.travel_mode, maneuver, leg_geometry.FrontIndex(segment_index), + leg_geometry.BackIndex(segment_index) + 1}); + maneuver = detail::stepManeuverFromGeometry( + path_point.turn_instruction, leg_geometry, segment_index, path_point.exit); segment_index++; } } + // TODO remove this hack const auto distance = leg_geometry.segment_distances[segment_index]; steps.push_back(RouteStep{target_node.name_id, facade.get_name_for_id(target_node.name_id), target_duration, distance, target_mode, maneuver, @@ -110,19 +116,24 @@ std::vector assembleSteps(const DataFacadeT &facade, // |-------------t target_duration // x---*---*---*---z compressed edge // |-------| duration + StepManeuver maneuver = {source_node.location, 0., 0., + TurnInstruction{TurnType::Location, DirectionModifier::Straight}, + INVALID_EXIT_NR}; + steps.push_back(RouteStep{ source_node.name_id, facade.get_name_for_id(source_node.name_id), target_duration - source_duration, leg_geometry.segment_distances[segment_index], - source_mode, - StepManeuver{source_node.location, 0., 0., extractor::TurnInstruction::StartAtEndOfStreet}, - leg_geometry.FrontIndex(segment_index), leg_geometry.BackIndex(segment_index) + 1}); + source_mode, std::move(maneuver), leg_geometry.FrontIndex(segment_index), + leg_geometry.BackIndex(segment_index) + 1}); } BOOST_ASSERT(segment_index == number_of_segments - 1); // This step has length zero, the only reason we need it is the target location steps.push_back(RouteStep{ target_node.name_id, facade.get_name_for_id(target_node.name_id), 0., 0., target_mode, - StepManeuver{target_node.location, 0., 0., extractor::TurnInstruction::ReachedYourDestination}, + StepManeuver{target_node.location, 0., 0., + TurnInstruction{TurnType::Location, DirectionModifier::Straight}, + INVALID_EXIT_NR}, leg_geometry.locations.size(), leg_geometry.locations.size()}); return steps; diff --git a/include/engine/guidance/classification_data.hpp b/include/engine/guidance/classification_data.hpp new file mode 100644 index 000000000..c3451e847 --- /dev/null +++ b/include/engine/guidance/classification_data.hpp @@ -0,0 +1,114 @@ +#ifndef OSRM_GUIDANCE_CLASSIFICATION_DATA_HPP_ +#define OSRM_GUIDANCE_CLASSIFICATION_DATA_HPP_ + +#include +#include + +#include //TODO remove + +#include "util/simple_logger.hpp" + +// Forward Declaration to allow usage of external osmium::Way +namespace osmium +{ +class Way; +} + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +enum FunctionalRoadClass +{ + MOTORWAY, + MOTORWAY_LINK, + TRUNK, + TRUNK_LINK, + PRIMARY, + PRIMARY_LINK, + SECONDARY, + SECONDARY_LINK, + TERTIARY, + TERTIARY_LINK, + UNCLASSIFIED, + RESIDENTIAL, + SERVICE, + LIVING_STREET, + LOW_PRIORITY_ROAD, // a road simply included for connectivity. Should be avoided at all cost + UNKNOWN +}; + +inline FunctionalRoadClass functionalRoadClassFromTag(std::string const &value) +{ + const static auto initializeClassHash = []() + { + std::unordered_map hash; + hash["motorway"] = FunctionalRoadClass::MOTORWAY; + hash["motorway_link"] = FunctionalRoadClass::MOTORWAY_LINK; + hash["trunk"] = FunctionalRoadClass::TRUNK; + hash["trunk_link"] = FunctionalRoadClass::TRUNK_LINK; + hash["primary"] = FunctionalRoadClass::PRIMARY; + hash["primary_link"] = FunctionalRoadClass::PRIMARY_LINK; + hash["secondary"] = FunctionalRoadClass::SECONDARY; + hash["secondary_link"] = FunctionalRoadClass::SECONDARY_LINK; + hash["tertiary"] = FunctionalRoadClass::TERTIARY; + hash["tertiary_link"] = FunctionalRoadClass::TERTIARY_LINK; + hash["unclassified"] = FunctionalRoadClass::UNCLASSIFIED; + hash["residential"] = FunctionalRoadClass::RESIDENTIAL; + hash["service"] = FunctionalRoadClass::SERVICE; + hash["living_street"] = FunctionalRoadClass::LIVING_STREET; + hash["track"] = FunctionalRoadClass::LOW_PRIORITY_ROAD; + hash["road"] = FunctionalRoadClass::LOW_PRIORITY_ROAD; + hash["path"] = FunctionalRoadClass::LOW_PRIORITY_ROAD; + hash["driveway"] = FunctionalRoadClass::LOW_PRIORITY_ROAD; + return hash; + }; + + static const std::unordered_map class_hash = + initializeClassHash(); + + if (class_hash.find(value) != class_hash.end()) + { + return class_hash.find(value)->second; + } + else + { + util::SimpleLogger().Write(logDEBUG) << "Unknown road class encountered: " << value; + return FunctionalRoadClass::UNKNOWN; + } +} + +inline bool isRampClass(const FunctionalRoadClass road_class) +{ + // Primary Roads and down are usually too small to announce their links as ramps + return road_class == MOTORWAY_LINK || road_class == TRUNK_LINK; + //|| road_class == PRIMARY_LINK || + // road_class == SECONDARY_LINK || road_class == TERTIARY_LINK; +} + +// TODO augment this with all data required for guidance generation +struct RoadClassificationData +{ + FunctionalRoadClass road_class; + + void augment(const osmium::Way &way); + + // reset to a defined but invalid state + void invalidate(); + + static RoadClassificationData INVALID() + { + RoadClassificationData tmp; + tmp.invalidate(); + return tmp; + }; +}; + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif // OSRM_GUIDANCE_CLASSIFICATION_DATA_HPP_ diff --git a/include/engine/guidance/guidance_toolkit.hpp b/include/engine/guidance/guidance_toolkit.hpp new file mode 100644 index 000000000..7e3284e25 --- /dev/null +++ b/include/engine/guidance/guidance_toolkit.hpp @@ -0,0 +1,416 @@ +#ifndef OSRM_GUIDANCE_GUIDANCE_TOOLKIT_HPP_ +#define OSRM_GUIDANCE_GUIDANCE_TOOLKIT_HPP_ + +#include "util/coordinate.hpp" +#include "util/coordinate_calculation.hpp" + +#include "extractor/compressed_edge_container.hpp" +#include "extractor/query_node.hpp" + +#include "engine/guidance/classification_data.hpp" +#include "engine/guidance/turn_instruction.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +namespace detail +{ +const constexpr double DESIRED_SEGMENT_LENGTH = 10.0; +const constexpr bool shiftable_ccw[] = {false, true, true, false, false, true, true, false}; +const constexpr bool shiftable_cw[] = {false, false, true, true, false, false, true, true}; +// direction modifier bounds in 360./256. degrees +const constexpr uint8_t modifier_bounds[num_direction_modifiers] = {0, 36, 93, 121, + 136, 163, 220, 255}; + +const constexpr double discrete_angle_step_size = 360. / 256.; +} // namespace detail + +namespace detail +{ +template +util::Coordinate +getCoordinateFromCompressedRange(util::Coordinate current_coordinate, + IteratorType compressed_geometry_begin, + const IteratorType compressed_geometry_end, + const util::Coordinate final_coordinate, + const std::vector &query_nodes) +{ + const auto extractCoordinateFromNode = []( + const extractor::QueryNode &node) -> util::Coordinate + { + return {node.lon, node.lat}; + }; + double distance_to_current_coordinate = 0; + double distance_to_next_coordinate = 0; + + // get the length that is missing from the current segment to reach DESIRED_SEGMENT_LENGTH + const auto getFactor = [](const double first_distance, const double second_distance) + { + BOOST_ASSERT(first_distance < detail::DESIRED_SEGMENT_LENGTH); + double segment_length = second_distance - first_distance; + BOOST_ASSERT(segment_length > 0); + BOOST_ASSERT(second_distance >= detail::DESIRED_SEGMENT_LENGTH); + double missing_distance = detail::DESIRED_SEGMENT_LENGTH - first_distance; + return missing_distance / segment_length; + }; + + for (auto compressed_geometry_itr = compressed_geometry_begin; + compressed_geometry_itr != compressed_geometry_end; ++compressed_geometry_itr) + { + const auto next_coordinate = + extractCoordinateFromNode(query_nodes[compressed_geometry_itr->node_id]); + distance_to_next_coordinate = + distance_to_current_coordinate + + util::coordinate_calculation::haversineDistance(current_coordinate, next_coordinate); + + // reached point where coordinates switch between + if (distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH) + return util::coordinate_calculation::interpolateLinear( + getFactor(distance_to_current_coordinate, distance_to_next_coordinate), + current_coordinate, next_coordinate); + + // prepare for next iteration + current_coordinate = next_coordinate; + distance_to_current_coordinate = distance_to_next_coordinate; + } + + distance_to_next_coordinate = + distance_to_current_coordinate + + util::coordinate_calculation::haversineDistance(current_coordinate, final_coordinate); + + // reached point where coordinates switch between + if (distance_to_next_coordinate >= detail::DESIRED_SEGMENT_LENGTH) + return util::coordinate_calculation::interpolateLinear( + getFactor(distance_to_current_coordinate, distance_to_next_coordinate), + current_coordinate, final_coordinate); + else + return final_coordinate; +} +} // namespace detail + +// Finds a (potentially inteprolated) coordinate that is DESIRED_SEGMENT_LENGTH away +// from the start of an edge +inline util::Coordinate +getRepresentativeCoordinate(const NodeID from_node, + const NodeID to_node, + const EdgeID via_edge_id, + const bool traverse_in_reverse, + const extractor::CompressedEdgeContainer &compressed_geometries, + const std::vector &query_nodes) +{ + const auto extractCoordinateFromNode = []( + const extractor::QueryNode &node) -> util::Coordinate + { + return {node.lon, node.lat}; + }; + + // Uncompressed roads are simple, return the coordinate at the end + if (!compressed_geometries.HasEntryForID(via_edge_id)) + { + return extractCoordinateFromNode(traverse_in_reverse ? query_nodes[from_node] + : query_nodes[to_node]); + } + else + { + const auto &geometry = compressed_geometries.GetBucketReference(via_edge_id); + + const auto base_node_id = (traverse_in_reverse) ? to_node : from_node; + const auto base_coordinate = extractCoordinateFromNode(query_nodes[base_node_id]); + + const auto final_node = (traverse_in_reverse) ? from_node : to_node; + const auto final_coordinate = extractCoordinateFromNode(query_nodes[final_node]); + + if (traverse_in_reverse) + return detail::getCoordinateFromCompressedRange( + base_coordinate, geometry.rbegin(), geometry.rend(), final_coordinate, query_nodes); + else + return detail::getCoordinateFromCompressedRange( + base_coordinate, geometry.begin(), geometry.end(), final_coordinate, query_nodes); + } +} + +// shift an instruction around the degree circle in CCW order +inline DirectionModifier forcedShiftCCW(const DirectionModifier modifier) +{ + return static_cast((static_cast(modifier) + 1) % + detail::num_direction_modifiers); +} + +inline DirectionModifier shiftCCW(const DirectionModifier modifier) +{ + if (detail::shiftable_ccw[static_cast(modifier)]) + return forcedShiftCCW(modifier); + else + return modifier; +} + +// shift an instruction around the degree circle in CW order +inline DirectionModifier forcedShiftCW(const DirectionModifier modifier) +{ + return static_cast( + (static_cast(modifier) + detail::num_direction_modifiers - 1) % + detail::num_direction_modifiers); +} + +inline DirectionModifier shiftCW(const DirectionModifier modifier) +{ + if (detail::shiftable_cw[static_cast(modifier)]) + return forcedShiftCW(modifier); + else + return modifier; +} + +inline bool entersRoundabout(const TurnInstruction instruction) +{ + return (instruction.type == TurnType::EnterRoundabout || + instruction.type == TurnType::EnterRotary); +} + +inline bool leavesRoundabout(const TurnInstruction instruction) +{ + return (instruction.type == TurnType::ExitRoundabout || + instruction.type == TurnType::ExitRotary); +} + +inline bool staysOnRoundabout(const TurnInstruction instruction) +{ + return instruction.type == TurnType::StayOnRoundabout; +} + +inline bool isOnRoundabout(const TurnInstruction instruction) +{ + return staysOnRoundabout(instruction) || leavesRoundabout(instruction); +} + +inline bool isTurnNecessary(const TurnInstruction instruction) +{ + return instruction.type != TurnType::NoTurn && instruction.type != TurnType::Suppressed; +} + +inline bool isLeftRight(const DirectionModifier modifier) +{ + return DirectionModifier::Right == modifier || DirectionModifier::Left == modifier; +} + +inline bool isSlightLeftRight(const DirectionModifier modifier) +{ + return DirectionModifier::SlightRight == modifier || DirectionModifier::SlightLeft == modifier; +} + +inline bool isBasic(const TurnType type) +{ + return type == TurnType::Turn || type == TurnType::EndOfRoad; +} + +inline bool isUturn(const TurnInstruction instruction) +{ + return isBasic(instruction.type) && instruction.direction_modifier == DirectionModifier::UTurn; +} + +inline bool resolve(TurnInstruction &to_resolve, const TurnInstruction neighbor, bool resolve_cw) +{ + const auto shifted_turn = resolve_cw ? shiftCW(to_resolve.direction_modifier) + : shiftCCW(to_resolve.direction_modifier); + if (shifted_turn == neighbor.direction_modifier || + shifted_turn == to_resolve.direction_modifier) + return false; + + to_resolve.direction_modifier = shifted_turn; + return true; +} + +inline bool resolveTransitive(TurnInstruction &first, + TurnInstruction &second, + const TurnInstruction third, + bool resolve_cw) +{ + if (resolve(second, third, resolve_cw)) + { + first.direction_modifier = + resolve_cw ? shiftCW(first.direction_modifier) : shiftCCW(first.direction_modifier); + return true; + } + return false; +} + +inline bool isSlightTurn(const TurnInstruction turn) +{ + return (isBasic(turn.type) || turn.type == TurnType::NoTurn) && + (turn.direction_modifier == DirectionModifier::Straight || + turn.direction_modifier == DirectionModifier::SlightRight || + turn.direction_modifier == DirectionModifier::SlightLeft); +} + +inline bool isSlightModifier(const DirectionModifier direction_modifier) +{ + return (direction_modifier == DirectionModifier::Straight || + direction_modifier == DirectionModifier::SlightRight || + direction_modifier == DirectionModifier::SlightLeft); +} + +inline bool isSharpTurn(const TurnInstruction turn) +{ + return isBasic(turn.type) && (turn.direction_modifier == DirectionModifier::SharpLeft || + turn.direction_modifier == DirectionModifier::SharpRight); +} + +inline bool isStraight(const TurnInstruction turn) +{ + return (isBasic(turn.type) || turn.type == TurnType::NoTurn) && + turn.direction_modifier == DirectionModifier::Straight; +} + +inline bool isConflict(const TurnInstruction first, const TurnInstruction second) +{ + return (first.type == second.type && first.direction_modifier == second.direction_modifier) || + (isStraight(first) && isStraight(second)); +} + +inline DirectionModifier discreteAngleToDircetionModifier(const DiscreteAngle angle) +{ + auto modifier = DirectionModifier::UTurn; + DiscreteAngle bound(detail::modifier_bounds[modifier]); + do + { + if (angle <= bound) + return modifier; + modifier = forcedShiftCW(modifier); + bound = static_cast(detail::modifier_bounds[modifier]); + } while (modifier != DirectionModifier::UTurn); + return modifier; +} + +inline DiscreteAngle discretizeAngle(const double angle) +{ + BOOST_ASSERT(angle >= 0. && angle <= 360.); + return DiscreteAngle(static_cast(angle / detail::discrete_angle_step_size)); +} + +inline double angleFromDiscreteAngle(const DiscreteAngle angle) +{ + return static_cast(angle) * detail::discrete_angle_step_size; +} + +inline double angularDeviation(const double angle, const double from) +{ + const double deviation = std::abs(angle - from); + return std::min(360 - deviation, deviation); +} + +inline double getAngularPenalty(const double angle, TurnInstruction instruction) +{ + const double center[] = {0, 45, 90, 135, 180, 225, 270, 315}; + return angularDeviation(center[static_cast(instruction.direction_modifier)], angle); +} + +inline double getTurnConfidence(const double angle, TurnInstruction instruction) +{ + + // special handling of U-Turns and Roundabout + if (!isBasic(instruction.type) || instruction.direction_modifier == DirectionModifier::UTurn) + return 1.0; + + const double deviations[] = {0, 45, 50, 35, 10, 35, 50, 45}; + const double difference = getAngularPenalty(angle, instruction); + const double max_deviation = deviations[static_cast(instruction.direction_modifier)]; + return 1.0 - (difference / max_deviation) * (difference / max_deviation); +} + +// Translates between angles and their human-friendly directional representation +inline DirectionModifier getTurnDirection(const double angle) +{ + // An angle of zero is a u-turn + // 180 goes perfectly straight + // 0-180 are right turns + // 180-360 are left turns + if (angle > 0 && angle < 60) + return DirectionModifier::SharpRight; + if (angle >= 60 && angle < 140) + return DirectionModifier::Right; + if (angle >= 140 && angle < 170) + return DirectionModifier::SlightRight; + if (angle >= 170 && angle <= 190) + return DirectionModifier::Straight; + if (angle > 190 && angle <= 220) + return DirectionModifier::SlightLeft; + if (angle > 220 && angle <= 300) + return DirectionModifier::Left; + if (angle > 300 && angle < 360) + return DirectionModifier::SharpLeft; + return DirectionModifier::UTurn; +} + +inline DirectionModifier angleToDirectionModifier(const double bearing) +{ + if (bearing < 135) + { + return DirectionModifier::Right; + } + + if (bearing <= 225) + { + return DirectionModifier::Straight; + } + return DirectionModifier::Left; +} + +inline DirectionModifier bearingToDirectionModifier(const std::string &bearing) +{ + const static auto buildHash = []() + { + std::map hash; + hash["N"] = DirectionModifier::Straight; + hash["NE"] = DirectionModifier::SlightRight; + hash["E"] = DirectionModifier::Right; + hash["SE"] = DirectionModifier::SharpRight; + hash["S"] = DirectionModifier::UTurn; + hash["SW"] = DirectionModifier::SharpLeft; + hash["W"] = DirectionModifier::Left; + hash["NW"] = DirectionModifier::SlightLeft; + return hash; + }; + + const static std::map hash = buildHash(); + return hash.find(bearing)->second; +} + +inline bool isHighway(FunctionalRoadClass road_class) +{ + return road_class == FunctionalRoadClass::MOTORWAY || road_class == FunctionalRoadClass::TRUNK; +} + +// swaps left <-> right modifier types +inline DirectionModifier mirrorDirectionModifier(const DirectionModifier modifier) +{ + const constexpr DirectionModifier results[] = { + DirectionModifier::UTurn, DirectionModifier::SharpLeft, DirectionModifier::Left, + DirectionModifier::SlightLeft, DirectionModifier::Straight, DirectionModifier::SlightRight, + DirectionModifier::Right, DirectionModifier::SharpRight}; + return results[modifier]; +} + +inline bool canBeSuppressed(const TurnType type) +{ + if (type == TurnType::Turn) + return true; + return false; +} + +inline bool isLowPriorityRoadClass(const FunctionalRoadClass road_class) +{ + return road_class == FunctionalRoadClass::LOW_PRIORITY_ROAD || + road_class == FunctionalRoadClass::SERVICE; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif // OSRM_GUIDANCE_GUIDANCE_TOOLKIT_HPP_ diff --git a/include/extractor/turn_instructions.hpp b/include/engine/guidance/instruction_symbols.hpp similarity index 69% rename from include/extractor/turn_instructions.hpp rename to include/engine/guidance/instruction_symbols.hpp index 7cca8d78e..42ea3fbd5 100644 --- a/include/extractor/turn_instructions.hpp +++ b/include/engine/guidance/instruction_symbols.hpp @@ -1,17 +1,19 @@ -#ifndef TURN_INSTRUCTIONS_HPP -#define TURN_INSTRUCTIONS_HPP +#ifndef OSRM_GUIDANCE_INSTRUCTION_SYMBOLS_HPP +#define OSRM_GUIDANCE_INSTRUCTION_SYMBOLS_HPP -#include -#include +#include "guidance/turn_instruction.hpp" +#include "guidance/guidance_toolkit.hpp" -#include +#include "util/simple_logger.hpp" namespace osrm { -namespace extractor +namespace engine +{ +namespace guidance { -enum class TurnInstruction : unsigned char +enum class InstructionSymbol : unsigned char { NoTurn = 0, GoStraight, @@ -37,6 +39,60 @@ enum class TurnInstruction : unsigned char AccessRestrictionPenalty = 129 }; +inline InstructionSymbol directTranslation(const DirectionModifier direction_modifier) +{ + const constexpr InstructionSymbol translation[] = { + InstructionSymbol::UTurn, InstructionSymbol::TurnSharpRight, + InstructionSymbol::TurnRight, InstructionSymbol::TurnSlightRight, + InstructionSymbol::GoStraight, InstructionSymbol::TurnSlightLeft, + InstructionSymbol::TurnLeft, InstructionSymbol::TurnSharpLeft}; + return translation[direction_modifier]; +} + +inline bool canTranslateDirectly(const TurnType type) +{ + return type == TurnType::Continue // remain on a street + || type == TurnType::NewName // no turn, but name changes + || type == TurnType::Turn // basic turn + || type == TurnType::Ramp // special turn (highway ramp exits) + || type == TurnType::Fork // fork road splitting up + || type == TurnType::EndOfRoad || type == TurnType::Restriction || + type == TurnType::Merge || type == TurnType::Notification; +} + +inline InstructionSymbol getSymbol(const TurnInstruction instruction) +{ + if (canTranslateDirectly(instruction.type)) + { + return directTranslation(instruction.direction_modifier); + } + else if (instruction.type == TurnType::EnterRoundabout || + instruction.type == TurnType::EnterRotary) + { + return InstructionSymbol::EnterRoundAbout; + } + else + { + util::SimpleLogger().Write(logDEBUG) + << "Unreasonable request for symbol: " + << std::to_string(static_cast(instruction.type)) << " " + << std::to_string(static_cast(instruction.direction_modifier)); + return InstructionSymbol::NoTurn; + } +} + +inline InstructionSymbol getLocationSymbol(const LocationType type) +{ + if (type == LocationType::Start) + return InstructionSymbol::HeadOn; + if (type == LocationType::Intermediate) + return InstructionSymbol::ReachViaLocation; + if (type == LocationType::Destination) + return InstructionSymbol::ReachedYourDestination; + return InstructionSymbol::NoTurn; +} + +#if 0 // shiftable turns to left and right const constexpr bool shiftable_left[] = {false, false, true, true, true, false, false, true, true}; const constexpr bool shiftable_right[] = {false, false, true, true, false, false, true, true, true}; @@ -178,5 +234,10 @@ inline bool isConflict(const TurnInstruction first, const TurnInstruction second } } } +#endif -#endif /* TURN_INSTRUCTIONS_HPP */ +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif /* OSRM_GUIDANCE_INSTRUCTION_SYMBOLS_HPP */ diff --git a/include/engine/guidance/post_processing.hpp b/include/engine/guidance/post_processing.hpp new file mode 100644 index 000000000..c66e997f2 --- /dev/null +++ b/include/engine/guidance/post_processing.hpp @@ -0,0 +1,21 @@ +#ifndef ENGINE_GUIDANCE_POST_PROCESSING_HPP +#define ENGINE_GUIDANCE_POST_PROCESSING_HPP + +#include "engine/internal_route_result.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +std::vector> postProcess( std::vector> path_data ); + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif // ENGINE_GUIDANCE_POST_PROCESSING_HPP diff --git a/include/engine/guidance/segment_compression.hpp b/include/engine/guidance/segment_compression.hpp deleted file mode 100644 index b9eea766d..000000000 --- a/include/engine/guidance/segment_compression.hpp +++ /dev/null @@ -1,66 +0,0 @@ -#ifndef ENGINE_GUIDANCE_PROCESSING_SEGMENT_COMPRESSION_HPP_ -#define ENGINE_GUIDANCE_PROCESSING_SEGMENT_COMPRESSION_HPP_ - -#include "engine/segment_inforamtion.hpp" - -#include - -namespace osrm -{ -namespace engine -{ -namespace guidance -{ - -/* - Simplify turn instructions - Input : - 10. Turn left on B 36 for 20 km - 11. Continue on B 35; B 36 for 2 km - 12. Continue on B 36 for 13 km - - Output: - 10. Turn left on B 36 for 35 km -*/ - -inline void CombineSimilarSegments(std::vector &segments) -{ - // TODO: rework to check only end and start of string. - // stl string is way to expensive - // unsigned lastTurn = 0; - // for(unsigned i = 1; i < path_description.size(); ++i) { - // string1 = sEngine.GetEscapedNameForNameID(path_description[i].name_id); - // if(TurnInstruction::GoStraight == path_description[i].turn_instruction) { - // if(std::string::npos != string0.find(string1+";") - // || std::string::npos != string0.find(";"+string1) - // || std::string::npos != string0.find(string1+" ;") - // || std::string::npos != string0.find("; "+string1) - // ){ - // SimpleLogger().Write() << "->next correct: " << string0 << " contains " << - // string1; - // for(; lastTurn != i; ++lastTurn) - // path_description[lastTurn].name_id = path_description[i].name_id; - // path_description[i].turn_instruction = TurnInstruction::NoTurn; - // } else if(std::string::npos != string1.find(string0+";") - // || std::string::npos != string1.find(";"+string0) - // || std::string::npos != string1.find(string0+" ;") - // || std::string::npos != string1.find("; "+string0) - // ){ - // SimpleLogger().Write() << "->prev correct: " << string1 << " contains " << - // string0; - // path_description[i].name_id = path_description[i-1].name_id; - // path_description[i].turn_instruction = TurnInstruction::NoTurn; - // } - // } - // if (TurnInstruction::NoTurn != path_description[i].turn_instruction) { - // lastTurn = i; - // } - // string0 = string1; - // } - // -} -} // namespace guidance -} // namespace engine -} // namespace osrm - -#endif // ENGINE_GUIDANCE_PROCESSING_SEGMENT_COMPRESSION_HPP_ diff --git a/include/engine/guidance/step_maneuver.hpp b/include/engine/guidance/step_maneuver.hpp index f0b9b4135..070b60f7d 100644 --- a/include/engine/guidance/step_maneuver.hpp +++ b/include/engine/guidance/step_maneuver.hpp @@ -2,7 +2,7 @@ #define ENGINE_GUIDANCE_STEP_MANEUVER_HPP #include "util/coordinate.hpp" -#include "extractor/turn_instructions.hpp" +#include "engine/guidance/turn_instruction.hpp" namespace osrm { @@ -16,9 +16,10 @@ struct StepManeuver util::Coordinate location; double bearing_before; double bearing_after; - extractor::TurnInstruction instruction; + TurnInstruction instruction; + unsigned exit; }; -} -} -} +} // namespace guidance +} // namespace engine +} // namespace osrmn #endif diff --git a/include/engine/guidance/turn_classification.hpp b/include/engine/guidance/turn_classification.hpp new file mode 100644 index 000000000..e7cab4238 --- /dev/null +++ b/include/engine/guidance/turn_classification.hpp @@ -0,0 +1,104 @@ +#ifndef OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_ +#define OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_ + +#include "engine/guidance/turn_instruction.hpp" +#include "engine/guidance/guidance_toolkit.hpp" + +#include "util/typedefs.hpp" +#include "util/coordinate.hpp" +#include "util/node_based_graph.hpp" + +#include "extractor/compressed_edge_container.hpp" +#include "extractor/query_node.hpp" + +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +struct TurnPossibility +{ + TurnPossibility(DiscreteAngle angle, EdgeID edge_id) + : angle(std::move(angle)), edge_id(std::move(edge_id)) + { + } + + TurnPossibility() : angle(0), edge_id(SPECIAL_EDGEID) {} + + DiscreteAngle angle; + EdgeID edge_id; +}; + +inline std::vector +classifyIntersection(NodeID nid, + const util::NodeBasedDynamicGraph &graph, + const extractor::CompressedEdgeContainer &compressed_geometries, + const std::vector &query_nodes) +{ + + std::vector turns; + + if (graph.BeginEdges(nid) == graph.EndEdges(nid)) + return std::vector(); + + const EdgeID base_id = graph.BeginEdges(nid); + const auto base_coordinate = getRepresentativeCoordinate(nid, graph.GetTarget(base_id), base_id, + graph.GetEdgeData(base_id).reversed, + compressed_geometries, query_nodes); + const auto node_coordinate = Coordinate(query_nodes[nid].lon, query_nodes[nid].lat); + + // generate a list of all turn angles between a base edge, the node and a current edge + for (const EdgeID eid : graph.GetAdjacentEdgeRange(nid)) + { + const auto edge_coordinate = getRepresentativeCoordinate( + nid, graph.GetTarget(eid), eid, false, compressed_geometries, query_nodes); + + double angle = util::coordinate_calculation::computeAngle(base_coordinate, node_coordinate, + edge_coordinate); + turns.emplace_back(discretizeAngle(angle), eid); + } + + std::sort(turns.begin(), turns.end(), + [](const TurnPossibility left, const TurnPossibility right) + { + return left.angle < right.angle; + }); + + turns.push_back(turns.front()); // sentinel + for (std::size_t turn_nr = 0; turn_nr + 1 < turns.size(); ++turn_nr) + { + turns[turn_nr].angle = (256 + static_cast(turns[turn_nr + 1].angle) - + static_cast(turns[turn_nr].angle)) % + 256; // calculate the difference to the right + } + turns.pop_back(); // remove sentinel again + + // find largest: + std::size_t best_id = 0; + DiscreteAngle largest_turn_angle = turns.front().angle; + for (std::size_t current_turn_id = 1; current_turn_id < turns.size(); ++current_turn_id) + { + if (turns[current_turn_id].angle > largest_turn_angle) + { + largest_turn_angle = turns[current_turn_id].angle; + best_id = current_turn_id; + } + } + + // rotate all angles so the largest angle comes first + std::rotate(turns.begin(), turns.begin() + best_id, turns.end()); + + return turns; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif // OSRM_GUIDANCE_TURN_CLASSIFICATION_HPP_ diff --git a/include/engine/guidance/turn_instruction.hpp b/include/engine/guidance/turn_instruction.hpp new file mode 100644 index 000000000..cd18d4afa --- /dev/null +++ b/include/engine/guidance/turn_instruction.hpp @@ -0,0 +1,154 @@ +#ifndef OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_ +#define OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_ + +#include + +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +namespace detail +{ +// inclusive bounds for turn modifiers +const constexpr uint8_t num_direction_modifiers = 8; +} // detail + +// direction modifiers based on angle +// Would be nice to have +// enum class DirectionModifier : unsigned char +enum DirectionModifier +{ + UTurn, + SharpRight, + Right, + SlightRight, + Straight, + SlightLeft, + Left, + SharpLeft +}; + +const constexpr char *modifier_names[detail::num_direction_modifiers] = { + "uturn", "sharp right", "right", "slight right", + "straight", "slight left", "left", "sharp left"}; + +enum LocationType +{ + Start, + Intermediate, + Destination +}; + +// enum class TurnType : unsigned char +enum TurnType // at the moment we can support 32 turn types, without increasing memory consumption +{ + Invalid, // no valid turn instruction + NoTurn, // end of segment without turn + Location, // start,end,via + Suppressed, // location that suppresses a turn + NewName, // no turn, but name changes + Continue, // remain on a street + Turn, // basic turn + Merge, // merge onto a street + Ramp, // special turn (highway ramp exits) + Fork, // fork road splitting up + EndOfRoad, // T intersection + EnterRoundabout, // Entering a small Roundabout + EnterRoundaboutAtExit, // Entering a small Roundabout at a countable exit + ExitRoundabout, // Exiting a small Roundabout + EnterRotary, // Enter a rotary + EnterRotaryAtExit, // Enter A Rotary at a countable exit + ExitRotary, // Exit a rotary + StayOnRoundabout, // Continue on Either a small or a large Roundabout + Restriction, // Cross a Barrier, requires barrier penalties instead of full block + Notification // Travel Mode Changes` +}; + +const constexpr char *turn_type_names[] = {"invalid", + "no turn", + "waypoint", + "passing intersection", + "new name", + "continue", + "turn", + "merge", + "ramp", + "fork", + "end of road", + "roundabout", + "invalid" + "invalid", + "traffic circle", + "invalid", + "invalid", + "invalid", + "restriction", + "notification"}; + +// turn angle in 1.40625 degree -> 128 == 180 degree +typedef uint8_t DiscreteAngle; + +struct TurnInstruction +{ + TurnInstruction(const TurnType type = TurnType::Invalid, + const DirectionModifier direction_modifier = DirectionModifier::Straight) + : type(type), direction_modifier(direction_modifier) + { + } + + TurnType type : 5; + DirectionModifier direction_modifier : 3; + + static TurnInstruction INVALID() + { + return TurnInstruction(TurnType::Invalid, DirectionModifier::UTurn); + } + + static TurnInstruction NO_TURN() + { + return TurnInstruction(TurnType::NoTurn, DirectionModifier::Straight); + } + + static TurnInstruction REMAIN_ROUNDABOUT(const DirectionModifier modifier) + { + return TurnInstruction(TurnType::StayOnRoundabout, modifier); + } + + static TurnInstruction ENTER_ROUNDABOUT(const DirectionModifier modifier) + { + return TurnInstruction(TurnType::EnterRoundabout, modifier); + } + + static TurnInstruction EXIT_ROUNDABOUT(const DirectionModifier modifier) + { + return TurnInstruction(TurnType::ExitRoundabout, modifier); + } +}; + +inline bool operator!=(const TurnInstruction lhs, const TurnInstruction rhs) +{ + return lhs.type != rhs.type || lhs.direction_modifier != rhs.direction_modifier; +} + +inline bool operator==(const TurnInstruction lhs, const TurnInstruction rhs) +{ + return lhs.type == rhs.type && lhs.direction_modifier == rhs.direction_modifier; +} + +// Silent Turn Instructions are not to be mentioned to the outside world +inline bool isSilent(const TurnInstruction instruction) +{ + return instruction.type == TurnType::NoTurn || instruction.type == TurnType::Suppressed || + instruction.type == TurnType::StayOnRoundabout; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif // OSRM_GUIDANCE_TURN_INSTRUCTION_HPP_ diff --git a/include/engine/internal_route_result.hpp b/include/engine/internal_route_result.hpp index d9620fba4..0faba9594 100644 --- a/include/engine/internal_route_result.hpp +++ b/include/engine/internal_route_result.hpp @@ -3,7 +3,7 @@ #include "engine/phantom_node.hpp" #include "extractor/travel_mode.hpp" -#include "extractor/turn_instructions.hpp" +#include "guidance/turn_instruction.hpp" #include "util/typedefs.hpp" #include "osrm/coordinate.hpp" @@ -15,6 +15,8 @@ namespace osrm namespace engine { +const constexpr unsigned INVALID_EXIT_NR = 0; + struct PathData { // id of via node of the turn @@ -24,9 +26,11 @@ struct PathData // duration that is traveled on the segment until the turn is reached EdgeWeight duration_until_turn; // instruction to execute at the turn - extractor::TurnInstruction turn_instruction; + guidance::TurnInstruction turn_instruction; // travel mode of the street that leads to the turn extractor::TravelMode travel_mode : 4; + // exit ID of highway exit, roundabout exit, intersection nr + unsigned exit; }; struct InternalRouteResult diff --git a/include/engine/polyline_compressor.hpp b/include/engine/polyline_compressor.hpp index a1b7241f2..4ecf94105 100644 --- a/include/engine/polyline_compressor.hpp +++ b/include/engine/polyline_compressor.hpp @@ -12,9 +12,9 @@ namespace engine { namespace detail { -constexpr double POLYLINE_PECISION = 1e5; -constexpr double COORDINATE_TO_POLYLINE = POLYLINE_PECISION / COORDINATE_PRECISION; -constexpr double POLYLINE_TO_COORDINATE = COORDINATE_PRECISION / POLYLINE_PECISION; +constexpr double POLYLINE_PRECISION = 1e5; +constexpr double COORDINATE_TO_POLYLINE = POLYLINE_PRECISION / COORDINATE_PRECISION; +constexpr double POLYLINE_TO_COORDINATE = COORDINATE_PRECISION / POLYLINE_PRECISION; } using CoordVectorForwardIter = std::vector::const_iterator; diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index da2302dcb..0337162ae 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -4,7 +4,7 @@ #include "util/coordinate_calculation.hpp" #include "engine/internal_route_result.hpp" #include "engine/search_engine_data.hpp" -#include "extractor/turn_instructions.hpp" +#include "engine/guidance/turn_instruction.hpp" #include "util/typedefs.hpp" #include @@ -283,7 +283,7 @@ template class BasicRoutingInterface { BOOST_ASSERT_MSG(!ed.shortcut, "original edge flagged as shortcut"); unsigned name_index = facade->GetNameIndexFromEdgeID(ed.id); - const extractor::TurnInstruction turn_instruction = + const guidance::TurnInstruction turn_instruction = facade->GetTurnInstructionForEdgeID(ed.id); const extractor::TravelMode travel_mode = (unpacked_path.empty() && start_traversed_in_reverse) @@ -320,9 +320,9 @@ template class BasicRoutingInterface BOOST_ASSERT(start_index < end_index); for (std::size_t i = start_index; i < end_index; ++i) { - unpacked_path.push_back(PathData{id_vector[i], name_index, weight_vector[i], - extractor::TurnInstruction::NoTurn, - travel_mode}); + unpacked_path.push_back(PathData{id_vector[i], name_index, weight_vector[i], + guidance::TurnInstruction::NO_TURN(), + travel_mode, INVALID_EXIT_NR}); } BOOST_ASSERT(unpacked_path.size() > 0); unpacked_path.back().turn_instruction = turn_instruction; @@ -363,12 +363,12 @@ template class BasicRoutingInterface { BOOST_ASSERT(i < id_vector.size()); BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode > 0); - unpacked_path.emplace_back( - PathData{id_vector[i], phantom_node_pair.target_phantom.name_id, 0, - extractor::TurnInstruction::NoTurn, - target_traversed_in_reverse - ? phantom_node_pair.target_phantom.backward_travel_mode - : phantom_node_pair.target_phantom.forward_travel_mode}); + unpacked_path.emplace_back(PathData{ + id_vector[i], phantom_node_pair.target_phantom.name_id, 0, + guidance::TurnInstruction::NO_TURN(), + target_traversed_in_reverse ? phantom_node_pair.target_phantom.backward_travel_mode + : phantom_node_pair.target_phantom.forward_travel_mode, + INVALID_EXIT_NR}); } // there is no equivalent to a node-based node in an edge-expanded graph. diff --git a/include/extractor/edge_based_graph_factory.hpp b/include/extractor/edge_based_graph_factory.hpp index 6dfbca3ff..edef80d42 100644 --- a/include/extractor/edge_based_graph_factory.hpp +++ b/include/extractor/edge_based_graph_factory.hpp @@ -5,15 +5,17 @@ #include "extractor/edge_based_edge.hpp" #include "extractor/speed_profile.hpp" -#include "util/typedefs.hpp" +#include "extractor/restriction_map.hpp" #include "extractor/compressed_edge_container.hpp" -#include "util/deallocating_vector.hpp" #include "extractor/edge_based_node.hpp" #include "extractor/original_edge_data.hpp" #include "extractor/query_node.hpp" -#include "extractor/turn_instructions.hpp" + +#include "engine/guidance/turn_instruction.hpp" + #include "util/node_based_graph.hpp" -#include "extractor/restriction_map.hpp" +#include "util/typedefs.hpp" +#include "util/deallocating_vector.hpp" #include #include @@ -68,12 +70,12 @@ class EdgeBasedGraphFactory // with known angle. // Handles special cases like u-turns and roundabouts // For basic turns, the turn based on the angle-classification is returned - TurnInstruction AnalyzeTurn(const NodeID u, - const EdgeID e1, - const NodeID v, - const EdgeID e2, - const NodeID w, - const double angle) const; + engine::guidance::TurnInstruction AnalyzeTurn(const NodeID u, + const EdgeID e1, + const NodeID v, + const EdgeID e2, + const NodeID w, + const double angle) const; std::int32_t GetTurnPenalty(double angle, lua_State *lua_state) const; @@ -130,9 +132,9 @@ class EdgeBasedGraphFactory { EdgeID eid; // the id of the arc bool valid; // a turn may be relevant to good instructions, even if we cannot take the road - double angle; // the approximated angle of the turn - TurnInstruction instruction; // a proposed instruction - double confidence; // how close to the border is the turn? + double angle; // the approximated angle of the turn + engine::guidance::TurnInstruction instruction; // a proposed instruction + double confidence; // how close to the border is the turn? std::string toString() const { @@ -143,7 +145,8 @@ class EdgeBasedGraphFactory result += " angle: "; result += std::to_string(angle); result += " instruction: "; - result += std::to_string(static_cast(instruction)); + result += std::to_string(static_cast(instruction.type)) + " " + + std::to_string(static_cast(instruction.direction_modifier)); result += " confidence: "; result += std::to_string(confidence); return result; @@ -153,19 +156,23 @@ class EdgeBasedGraphFactory // Use In Order to generate base turns // cannot be const due to the counters... - std::vector getTurnCandidates(NodeID from, EdgeID via_edge); - std::vector optimizeCandidates(NodeID via_edge, + std::vector getTurnCandidates(const NodeID from, const EdgeID via_edge); + std::vector optimizeCandidates(const EdgeID via_edge, std::vector turn_candidates) const; - std::vector suppressTurns(EdgeID via_edge, + + std::vector optimizeRamps(const EdgeID via_edge, std::vector turn_candidates) const; - QueryNode getRepresentativeCoordinate(const NodeID src, - const NodeID tgt, - const EdgeID via_eid, - bool INVERTED) const; + engine::guidance::TurnType + checkForkAndEnd(const EdgeID via_edge, const std::vector &turn_candidates) const; + std::vector handleForkAndEnd(const engine::guidance::TurnType type, + std::vector turn_candidates) const; - bool isObviousChoice(EdgeID coming_from_eid, - std::size_t turn_index, + std::vector suppressTurns(const EdgeID via_edge, + std::vector turn_candidates) const; + + bool isObviousChoice(const EdgeID coming_from_eid, + const std::size_t turn_index, const std::vector &turn_candidates) const; std::size_t restricted_turns_counter; diff --git a/include/extractor/extraction_way.hpp b/include/extractor/extraction_way.hpp index f82787d96..878c24d44 100644 --- a/include/extractor/extraction_way.hpp +++ b/include/extractor/extraction_way.hpp @@ -3,6 +3,7 @@ #include "extractor/travel_mode.hpp" #include "util/typedefs.hpp" +#include "engine/guidance/classification_data.hpp" #include #include @@ -33,6 +34,7 @@ struct ExtractionWay name.clear(); forward_travel_mode = TRAVEL_MODE_INACCESSIBLE; backward_travel_mode = TRAVEL_MODE_INACCESSIBLE; + road_classification_data.invalidate(); } // These accessors exists because it's not possible to take the address of a bitfield, @@ -51,6 +53,7 @@ struct ExtractionWay bool is_startpoint; TravelMode forward_travel_mode : 4; TravelMode backward_travel_mode : 4; + engine::guidance::RoadClassificationData road_classification_data; }; } } diff --git a/include/extractor/internal_extractor_edge.hpp b/include/extractor/internal_extractor_edge.hpp index 57075f20f..404144755 100644 --- a/include/extractor/internal_extractor_edge.hpp +++ b/include/extractor/internal_extractor_edge.hpp @@ -9,6 +9,7 @@ #include "osrm/coordinate.hpp" #include +#include "engine/guidance/classification_data.hpp" namespace osrm { @@ -50,7 +51,8 @@ struct InternalExtractorEdge false, true, TRAVEL_MODE_INACCESSIBLE, - false) + false, + engine::guidance::RoadClassificationData::INVALID()) { } @@ -64,7 +66,8 @@ struct InternalExtractorEdge bool access_restricted, bool startpoint, TravelMode travel_mode, - bool is_split) + bool is_split, + engine::guidance::RoadClassificationData road_classification) : result(OSMNodeID(source), OSMNodeID(target), name_id, @@ -75,7 +78,8 @@ struct InternalExtractorEdge access_restricted, startpoint, travel_mode, - is_split), + is_split, + std::move(road_classification)), weight_data(std::move(weight_data)) { } @@ -91,12 +95,14 @@ struct InternalExtractorEdge static InternalExtractorEdge min_osm_value() { return InternalExtractorEdge(MIN_OSM_NODEID, MIN_OSM_NODEID, 0, WeightData(), false, false, - false, false, true, TRAVEL_MODE_INACCESSIBLE, false); + false, false, true, TRAVEL_MODE_INACCESSIBLE, false, + engine::guidance::RoadClassificationData::INVALID()); } static InternalExtractorEdge max_osm_value() { return InternalExtractorEdge(MAX_OSM_NODEID, MAX_OSM_NODEID, 0, WeightData(), false, false, - false, false, true, TRAVEL_MODE_INACCESSIBLE, false); + false, false, true, TRAVEL_MODE_INACCESSIBLE, false, + engine::guidance::RoadClassificationData::INVALID()); } static InternalExtractorEdge min_internal_value() diff --git a/include/extractor/node_based_edge.hpp b/include/extractor/node_based_edge.hpp index 9af6ee9c4..4ca658f53 100644 --- a/include/extractor/node_based_edge.hpp +++ b/include/extractor/node_based_edge.hpp @@ -4,6 +4,8 @@ #include "extractor/travel_mode.hpp" #include "util/typedefs.hpp" +#include "engine/guidance/classification_data.hpp" + namespace osrm { namespace extractor @@ -23,7 +25,8 @@ struct NodeBasedEdge bool access_restricted, bool startpoint, TravelMode travel_mode, - bool is_split); + bool is_split, + engine::guidance::RoadClassificationData road_classification); bool operator<(const NodeBasedEdge &other) const; @@ -38,6 +41,7 @@ struct NodeBasedEdge bool startpoint : 1; bool is_split : 1; TravelMode travel_mode : 4; + engine::guidance::RoadClassificationData road_classification; }; struct NodeBasedEdgeWithOSM : NodeBasedEdge @@ -52,7 +56,8 @@ struct NodeBasedEdgeWithOSM : NodeBasedEdge bool access_restricted, bool startpoint, TravelMode travel_mode, - bool is_split); + bool is_split, + engine::guidance::RoadClassificationData road_classification); OSMNodeID osm_source_id; OSMNodeID osm_target_id; @@ -65,6 +70,7 @@ inline NodeBasedEdge::NodeBasedEdge() backward(false), roundabout(false), access_restricted(false), startpoint(true), is_split(false), travel_mode(false) { + road_classification.invalidate(); } inline NodeBasedEdge::NodeBasedEdge(NodeID source, @@ -77,10 +83,12 @@ inline NodeBasedEdge::NodeBasedEdge(NodeID source, bool access_restricted, bool startpoint, TravelMode travel_mode, - bool is_split) + bool is_split, + engine::guidance::RoadClassificationData road_classification) : source(source), target(target), name_id(name_id), weight(weight), forward(forward), backward(backward), roundabout(roundabout), access_restricted(access_restricted), - startpoint(startpoint), is_split(is_split), travel_mode(travel_mode) + startpoint(startpoint), is_split(is_split), travel_mode(travel_mode), + road_classification(std::move(road_classification)) { } @@ -101,17 +109,19 @@ inline bool NodeBasedEdge::operator<(const NodeBasedEdge &other) const return source < other.source; } -inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source, - OSMNodeID target, - NodeID name_id, - EdgeWeight weight, - bool forward, - bool backward, - bool roundabout, - bool access_restricted, - bool startpoint, - TravelMode travel_mode, - bool is_split) +inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM( + OSMNodeID source, + OSMNodeID target, + NodeID name_id, + EdgeWeight weight, + bool forward, + bool backward, + bool roundabout, + bool access_restricted, + bool startpoint, + TravelMode travel_mode, + bool is_split, + engine::guidance::RoadClassificationData road_classification) : NodeBasedEdge(SPECIAL_NODEID, SPECIAL_NODEID, name_id, @@ -122,7 +132,8 @@ inline NodeBasedEdgeWithOSM::NodeBasedEdgeWithOSM(OSMNodeID source, access_restricted, startpoint, travel_mode, - is_split), + is_split, + std::move(road_classification)), osm_source_id(std::move(source)), osm_target_id(std::move(target)) { } diff --git a/include/extractor/original_edge_data.hpp b/include/extractor/original_edge_data.hpp index 4ebcc04e8..c4e2b37ea 100644 --- a/include/extractor/original_edge_data.hpp +++ b/include/extractor/original_edge_data.hpp @@ -2,7 +2,7 @@ #define ORIGINAL_EDGE_DATA_HPP #include "extractor/travel_mode.hpp" -#include "extractor/turn_instructions.hpp" +#include "engine/guidance/turn_instruction.hpp" #include "util/typedefs.hpp" #include @@ -16,7 +16,7 @@ struct OriginalEdgeData { explicit OriginalEdgeData(NodeID via_node, unsigned name_id, - TurnInstruction turn_instruction, + engine::guidance::TurnInstruction turn_instruction, TravelMode travel_mode) : via_node(via_node), name_id(name_id), turn_instruction(turn_instruction), travel_mode(travel_mode) @@ -25,14 +25,15 @@ struct OriginalEdgeData OriginalEdgeData() : via_node(std::numeric_limits::max()), - name_id(std::numeric_limits::max()), turn_instruction(TurnInstruction::NoTurn), + name_id(std::numeric_limits::max()), + turn_instruction(engine::guidance::TurnInstruction::INVALID()), travel_mode(TRAVEL_MODE_INACCESSIBLE) { } NodeID via_node; unsigned name_id; - TurnInstruction turn_instruction; + engine::guidance::TurnInstruction turn_instruction; TravelMode travel_mode; }; } diff --git a/include/util/coordinate_calculation.hpp b/include/util/coordinate_calculation.hpp index 917d3eef6..124805a6e 100644 --- a/include/util/coordinate_calculation.hpp +++ b/include/util/coordinate_calculation.hpp @@ -3,9 +3,11 @@ #include "util/coordinate.hpp" -#include +#include #include +#include + namespace osrm { namespace util @@ -59,6 +61,8 @@ double bearing(const Coordinate first_coordinate, const Coordinate second_coordi // Get angle of line segment (A,C)->(C,B) double computeAngle(const Coordinate first, const Coordinate second, const Coordinate third); +Coordinate interpolateLinear( double factor, const Coordinate from, const Coordinate to ); + namespace mercator { FloatLatitude yToLat(const double value); diff --git a/include/util/node_based_graph.hpp b/include/util/node_based_graph.hpp index fcfad9e4d..e82b9daa6 100644 --- a/include/util/node_based_graph.hpp +++ b/include/util/node_based_graph.hpp @@ -4,6 +4,7 @@ #include "util/dynamic_graph.hpp" #include "extractor/node_based_edge.hpp" #include "util/graph_utils.hpp" +#include "engine/guidance/classification_data.hpp" #include @@ -45,6 +46,7 @@ struct NodeBasedEdgeData bool roundabout : 1; bool startpoint : 1; extractor::TravelMode travel_mode : 4; + engine::guidance::RoadClassificationData road_classification; bool IsCompatibleTo(const NodeBasedEdgeData &other) const { @@ -74,6 +76,7 @@ NodeBasedDynamicGraphFromEdges(std::size_t number_of_nodes, output_edge.data.access_restricted = input_edge.access_restricted; output_edge.data.travel_mode = input_edge.travel_mode; output_edge.data.startpoint = input_edge.startpoint; + output_edge.data.road_classification = input_edge.road_classification; }); tbb::parallel_sort(edges_list.begin(), edges_list.end()); diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp index 8df0f9237..7a259d055 100644 --- a/src/contractor/contractor.cpp +++ b/src/contractor/contractor.cpp @@ -52,7 +52,7 @@ int Contractor::Run() #ifdef WIN32 #pragma message("Memory consumption on Windows can be higher due to different bit packing") #else - static_assert(sizeof(extractor::NodeBasedEdge) == 20, + static_assert(sizeof(extractor::NodeBasedEdge) == 24, "changing extractor::NodeBasedEdge type has influence on memory consumption!"); static_assert(sizeof(extractor::EdgeBasedEdge) == 16, "changing EdgeBasedEdge type has influence on memory consumption!"); diff --git a/src/engine/api/json_factory.cpp b/src/engine/api/json_factory.cpp index fcf327045..f052c3778 100644 --- a/src/engine/api/json_factory.cpp +++ b/src/engine/api/json_factory.cpp @@ -24,74 +24,14 @@ namespace json namespace detail { -std::string instructionToString(extractor::TurnInstruction instruction) +std::string instructionTypeToString(guidance::TurnType type) { - std::string token; - // FIXME this could be an array. - switch (instruction) - { - case extractor::TurnInstruction::GoStraight: - token = "continue"; - break; - case extractor::TurnInstruction::TurnSlightRight: - token = "bear right"; - break; - case extractor::TurnInstruction::TurnRight: - token = "right"; - break; - case extractor::TurnInstruction::TurnSharpRight: - token = "sharp right"; - break; - case extractor::TurnInstruction::UTurn: - token = "uturn"; - break; - case extractor::TurnInstruction::TurnSharpLeft: - token = "sharp left"; - break; - case extractor::TurnInstruction::TurnLeft: - token = "left"; - break; - case extractor::TurnInstruction::TurnSlightLeft: - token = "bear left"; - break; - case extractor::TurnInstruction::HeadOn: - token = "head on"; - break; - case extractor::TurnInstruction::EnterRoundAbout: - token = "enter roundabout"; - break; - case extractor::TurnInstruction::LeaveRoundAbout: - token = "leave roundabout"; - break; - case extractor::TurnInstruction::StayOnRoundAbout: - token = "stay on roundabout"; - break; - case extractor::TurnInstruction::StartAtEndOfStreet: - token = "depart"; - break; - case extractor::TurnInstruction::ReachedYourDestination: - token = "arrive"; - break; - case extractor::TurnInstruction::NameChanges: - token = "name changed"; - break; + return guidance::turn_type_names[static_cast(type)]; +} - case extractor::TurnInstruction::NoTurn: - case extractor::TurnInstruction::ReachViaLocation: - case extractor::TurnInstruction::EnterAgainstAllowedDirection: - case extractor::TurnInstruction::LeaveAgainstAllowedDirection: - case extractor::TurnInstruction::InverseAccessRestrictionFlag: - case extractor::TurnInstruction::AccessRestrictionFlag: - case extractor::TurnInstruction::AccessRestrictionPenalty: - BOOST_ASSERT_MSG(false, "Invalid turn type used"); - break; - - default: - BOOST_ASSERT_MSG(false, "unknown TurnInstruction"); - break; - } - - return token; +std::string instructionModifierToString(guidance::DirectionModifier modifier) +{ + return guidance::modifier_names[static_cast(modifier)]; } util::json::Array coordinateToLonLat(const util::Coordinate coordinate) @@ -159,10 +99,14 @@ std::string modeToString(const extractor::TravelMode mode) util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver) { util::json::Object step_maneuver; - step_maneuver.values["type"] = detail::instructionToString(maneuver.instruction); + step_maneuver.values["type"] = detail::instructionTypeToString(maneuver.instruction.type); + step_maneuver.values["modifier"] = + detail::instructionModifierToString(maneuver.instruction.direction_modifier); step_maneuver.values["location"] = detail::coordinateToLonLat(maneuver.location); step_maneuver.values["bearing_before"] = maneuver.bearing_before; step_maneuver.values["bearing_after"] = maneuver.bearing_after; + if( maneuver.exit != 0 ) + step_maneuver.values["exit"] = maneuver.exit; return step_maneuver; } diff --git a/src/engine/guidance/classification_data.cpp b/src/engine/guidance/classification_data.cpp new file mode 100644 index 000000000..99ffa0aef --- /dev/null +++ b/src/engine/guidance/classification_data.cpp @@ -0,0 +1,21 @@ +#include "engine/guidance/classification_data.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ +void RoadClassificationData::invalidate() { road_class = FunctionalRoadClass::UNKNOWN; } + +void RoadClassificationData::augment(const osmium::Way &way) +{ + const char *data = way.get_value_by_key("highway"); + if (data) + road_class = functionalRoadClassFromTag(data); +} +} // namespace guidance +} // namespace engine +} // namespace osrm diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp new file mode 100644 index 000000000..875308ae3 --- /dev/null +++ b/src/engine/guidance/post_processing.cpp @@ -0,0 +1,150 @@ +#include "engine/guidance/post_processing.hpp" +#include "engine/guidance/turn_instruction.hpp" +#include "engine/guidance/guidance_toolkit.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +namespace detail +{ +bool canMergeTrivially(const PathData &destination, const PathData &source) +{ + return destination.exit == 0 && destination.name_id == source.name_id && + destination.travel_mode == source.travel_mode && isSilent(destination.turn_instruction); +} + +PathData forwardInto(PathData destination, const PathData &source) +{ + // Merge a turn into a silent turn + // Overwrites turn instruction and increases exit NR + destination.duration_until_turn += source.duration_until_turn; + destination.exit = source.exit; + return destination; +} + +PathData accumulateInto(PathData destination, const PathData &source) +{ + // Merge a turn into a silent turn + // Overwrites turn instruction and increases exit NR + BOOST_ASSERT(canMergeTrivially(destination, source)); + destination.duration_until_turn += source.duration_until_turn; + destination.exit = source.exit + 1; + return destination; +} + +PathData mergeInto(PathData destination, const PathData &source) +{ + if (source.turn_instruction == TurnInstruction::NO_TURN()) + { + BOOST_ASSERT(canMergeTrivially(destination, source)); + return detail::forwardInto(destination, source); + } + if (source.turn_instruction == TurnType::Suppressed && + detail::canMergeTrivially(destination, source)) + { + return detail::accumulateInto(destination, source); + } + if (source.turn_instruction.type == TurnType::StayOnRoundabout) + { + return detail::accumulateInto(destination, source); + } + return destination; +} + +} // namespace detail + +void print( const std::vector> & leg_data ) +{ + std::cout << "Path\n"; + int legnr = 0; + for( const auto & leg : leg_data ) + { + std::cout << "\tLeg: " << ++legnr << "\n"; + int segment = 0; + for( const auto &data : leg ){ + std::cout << "\t\t[" << ++segment << "]: " << (int) data.turn_instruction.type << " " << (int)data.turn_instruction.direction_modifier << " exit: " << data.exit << "\n"; + } + } + std::cout << std::endl; +} + +std::vector> postProcess(std::vector> leg_data) +{ + std::cout << "[POSTPROCESSING ITERATION]" << std::endl; + unsigned carry_exit = 0; + // Count Street Exits forward + print( leg_data ); + for (auto &path_data : leg_data) + { + path_data[0].exit = carry_exit; + for (std::size_t data_index = 0; data_index + 1 < path_data.size(); ++data_index) + { + if (path_data[data_index].turn_instruction.type == TurnType::EnterRoundaboutAtExit) + { + path_data[data_index].exit += 1; // Count the exit + path_data[data_index].turn_instruction.type = TurnType::EnterRoundabout; + } + else if (path_data[data_index].turn_instruction.type == TurnType::EnterRotaryAtExit) + { + path_data[data_index].exit += 1; + path_data[data_index].turn_instruction.type = TurnType::EnterRotary; + } + + if (isSilent(path_data[data_index].turn_instruction)) + { + path_data[data_index + 1] = + detail::mergeInto(path_data[data_index + 1], path_data[data_index]); + } + carry_exit = path_data[data_index].exit; + } + } + + print( leg_data ); + // Move Roundabout exit numbers to front + for (auto rev_itr = leg_data.rbegin(); rev_itr != leg_data.rend(); ++rev_itr) + { + auto &path_data = *rev_itr; + for (std::size_t data_index = path_data.size(); data_index > 1; --data_index) + { + if (leavesRoundabout(path_data[data_index - 1].turn_instruction) || + staysOnRoundabout(path_data[data_index - 1].turn_instruction)) + { + path_data[data_index - 2].exit = path_data[data_index - 1].exit; + } + } + auto prev_leg = std::next(rev_itr); + if (!path_data.empty() && prev_leg != leg_data.rend()) + { + if (staysOnRoundabout(path_data[0].turn_instruction) || + leavesRoundabout(path_data[0].turn_instruction)) + { + prev_leg->back().exit = path_data[0].exit; + } + } + } + + print( leg_data ); + // silence turns for good + for (auto &path_data : leg_data) + { + for (auto &data : path_data) + { + if (isSilent(data.turn_instruction) || leavesRoundabout(data.turn_instruction)) + data.turn_instruction = TurnInstruction::NO_TURN(); + } + } + + print( leg_data ); + return std::move(leg_data); +} + +} // namespace guidance +} // namespace engine +} // namespace osrm diff --git a/src/engine/polyline_compressor.cpp b/src/engine/polyline_compressor.cpp index 31907cb5b..728a26cb3 100644 --- a/src/engine/polyline_compressor.cpp +++ b/src/engine/polyline_compressor.cpp @@ -81,6 +81,7 @@ std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter }); return encode(delta_numbers); } + std::vector decodePolyline(const std::string &geometry_string) { std::vector new_coordinates; diff --git a/src/extractor/edge_based_graph_factory.cpp b/src/extractor/edge_based_graph_factory.cpp index 002826411..cee615e3e 100644 --- a/src/extractor/edge_based_graph_factory.cpp +++ b/src/extractor/edge_based_graph_factory.cpp @@ -9,6 +9,9 @@ #include "util/timing_util.hpp" #include "util/exception.hpp" +#include "engine/guidance/turn_classification.hpp" +#include "engine/guidance/guidance_toolkit.hpp" + #include #include @@ -35,14 +38,31 @@ const double constexpr STRAIGHT_ANGLE = 180.; // if a turn deviates this much from going straight, it will be kept straight const double constexpr MAXIMAL_ALLOWED_NO_TURN_DEVIATION = 2.; // angle that lies between two nearly indistinguishable roads -const double constexpr NARROW_TURN_ANGLE = 25.; +const double constexpr NARROW_TURN_ANGLE = 35.; // angle difference that can be classified as straight, if its the only narrow turn const double constexpr FUZZY_STRAIGHT_ANGLE = 15.; const double constexpr DISTINCTION_RATIO = 2; +using engine::guidance::TurnPossibility; +using engine::guidance::TurnInstruction; +using engine::guidance::DirectionModifier; +using engine::guidance::TurnType; +using engine::guidance::FunctionalRoadClass; + +using engine::guidance::classifyIntersection; +using engine::guidance::isLowPriorityRoadClass; +using engine::guidance::angularDeviation; +using engine::guidance::getTurnDirection; +using engine::guidance::getRepresentativeCoordinate; +using engine::guidance::isBasic; +using engine::guidance::isRampClass; +using engine::guidance::isUturn; +using engine::guidance::isConflict; +using engine::guidance::isSlightTurn; +using engine::guidance::isSlightModifier; +using engine::guidance::mirrorDirectionModifier; + // Configuration to find representative candidate for turn angle calculations -const double constexpr MINIMAL_SEGMENT_LENGTH = 1.; -const double constexpr DESIRED_SEGMENT_LENGTH = 10.; EdgeBasedGraphFactory::EdgeBasedGraphFactory( std::shared_ptr node_based_graph, @@ -140,9 +160,10 @@ void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeI // traverse arrays from start and end respectively for (const auto i : util::irange(std::size_t{ 0 }, geometry_size)) { - BOOST_ASSERT(current_edge_source_coordinate_id == - m_compressed_edge_container.GetBucketReference( - edge_id_2)[geometry_size - 1 - i].node_id); + BOOST_ASSERT( + current_edge_source_coordinate_id == + m_compressed_edge_container.GetBucketReference(edge_id_2)[geometry_size - 1 - i] + .node_id); const NodeID current_edge_target_coordinate_id = forward_geometry[i].node_id; BOOST_ASSERT(current_edge_target_coordinate_id != current_edge_source_coordinate_id); @@ -321,8 +342,55 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( // linear number of turns only. util::Percent progress(m_node_based_graph->GetNumberOfNodes()); + struct CompareTurnPossibilities + { + bool operator()(const std::vector &left, + const std::vector &right) const + { + if (left.size() < right.size()) + return true; + if (left.size() > right.size()) + return false; + for (std::size_t i = 0; i < left.size(); ++i) + { + if ((((int)left[i].angle + 16) % 256) / 32 < + (((int)right[i].angle + 16) % 256) / 32) + return true; + if ((((int)left[i].angle + 16) % 256) / 32 > + (((int)right[i].angle + 16) % 256) / 32) + return false; + } + return false; + } + }; + +// temporary switch to allow display of turn types +#define SHOW_TURN_TYPES 0 +#if SHOW_TURN_TYPES + std::map, std::vector, + CompareTurnPossibilities> turn_types; +#endif + for (const auto node_u : util::irange(0u, m_node_based_graph->GetNumberOfNodes())) { +#if SHOW_TURN_TYPES + auto turn_possibilities = classifyIntersection( + node_u, *m_node_based_graph, m_compressed_edge_container, m_node_info_list); + if (turn_possibilities.empty()) + continue; + auto set = turn_types.find(turn_possibilities); + if (set != turn_types.end()) + { + if (set->second.size() < 5) + set->second.emplace_back(m_node_info_list[node_u].lat, + m_node_info_list[node_u].lon); + } + else + { + turn_types[turn_possibilities] = std::vector( + 1, {m_node_info_list[node_u].lat, m_node_info_list[node_u].lon}); + } +#endif // progress.printStatus(node_u); for (const EdgeID edge_form_u : m_node_based_graph->GetAdjacentEdgeRange(node_u)) { @@ -333,8 +401,33 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( ++node_based_edge_counter; auto turn_candidates = getTurnCandidates(node_u, edge_form_u); +#define PRINT_DEBUG_CANDIDATES 0 +#if PRINT_DEBUG_CANDIDATES + std::cout << "Initial Candidates:\n"; + for (auto tc : turn_candidates) + std::cout << "\t" << tc.toString() << " " + << (int)m_node_based_graph->GetEdgeData(tc.eid) + .road_classification.road_class + << std::endl; +#endif turn_candidates = optimizeCandidates(edge_form_u, turn_candidates); +#if PRINT_DEBUG_CANDIDATES + std::cout << "Optimized Candidates:\n"; + for (auto tc : turn_candidates) + std::cout << "\t" << tc.toString() << " " + << (int)m_node_based_graph->GetEdgeData(tc.eid) + .road_classification.road_class + << std::endl; +#endif turn_candidates = suppressTurns(edge_form_u, turn_candidates); +#if PRINT_DEBUG_CANDIDATES + std::cout << "Suppressed Candidates:\n"; + for (auto tc : turn_candidates) + std::cout << "\t" << tc.toString() << " " + << (int)m_node_based_graph->GetEdgeData(tc.eid) + .road_classification.road_class + << std::endl; +#endif const NodeID node_v = m_node_based_graph->GetTarget(edge_form_u); @@ -361,9 +454,9 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( } const int turn_penalty = GetTurnPenalty(turn_angle, lua_state); - const TurnInstruction turn_instruction = turn.instruction; + const auto turn_instruction = turn.instruction; - if (turn_instruction == TurnInstruction::UTurn) + if (isUturn(turn_instruction)) { distance += speed_profile.u_turn_penalty; } @@ -438,6 +531,21 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( } } } +#if SHOW_TURN_TYPES + std::cout << "[info] found " << turn_types.size() << " turn types." << std::endl; + for (const auto &tt : turn_types) + { + std::cout << tt.second.size(); + for (auto coord : tt.second) + std::cout << " " << coord.lat << " " << coord.lon; + + std::cout << " " << tt.first.size(); + for (auto tte : tt.first) + std::cout << " " << (int)tte.angle; + + std::cout << std::endl; + } +#endif FlushVectorToStream(edge_data_file, original_edge_data_vector); @@ -463,9 +571,107 @@ void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges( << " turns over barriers"; } +std::vector +EdgeBasedGraphFactory::optimizeRamps(const EdgeID via_edge, + std::vector turn_candidates) const +{ + EdgeID continue_eid = SPECIAL_EDGEID; + double continue_angle = 0; + const auto &in_edge_data = m_node_based_graph->GetEdgeData(via_edge); + for (auto &candidate : turn_candidates) + { + if (candidate.instruction.direction_modifier == DirectionModifier::UTurn) + continue; + + const auto &out_edge_data = m_node_based_graph->GetEdgeData(candidate.eid); + if (out_edge_data.name_id == in_edge_data.name_id) + { + continue_eid = candidate.eid; + continue_angle = candidate.angle; + if (angularDeviation(candidate.angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && + isRampClass(in_edge_data.road_classification.road_class)) + candidate.instruction = TurnType::Suppressed; + break; + } + } + + if (continue_eid != SPECIAL_EDGEID) + { + bool to_the_right = true; + for (auto &candidate : turn_candidates) + { + if (candidate.eid == continue_eid) + { + to_the_right = false; + continue; + } + + if (candidate.instruction.type != TurnType::Ramp) + continue; + + if (isSlightModifier(candidate.instruction.direction_modifier)) + candidate.instruction.direction_modifier = + (to_the_right) ? DirectionModifier::SlightRight : DirectionModifier::SlightLeft; + } + } + return turn_candidates; +} + +TurnType +EdgeBasedGraphFactory::checkForkAndEnd(const EdgeID via_eid, + const std::vector &turn_candidates) const +{ + if (turn_candidates.size() != 3 || + turn_candidates.front().instruction.direction_modifier != DirectionModifier::UTurn) + return TurnType::Invalid; + + if (isOnRoundabout(turn_candidates[1].instruction)) + { + BOOST_ASSERT(isOnRoundabout(turn_candidates[2].instruction)); + return TurnType::Invalid; + } + BOOST_ASSERT(!isOnRoundabout(turn_candidates[2].instruction)); + + FunctionalRoadClass road_classes[3] = { + m_node_based_graph->GetEdgeData(via_eid).road_classification.road_class, + m_node_based_graph->GetEdgeData(turn_candidates[1].eid).road_classification.road_class, + m_node_based_graph->GetEdgeData(turn_candidates[2].eid).road_classification.road_class}; + + if (angularDeviation(turn_candidates[1].angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE && + angularDeviation(turn_candidates[2].angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) + { + if (road_classes[0] != road_classes[1] || road_classes[1] != road_classes[2]) + return TurnType::Invalid; + + if (turn_candidates[1].valid && turn_candidates[2].valid) + return TurnType::Fork; + } + + else if (angularDeviation(turn_candidates[1].angle, 90) < NARROW_TURN_ANGLE && + angularDeviation(turn_candidates[2].angle, 270) < NARROW_TURN_ANGLE) + { + return TurnType::EndOfRoad; + } + + return TurnType::Invalid; +} + +std::vector +EdgeBasedGraphFactory::handleForkAndEnd(const TurnType type, + std::vector turn_candidates) const +{ + turn_candidates[1].instruction.type = type; + turn_candidates[1].instruction.direction_modifier = + (type == TurnType::Fork) ? DirectionModifier::SlightRight : DirectionModifier::Right; + turn_candidates[2].instruction.type = type; + turn_candidates[2].instruction.direction_modifier = + (type == TurnType::Fork) ? DirectionModifier::SlightLeft : DirectionModifier::Left; + return turn_candidates; +} + // requires sorted candidates std::vector -EdgeBasedGraphFactory::optimizeCandidates(NodeID via_eid, +EdgeBasedGraphFactory::optimizeCandidates(const EdgeID via_eid, std::vector turn_candidates) const { BOOST_ASSERT_MSG(std::is_sorted(turn_candidates.begin(), turn_candidates.end(), @@ -477,6 +683,12 @@ EdgeBasedGraphFactory::optimizeCandidates(NodeID via_eid, if (turn_candidates.size() <= 1) return turn_candidates; + TurnType type = checkForkAndEnd(via_eid, turn_candidates); + if (type != TurnType::Invalid) + return handleForkAndEnd(type, std::move(turn_candidates)); + + turn_candidates = optimizeRamps(via_eid, std::move(turn_candidates)); + const auto getLeft = [&turn_candidates](std::size_t index) { return (index + 1) % turn_candidates.size(); @@ -487,12 +699,14 @@ EdgeBasedGraphFactory::optimizeCandidates(NodeID via_eid, }; // handle availability of multiple u-turns (e.g. street with separated small parking roads) - if (turn_candidates[0].instruction == TurnInstruction::UTurn && turn_candidates[0].angle == 0) + if (isUturn(turn_candidates[0].instruction) && turn_candidates[0].angle == 0) { - if (turn_candidates[getLeft(0)].instruction == TurnInstruction::UTurn) - turn_candidates[getLeft(0)].instruction = TurnInstruction::TurnSharpLeft; - if (turn_candidates[getRight(0)].instruction == TurnInstruction::UTurn) - turn_candidates[getRight(0)].instruction = TurnInstruction::TurnSharpRight; + if (isUturn(turn_candidates[getLeft(0)].instruction)) + turn_candidates[getLeft(0)].instruction.direction_modifier = + DirectionModifier::SharpLeft; + if (isUturn(turn_candidates[getRight(0)].instruction)) + turn_candidates[getRight(0)].instruction.direction_modifier = + DirectionModifier::SharpRight; } const auto keepStraight = [](double angle) @@ -503,8 +717,8 @@ EdgeBasedGraphFactory::optimizeCandidates(NodeID via_eid, for (std::size_t turn_index = 0; turn_index < turn_candidates.size(); ++turn_index) { auto &turn = turn_candidates[turn_index]; - if (turn.instruction > TurnInstruction::TurnSlightLeft || - turn.instruction == TurnInstruction::UTurn) + if (!isBasic(turn.instruction.type) || isUturn(turn.instruction) || + isOnRoundabout(turn.instruction)) continue; auto &left = turn_candidates[getLeft(turn_index)]; if (turn.angle == left.angle) @@ -542,10 +756,12 @@ EdgeBasedGraphFactory::optimizeCandidates(NodeID via_eid, auto &candidate_at_begin = turn_candidates[conflict_begin]; if (conflict_size == 2) { - if (turn.instruction == TurnInstruction::GoStraight) + if (turn.instruction.direction_modifier == DirectionModifier::Straight) { - if (instruction_left_of_end != TurnInstruction::TurnSlightLeft && - instruction_right_of_begin != TurnInstruction::TurnSlightRight) + if (instruction_left_of_end.direction_modifier != + DirectionModifier::SlightLeft && + instruction_right_of_begin.direction_modifier != + DirectionModifier::SlightRight) { std::int32_t resolved_count = 0; // uses side-effects in resolve @@ -592,8 +808,8 @@ EdgeBasedGraphFactory::optimizeCandidates(NodeID via_eid, if (isSlightTurn(turn.instruction) || isSharpTurn(turn.instruction)) { auto resolve_direction = - (turn.instruction == TurnInstruction::TurnSlightRight || - turn.instruction == TurnInstruction::TurnSharpLeft) + (turn.instruction.direction_modifier == DirectionModifier::SlightRight || + turn.instruction.direction_modifier == DirectionModifier::SharpLeft) ? RESOLVE_TO_RIGHT : RESOLVE_TO_LEFT; if (resolve_direction == RESOLVE_TO_RIGHT && @@ -658,8 +874,8 @@ EdgeBasedGraphFactory::optimizeCandidates(NodeID via_eid, return turn_candidates; } -bool EdgeBasedGraphFactory::isObviousChoice(EdgeID via_eid, - std::size_t turn_index, +bool EdgeBasedGraphFactory::isObviousChoice(const EdgeID via_eid, + const std::size_t turn_index, const std::vector &turn_candidates) const { const auto getLeft = [&turn_candidates](std::size_t index) @@ -677,8 +893,8 @@ bool EdgeBasedGraphFactory::isObviousChoice(EdgeID via_eid, const auto &candidate_to_the_right = turn_candidates[getRight(turn_index)]; - const auto hasValidRatio = - [](const TurnCandidate &left, const TurnCandidate ¢er, const TurnCandidate &right) + const auto hasValidRatio = [this](const TurnCandidate &left, const TurnCandidate ¢er, + const TurnCandidate &right) { auto angle_left = (left.angle > 180) ? angularDeviation(left.angle, STRAIGHT_ANGLE) : 180; auto angle_right = @@ -690,11 +906,31 @@ bool EdgeBasedGraphFactory::isObviousChoice(EdgeID via_eid, : (angle_left > self_angle && angle_right / self_angle > DISTINCTION_RATIO)); }; // only valid turn + if (!isLowPriorityRoadClass( + m_node_based_graph->GetEdgeData(candidate.eid).road_classification.road_class)) + { + bool is_only_normal_road = true; + BOOST_ASSERT(turn_candidates[0].instruction.type == TurnType::Turn && + turn_candidates[0].instruction.direction_modifier == DirectionModifier::UTurn); + for (size_t i = 0; i < turn_candidates.size(); ++i) + { + if (i == turn_index || turn_candidates[i].angle == 0) // skip self and u-turn + continue; + if (!isLowPriorityRoadClass(m_node_based_graph->GetEdgeData(turn_candidates[i].eid) + .road_classification.road_class)) + { + is_only_normal_road = false; + break; + } + } + if (is_only_normal_road == true) + return true; + } return turn_candidates.size() == 1 || // only non u-turn (turn_candidates.size() == 2 && - candidate_to_the_left.instruction == TurnInstruction::UTurn) || // nearly straight turn + isUturn(candidate_to_the_left.instruction)) || // nearly straight turn angularDeviation(candidate.angle, STRAIGHT_ANGLE) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION || hasValidRatio(candidate_to_the_left, candidate, candidate_to_the_right) || (in_data.name_id != 0 && in_data.name_id == out_data.name_id && @@ -702,22 +938,58 @@ bool EdgeBasedGraphFactory::isObviousChoice(EdgeID via_eid, } std::vector -EdgeBasedGraphFactory::suppressTurns(EdgeID via_eid, +EdgeBasedGraphFactory::suppressTurns(const EdgeID via_eid, std::vector turn_candidates) const { - // remove invalid candidates + if (turn_candidates.size() == 3) + { + BOOST_ASSERT(turn_candidates[0].instruction.direction_modifier == DirectionModifier::UTurn); + if (isLowPriorityRoadClass(m_node_based_graph->GetEdgeData(turn_candidates[1].eid) + .road_classification.road_class) && + !isLowPriorityRoadClass(m_node_based_graph->GetEdgeData(turn_candidates[2].eid) + .road_classification.road_class)) + { + if (angularDeviation(turn_candidates[2].angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) + { + if (m_node_based_graph->GetEdgeData(turn_candidates[2].eid).name_id == + m_node_based_graph->GetEdgeData(via_eid).name_id) + { + turn_candidates[2].instruction = TurnInstruction::NO_TURN(); + } + else + { + turn_candidates[2].instruction.type = TurnType::NewName; + } + return turn_candidates; + } + } + else if (isLowPriorityRoadClass(m_node_based_graph->GetEdgeData(turn_candidates[2].eid) + .road_classification.road_class) && + !isLowPriorityRoadClass(m_node_based_graph->GetEdgeData(turn_candidates[1].eid) + .road_classification.road_class)) + { + if (angularDeviation(turn_candidates[1].angle, STRAIGHT_ANGLE) < NARROW_TURN_ANGLE) + { + if (m_node_based_graph->GetEdgeData(turn_candidates[1].eid).name_id == + m_node_based_graph->GetEdgeData(via_eid).name_id) + { + turn_candidates[1].instruction = TurnInstruction::NO_TURN(); + } + else + { + turn_candidates[1].instruction.type = TurnType::NewName; + } + return turn_candidates; + } + } + } + BOOST_ASSERT_MSG(std::is_sorted(turn_candidates.begin(), turn_candidates.end(), [](const TurnCandidate &left, const TurnCandidate &right) { return left.angle < right.angle; }), "Turn Candidates not sorted by angle."); - const auto end_valid = std::remove_if(turn_candidates.begin(), turn_candidates.end(), - [](const TurnCandidate &candidate) - { - return !candidate.valid; - }); - turn_candidates.erase(end_valid, turn_candidates.end()); const auto getLeft = [&turn_candidates](std::size_t index) { @@ -747,56 +1019,73 @@ EdgeBasedGraphFactory::suppressTurns(EdgeID via_eid, for (std::size_t turn_index = 0; turn_index < turn_candidates.size(); ++turn_index) { auto &candidate = turn_candidates[turn_index]; + if (!isBasic(candidate.instruction.type)) + continue; + const EdgeData &out_data = m_node_based_graph->GetEdgeData(candidate.eid); - if (candidate.valid && candidate.instruction != TurnInstruction::UTurn) + if (out_data.name_id == in_data.name_id && in_data.name_id != 0 && + candidate.instruction.direction_modifier != DirectionModifier::UTurn && + !has_obvious_with_same_name) + { + candidate.instruction.type = TurnType::Continue; + } + if (candidate.valid && !isUturn(candidate.instruction)) { // TODO road category would be useful to indicate obviousness of turn // check if turn can be omitted or at least changed const auto &left = turn_candidates[getLeft(turn_index)]; const auto &right = turn_candidates[getRight(turn_index)]; - // make very slight instructions straight, if they are the only valid choice going with + // make very slight instructions straight, if they are the only valid choice going + // with // at most a slight turn - if (candidate.instruction < TurnInstruction::ReachViaLocation && - (!isSlightTurn(getTurnDirection(left.angle)) || !left.valid) && - (!isSlightTurn(getTurnDirection(right.angle)) || !right.valid) && + if ((!isSlightModifier(getTurnDirection(left.angle)) || !left.valid) && + (!isSlightModifier(getTurnDirection(right.angle)) || !right.valid) && angularDeviation(candidate.angle, STRAIGHT_ANGLE) < FUZZY_STRAIGHT_ANGLE) - candidate.instruction = TurnInstruction::GoStraight; + candidate.instruction.direction_modifier = DirectionModifier::Straight; // TODO this smaller comparison for turns is DANGEROUS, has to be revised if turn // instructions change - if (candidate.instruction < TurnInstruction::ReachViaLocation) + if (in_data.travel_mode == + out_data.travel_mode) // make sure to always announce mode changes { - if (in_data.travel_mode == - out_data.travel_mode) // make sure to always announce mode changes + if (isObviousChoice(via_eid, turn_index, turn_candidates)) { - if (isObviousChoice(via_eid, turn_index, turn_candidates)) - { - if (in_data.name_id == out_data.name_id) // same road - { - candidate.instruction = TurnInstruction::NoTurn; - } - - else if (!has_obvious_with_same_name) - { - // TODO discuss, we might want to keep the current name of the turn. But - // this would mean emitting a turn when you just keep on a road - candidate.instruction = TurnInstruction::NameChanges; - } - else if (candidate.angle < obvious_with_same_name_angle) - candidate.instruction = TurnInstruction::TurnSlightRight; - else - candidate.instruction = TurnInstruction::TurnSlightLeft; - } - else if (candidate.instruction == TurnInstruction::GoStraight && - has_obvious_with_same_name) + if (in_data.name_id == out_data.name_id) // same road { - if (candidate.angle < obvious_with_same_name_angle) - candidate.instruction = TurnInstruction::TurnSlightRight; - else - candidate.instruction = TurnInstruction::TurnSlightLeft; + candidate.instruction.type = TurnType::Suppressed; } + + else if (!has_obvious_with_same_name) + { + // TODO discuss, we might want to keep the current name of the turn. But + // this would mean emitting a turn when you just keep on a road + if (isRampClass(in_data.road_classification.road_class) && + !isRampClass(out_data.road_classification.road_class)) + { + candidate.instruction.type = TurnType::Merge; + candidate.instruction.direction_modifier = + mirrorDirectionModifier(candidate.instruction.direction_modifier); + } + else + { + if (engine::guidance::canBeSuppressed(candidate.instruction.type)) + candidate.instruction.type = TurnType::NewName; + } + } + else if (candidate.angle < obvious_with_same_name_angle) + candidate.instruction.direction_modifier = DirectionModifier::SlightRight; + else + candidate.instruction.direction_modifier = DirectionModifier::SlightLeft; + } + else if (candidate.instruction.direction_modifier == DirectionModifier::Straight && + has_obvious_with_same_name) + { + if (candidate.angle < obvious_with_same_name_angle) + candidate.instruction.direction_modifier = DirectionModifier::SlightRight; + else + candidate.instruction.direction_modifier = DirectionModifier::SlightLeft; } } } @@ -805,7 +1094,7 @@ EdgeBasedGraphFactory::suppressTurns(EdgeID via_eid, } std::vector -EdgeBasedGraphFactory::getTurnCandidates(NodeID from_node, EdgeID via_eid) +EdgeBasedGraphFactory::getTurnCandidates(const NodeID from_node, const EdgeID via_eid) { std::vector turn_candidates; const NodeID turn_node = m_node_based_graph->GetTarget(via_eid); @@ -813,6 +1102,7 @@ EdgeBasedGraphFactory::getTurnCandidates(NodeID from_node, EdgeID via_eid) m_restriction_map->CheckForEmanatingIsOnlyTurn(from_node, turn_node); const bool is_barrier_node = m_barrier_nodes.find(turn_node) != m_barrier_nodes.end(); + bool has_non_roundabout = false, has_roundabout_entry; for (const EdgeID onto_edge : m_node_based_graph->GetAdjacentEdgeRange(turn_node)) { bool turn_is_valid = true; @@ -875,16 +1165,21 @@ EdgeBasedGraphFactory::getTurnCandidates(NodeID from_node, EdgeID via_eid) // unpack first node of second segment if packed - const auto first_coordinate = - getRepresentativeCoordinate(from_node, turn_node, via_eid, INVERT); - const auto third_coordinate = - getRepresentativeCoordinate(turn_node, to_node, onto_edge, !INVERT); + const auto first_coordinate = getRepresentativeCoordinate( + from_node, turn_node, via_eid, INVERT, m_compressed_edge_container, m_node_info_list); + const auto third_coordinate = getRepresentativeCoordinate( + turn_node, to_node, onto_edge, !INVERT, m_compressed_edge_container, m_node_info_list); const auto angle = util::coordinate_calculation::computeAngle( first_coordinate, m_node_info_list[turn_node], third_coordinate); const auto turn = AnalyzeTurn(from_node, via_eid, turn_node, onto_edge, to_node, angle); + if (turn_is_valid && !entersRoundabout(turn)) + has_non_roundabout = true; + else if (turn_is_valid) + has_roundabout_entry = true; + auto confidence = getTurnConfidence(angle, turn); if (!turn_is_valid) confidence *= 0.8; // makes invalid turns more likely to be resolved in conflicts @@ -892,6 +1187,20 @@ EdgeBasedGraphFactory::getTurnCandidates(NodeID from_node, EdgeID via_eid) turn_candidates.push_back({onto_edge, turn_is_valid, angle, turn, confidence}); } + if (has_non_roundabout && has_roundabout_entry) + { + for (auto &candidate : turn_candidates) + { + if (entersRoundabout(candidate.instruction)) + { + if (candidate.instruction.type == TurnType::EnterRotary) + candidate.instruction.type = TurnType::EnterRotaryAtExit; + if (candidate.instruction.type == TurnType::EnterRoundabout) + candidate.instruction.type = TurnType::EnterRoundaboutAtExit; + } + } + } + const auto ByAngle = [](const TurnCandidate &first, const TurnCandidate second) { return first.angle < second.angle; @@ -960,9 +1269,11 @@ TurnInstruction EdgeBasedGraphFactory::AnalyzeTurn(const NodeID node_u, const EdgeData &data1 = m_node_based_graph->GetEdgeData(edge1); const EdgeData &data2 = m_node_based_graph->GetEdgeData(edge2); + bool from_ramp = isRampClass(data1.road_classification.road_class); + bool to_ramp = isRampClass(data2.road_classification.road_class); if (node_u == node_w) { - return TurnInstruction::UTurn; + return {TurnType::Turn, DirectionModifier::UTurn}; } // roundabouts need to be handled explicitely @@ -972,9 +1283,9 @@ TurnInstruction EdgeBasedGraphFactory::AnalyzeTurn(const NodeID node_u, if (1 == m_node_based_graph->GetDirectedOutDegree(node_v)) { // No turn possible. - return TurnInstruction::NoTurn; + return TurnInstruction::NO_TURN(); } - return TurnInstruction::StayOnRoundAbout; + return TurnInstruction::REMAIN_ROUNDABOUT(getTurnDirection(angle)); } // Does turn start or end on roundabout? if (data1.roundabout || data2.roundabout) @@ -982,97 +1293,23 @@ TurnInstruction EdgeBasedGraphFactory::AnalyzeTurn(const NodeID node_u, // We are entering the roundabout if ((!data1.roundabout) && data2.roundabout) { - return TurnInstruction::EnterRoundAbout; + return TurnInstruction::ENTER_ROUNDABOUT(getTurnDirection(angle)); } // We are leaving the roundabout if (data1.roundabout && (!data2.roundabout)) { - return TurnInstruction::LeaveRoundAbout; + return TurnInstruction::EXIT_ROUNDABOUT(getTurnDirection(angle)); } } + if (!from_ramp && to_ramp) + { + return {TurnType::Ramp, getTurnDirection(angle)}; + } + // assign a designated turn angle instruction purely based on the angle - return getTurnDirection(angle); + return {TurnType::Turn, getTurnDirection(angle)}; } -QueryNode EdgeBasedGraphFactory::getRepresentativeCoordinate(const NodeID src, - const NodeID tgt, - const EdgeID via_eid, - bool INVERTED) const -{ - if (m_compressed_edge_container.HasEntryForID(via_eid)) - { - util::Coordinate prev = util::Coordinate(m_node_info_list[INVERTED ? tgt : src].lon, - m_node_info_list[INVERTED ? tgt : src].lat), - cur; - // walk along the edge for the first 5 meters - const auto &geometry = m_compressed_edge_container.GetBucketReference(via_eid); - double dist = 0; - double this_dist = 0; - NodeID prev_id = INVERTED ? tgt : src; - - const auto selectBestCandidate = - [this](const NodeID current, const double current_distance, const NodeID previous, - const double previous_distance) - { - if (current_distance < DESIRED_SEGMENT_LENGTH || - current_distance - DESIRED_SEGMENT_LENGTH < - DESIRED_SEGMENT_LENGTH - previous_distance || - previous_distance < MINIMAL_SEGMENT_LENGTH) - { - return m_node_info_list[current]; - } - else - { - return m_node_info_list[previous]; - } - }; - - if (INVERTED) - { - for (auto itr = geometry.rbegin(), end = geometry.rend(); itr != end; ++itr) - { - const auto compressed_node = *itr; - cur = util::Coordinate(m_node_info_list[compressed_node.node_id].lon, - m_node_info_list[compressed_node.node_id].lat); - this_dist = util::coordinate_calculation::haversineDistance(prev, cur); - if (dist + this_dist > DESIRED_SEGMENT_LENGTH) - { - return selectBestCandidate(compressed_node.node_id, dist + this_dist, prev_id, - dist); - } - dist += this_dist; - prev = cur; - prev_id = compressed_node.node_id; - } - cur = util::Coordinate(m_node_info_list[src].lon, m_node_info_list[src].lat); - this_dist = util::coordinate_calculation::haversineDistance(prev, cur); - return selectBestCandidate(src, dist + this_dist, prev_id, dist); - } - else - { - for (auto itr = geometry.begin(), end = geometry.end(); itr != end; ++itr) - { - const auto compressed_node = *itr; - cur = util::Coordinate(m_node_info_list[compressed_node.node_id].lon, - m_node_info_list[compressed_node.node_id].lat); - this_dist = util::coordinate_calculation::haversineDistance(prev, cur); - if (dist + this_dist > DESIRED_SEGMENT_LENGTH) - { - return selectBestCandidate(compressed_node.node_id, dist + this_dist, prev_id, - dist); - } - dist += this_dist; - prev = cur; - prev_id = compressed_node.node_id; - } - cur = util::Coordinate(m_node_info_list[tgt].lon, m_node_info_list[tgt].lat); - this_dist = util::coordinate_calculation::haversineDistance(prev, cur); - return selectBestCandidate(tgt, dist + this_dist, prev_id, dist); - } - } - // default: If the edge is very short, or we do not have a compressed geometry - return m_node_info_list[INVERTED ? src : tgt]; -} } // namespace extractor } // namespace osrm diff --git a/src/extractor/extractor.cpp b/src/extractor/extractor.cpp index 10f0fa6f4..67950f8aa 100644 --- a/src/extractor/extractor.cpp +++ b/src/extractor/extractor.cpp @@ -188,6 +188,7 @@ int Extractor::run() local_state, "way_function", boost::cref(static_cast(*entity)), boost::ref(result_way)); + result_way.road_classification_data.augment(static_cast(*entity)); resulting_ways.push_back(std::make_pair(x, result_way)); break; case osmium::item_type::relation: diff --git a/src/extractor/extractor_callbacks.cpp b/src/extractor/extractor_callbacks.cpp index 441ac3986..1915f2744 100644 --- a/src/extractor/extractor_callbacks.cpp +++ b/src/extractor/extractor_callbacks.cpp @@ -173,7 +173,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti name_id, backward_weight_data, true, false, parsed_way.roundabout, parsed_way.is_access_restricted, parsed_way.is_startpoint, parsed_way.backward_travel_mode, - false)); + false,parsed_way.road_classification_data)); }); external_memory.way_start_end_id_list.push_back( @@ -193,7 +193,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti name_id, forward_weight_data, true, !forward_only, parsed_way.roundabout, parsed_way.is_access_restricted, parsed_way.is_startpoint, parsed_way.forward_travel_mode, - split_edge)); + split_edge,parsed_way.road_classification_data)); }); if (split_edge) { @@ -206,7 +206,7 @@ void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const Extracti OSMNodeID(first_node.ref()), OSMNodeID(last_node.ref()), name_id, backward_weight_data, false, true, parsed_way.roundabout, parsed_way.is_access_restricted, parsed_way.is_startpoint, - parsed_way.backward_travel_mode, true)); + parsed_way.backward_travel_mode, true,parsed_way.road_classification_data)); }); } diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp index 2c2361256..a725625ee 100644 --- a/src/storage/storage.cpp +++ b/src/storage/storage.cpp @@ -8,7 +8,7 @@ #include "util/static_rtree.hpp" #include "engine/datafacade/datafacade_base.hpp" #include "extractor/travel_mode.hpp" -#include "extractor/turn_instructions.hpp" +#include "engine/guidance/turn_instruction.hpp" #include "storage/storage.hpp" #include "storage/shared_datatype.hpp" #include "storage/shared_barriers.hpp" @@ -225,8 +225,8 @@ int Storage::Run() number_of_original_edges); shared_layout_ptr->SetBlockSize(SharedDataLayout::TRAVEL_MODE, number_of_original_edges); - shared_layout_ptr->SetBlockSize(SharedDataLayout::TURN_INSTRUCTION, - number_of_original_edges); + shared_layout_ptr->SetBlockSize( + SharedDataLayout::TURN_INSTRUCTION, number_of_original_edges); boost::filesystem::ifstream hsgr_input_stream(hsgr_path, std::ios::binary); @@ -390,8 +390,8 @@ int Storage::Run() shared_layout_ptr->GetBlockPtr(shared_memory_ptr, SharedDataLayout::TRAVEL_MODE); - extractor::TurnInstruction *turn_instructions_ptr = - shared_layout_ptr->GetBlockPtr( + engine::guidance::TurnInstruction *turn_instructions_ptr = + shared_layout_ptr->GetBlockPtr( shared_memory_ptr, SharedDataLayout::TURN_INSTRUCTION); extractor::OriginalEdgeData current_edge_data; diff --git a/src/util/coordinate_calculation.cpp b/src/util/coordinate_calculation.cpp index 6f37bd627..6c32b9ed8 100644 --- a/src/util/coordinate_calculation.cpp +++ b/src/util/coordinate_calculation.cpp @@ -257,6 +257,15 @@ double computeAngle(const Coordinate first, const Coordinate second, const Coord return angle; } +Coordinate interpolateLinear(double factor, const Coordinate from, const Coordinate to) +{ + BOOST_ASSERT(0 <= factor && factor <= 1.0); + return {from.lon + toFixed(FloatLongitude( + factor * static_cast(toFloating(to.lon - from.lon)))), + from.lat + toFixed(FloatLatitude( + factor * static_cast(toFloating(to.lat - from.lat))))}; +} + namespace mercator { FloatLatitude yToLat(const double value) @@ -276,6 +285,7 @@ double latToY(const FloatLatitude latitude) std::log(std::tan((pi() / 4.) + static_cast(latitude) * (pi() / 180.) / 2.)); } + } // ns mercato // ns mercatorr } // ns coordinate_calculation } // ns util