From f3e72623e9ec9764c6be1f2aa81a4523f9647077 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Thu, 28 Jan 2016 16:28:44 +0100 Subject: [PATCH] Add viaroute suport for new API --- CMakeLists.txt | 5 +- include/engine/api/base_api.hpp | 70 ++++ include/engine/api/base_parameters.hpp | 51 +++ include/engine/api/json_factory.hpp | 90 +++++ include/engine/api/match_parameters.hpp | 25 ++ include/engine/api/nearest_parameters.hpp | 23 ++ include/engine/api/route_api.hpp | 135 +++++++ include/engine/api/route_parameters.hpp | 58 +++ include/engine/api/table_parameters.hpp | 27 ++ include/engine/api/trip_parameters.hpp | 24 ++ include/engine/api_response_generator.hpp | 299 --------------- include/engine/datafacade/datafacade_base.hpp | 23 +- .../engine/datafacade/internal_datafacade.hpp | 54 ++- .../engine/datafacade/shared_datafacade.hpp | 56 ++- include/engine/douglas_peucker.hpp | 14 +- include/engine/engine.hpp | 48 ++- include/engine/geospatial_query.hpp | 126 ++++++- include/engine/guidance/assemble_geometry.hpp | 78 ++++ include/engine/guidance/assemble_leg.hpp | 165 +++++++++ include/engine/guidance/assemble_overview.hpp | 22 ++ include/engine/guidance/assemble_route.hpp | 37 ++ include/engine/guidance/assemble_steps.hpp | 135 +++++++ include/engine/guidance/leg_geometry.hpp | 54 +++ include/engine/guidance/route.hpp | 18 + include/engine/guidance/route_leg.hpp | 30 ++ include/engine/guidance/route_step.hpp | 39 ++ include/engine/guidance/segment_list.hpp | 347 ------------------ include/engine/guidance/step_maneuver.hpp | 26 ++ .../guidance/textual_route_annotation.hpp | 147 -------- include/engine/hint.hpp | 61 +++ include/engine/internal_route_result.hpp | 25 +- include/engine/phantom_node.hpp | 2 +- include/engine/plugins/plugin_base.hpp | 118 ++++-- include/engine/plugins/viaroute.hpp | 157 +------- include/engine/polyline_compressor.hpp | 13 +- include/engine/polyline_formatter.hpp | 24 -- include/engine/route_name_extraction.hpp | 150 -------- include/engine/route_parameters.hpp | 129 ------- .../direct_shortest_path.hpp | 3 - .../routing_algorithms/routing_base.hpp | 21 +- .../routing_algorithms/shortest_path.hpp | 13 +- include/engine/search_engine.hpp | 47 --- include/engine/segment_information.hpp | 60 --- include/engine/status.hpp | 18 + include/osrm/osrm.hpp | 14 +- include/osrm/route_parameters.hpp | 4 +- include/osrm/status.hpp | 11 + .../server/api/base_parameters_grammar.hpp | 82 +++++ include/server/api/parsed_url.hpp | 29 ++ .../server/api/route_parameters_grammar.hpp | 92 +++++ .../server/api/route_parameters_parser.hpp | 28 ++ include/server/api/url_parser.hpp | 30 ++ include/server/api_grammar.hpp | 151 -------- include/server/request_handler.hpp | 17 +- include/server/server.hpp | 6 +- include/server/service/base_service.hpp | 36 ++ include/server/service/route_service.hpp | 34 ++ include/server/service_handler.hpp | 40 ++ include/util/coordinate.hpp | 11 +- include/util/json_util.hpp | 2 +- include/util/tiles.hpp | 85 +++++ src/engine/api/josn_factory.cpp | 222 +++++++++++ src/engine/douglas_peucker.cpp | 91 ++--- src/engine/engine.cpp | 138 +++---- src/engine/guidance/assemble_overview.cpp | 99 +++++ src/engine/plugins/viaroute.cpp | 115 ++++++ src/engine/polyline_compressor.cpp | 31 +- src/engine/polyline_formatter.cpp | 31 -- src/engine/route_parameters.cpp | 170 --------- src/engine/search_engine_data.cpp | 7 + src/osrm/osrm.cpp | 7 +- src/server/api/route_parameters_parser.cpp | 28 ++ src/server/api/url_parser.cpp | 104 ++++++ src/server/connection.cpp | 2 +- src/server/http/reply.cpp | 4 +- src/server/request_handler.cpp | 90 ++--- src/server/service/route_service.cpp | 83 +++++ src/server/service_handler.cpp | 39 ++ src/storage/storage.cpp | 2 +- src/tools/routed.cpp | 5 +- src/util/coordinate.cpp | 11 +- unit_tests/engine/douglas_peucker.cpp | 72 ++-- unit_tests/engine/geometry_string.cpp | 2 +- unit_tests/server/route_parameters_parser.cpp | 168 +++++++++ unit_tests/server/url_parser.cpp | 110 ++++++ unit_tests/server_tests.cpp | 7 + unit_tests/util/tiles.cpp | 74 ++++ 87 files changed, 3352 insertions(+), 2099 deletions(-) create mode 100644 include/engine/api/base_api.hpp create mode 100644 include/engine/api/base_parameters.hpp create mode 100644 include/engine/api/json_factory.hpp create mode 100644 include/engine/api/match_parameters.hpp create mode 100644 include/engine/api/nearest_parameters.hpp create mode 100644 include/engine/api/route_api.hpp create mode 100644 include/engine/api/route_parameters.hpp create mode 100644 include/engine/api/table_parameters.hpp create mode 100644 include/engine/api/trip_parameters.hpp delete mode 100644 include/engine/api_response_generator.hpp create mode 100644 include/engine/guidance/assemble_geometry.hpp create mode 100644 include/engine/guidance/assemble_leg.hpp create mode 100644 include/engine/guidance/assemble_overview.hpp create mode 100644 include/engine/guidance/assemble_route.hpp create mode 100644 include/engine/guidance/assemble_steps.hpp create mode 100644 include/engine/guidance/leg_geometry.hpp create mode 100644 include/engine/guidance/route.hpp create mode 100644 include/engine/guidance/route_leg.hpp create mode 100644 include/engine/guidance/route_step.hpp delete mode 100644 include/engine/guidance/segment_list.hpp create mode 100644 include/engine/guidance/step_maneuver.hpp delete mode 100644 include/engine/guidance/textual_route_annotation.hpp create mode 100644 include/engine/hint.hpp delete mode 100644 include/engine/polyline_formatter.hpp delete mode 100644 include/engine/route_name_extraction.hpp delete mode 100644 include/engine/route_parameters.hpp delete mode 100644 include/engine/search_engine.hpp delete mode 100644 include/engine/segment_information.hpp create mode 100644 include/engine/status.hpp create mode 100644 include/osrm/status.hpp create mode 100644 include/server/api/base_parameters_grammar.hpp create mode 100644 include/server/api/parsed_url.hpp create mode 100644 include/server/api/route_parameters_grammar.hpp create mode 100644 include/server/api/route_parameters_parser.hpp create mode 100644 include/server/api/url_parser.hpp delete mode 100644 include/server/api_grammar.hpp create mode 100644 include/server/service/base_service.hpp create mode 100644 include/server/service/route_service.hpp create mode 100644 include/server/service_handler.hpp create mode 100644 include/util/tiles.hpp create mode 100644 src/engine/api/josn_factory.cpp create mode 100644 src/engine/guidance/assemble_overview.cpp create mode 100644 src/engine/plugins/viaroute.cpp delete mode 100644 src/engine/polyline_formatter.cpp delete mode 100644 src/engine/route_parameters.cpp create mode 100644 src/server/api/route_parameters_parser.cpp create mode 100644 src/server/api/url_parser.cpp create mode 100644 src/server/service/route_service.cpp create mode 100644 src/server/service_handler.cpp create mode 100644 unit_tests/server/route_parameters_parser.cpp create mode 100644 unit_tests/server/url_parser.cpp create mode 100644 unit_tests/server_tests.cpp create mode 100644 unit_tests/util/tiles.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 0fbe015b0..35eac9ac3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -45,7 +45,7 @@ add_custom_target(FingerPrintConfigure ALL ${CMAKE_COMMAND} COMMENT "Configuring revision fingerprint" VERBATIM) -add_custom_target(tests DEPENDS engine-tests extractor-tests util-tests) +add_custom_target(tests DEPENDS engine-tests extractor-tests util-tests server-tests) add_custom_target(benchmarks DEPENDS rtree-bench) set(BOOST_COMPONENTS date_time filesystem iostreams program_options regex system thread unit_test_framework) @@ -63,6 +63,7 @@ file(GLOB EngineGlob src/engine/*.cpp src/engine/**/*.cpp) file(GLOB ExtractorTestsGlob unit_tests/extractor/*.cpp) file(GLOB EngineTestsGlob unit_tests/engine/*.cpp) file(GLOB UtilTestsGlob unit_tests/util/*.cpp) +file(GLOB ServerTestsGlob unit_tests/server/*.cpp) file(GLOB IOTestsGlob unit_tests/io/*.cpp) add_library(UTIL OBJECT ${UtilGlob}) @@ -88,6 +89,7 @@ add_library(osrm_store $ $) add_executable(engine-tests EXCLUDE_FROM_ALL unit_tests/engine_tests.cpp ${EngineTestsGlob} $ $) add_executable(extractor-tests EXCLUDE_FROM_ALL unit_tests/extractor_tests.cpp ${ExtractorTestsGlob} $ $) add_executable(util-tests EXCLUDE_FROM_ALL unit_tests/util_tests.cpp ${UtilTestsGlob} $) +add_executable(server-tests EXCLUDE_FROM_ALL unit_tests/server_tests.cpp ${ServerTestsGlob} $ $) # Benchmarks add_executable(rtree-bench EXCLUDE_FROM_ALL src/benchmarks/static_rtree.cpp $) @@ -332,6 +334,7 @@ target_link_libraries(osrm_extract ${EXTRACTOR_LIBRARIES}) target_link_libraries(osrm_store ${STORAGE_LIBRARIES}) # Tests target_link_libraries(engine-tests ${ENGINE_LIBRARIES}) +target_link_libraries(server-tests osrm ${Boost_LIBRARIES}) target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES}) target_link_libraries(rtree-bench ${Boost_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${TBB_LIBRARIES}) target_link_libraries(util-tests ${UTIL_LIBRARIES}) diff --git a/include/engine/api/base_api.hpp b/include/engine/api/base_api.hpp new file mode 100644 index 000000000..ab1e9f2e1 --- /dev/null +++ b/include/engine/api/base_api.hpp @@ -0,0 +1,70 @@ +#ifndef ENGINE_API_BASE_API_HPP +#define ENGINE_API_BASE_API_HPP + +#include "engine/api/base_parameters.hpp" +#include "engine/datafacade/datafacade_base.hpp" + +#include "engine/api/json_factory.hpp" +#include "engine/hint.hpp" + +#include + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +namespace detail +{ +template class BaseAPI_ +{ + public: + BaseAPI_(const datafacade::BaseDataFacade &facade_, const BaseParameters ¶meters_) + : facade(facade_), parameters(parameters_) + { + } + + virtual util::json::Array + MakeWaypoints(const std::vector &segment_end_coordinates) const + { + BOOST_ASSERT(parameters.coordinates.size() > 0); + BOOST_ASSERT(parameters.coordinates.size() == segment_end_coordinates.size() + 1); + + util::json::Array waypoints; + waypoints.values.resize(parameters.coordinates.size()); + waypoints.values[0] = MakeWaypoint(parameters.coordinates.front(), + segment_end_coordinates.front().source_phantom); + + auto coord_iter = std::next(parameters.coordinates.begin()); + auto out_iter = std::next(waypoints.values.begin()); + for (const auto &phantoms : segment_end_coordinates) + { + *out_iter++ = MakeWaypoint(*coord_iter++, phantoms.target_phantom); + } + return waypoints; + } + + protected: + virtual util::json::Object MakeWaypoint(const util::FixedPointCoordinate input_coordinate, + const PhantomNode &phantom) const + { + return json::makeWaypoint(phantom.location, facade.get_name_for_id(phantom.name_id), + Hint{input_coordinate, phantom, facade.GetCheckSum()}); + } + + const datafacade::BaseDataFacade &facade; + const BaseParameters ¶meters; +}; +} + +// Only expose non-templated version +using BaseAPI = detail::BaseAPI_; +} +} +} + +#endif diff --git a/include/engine/api/base_parameters.hpp b/include/engine/api/base_parameters.hpp new file mode 100644 index 000000000..abcbe1a3e --- /dev/null +++ b/include/engine/api/base_parameters.hpp @@ -0,0 +1,51 @@ +#ifndef ENGINE_API_BASE_PARAMETERS_HPP +#define ENGINE_API_BASE_PARAMETERS_HPP + +#include "engine/hint.hpp" +#include "util/coordinate.hpp" + +#include + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ +struct BaseParameters +{ + struct Bearing + { + short bearing; + short range; + }; + + std::vector coordinates; + std::vector> hints; + std::vector> radiuses; + std::vector> bearings; + + // FIXME add validation for invalid bearing values + bool IsValid() const + { + return (hints.empty() || hints.size() == coordinates.size()) && + (bearings.empty() || bearings.size() == coordinates.size()) && + (radiuses.empty() || radiuses.size() == coordinates.size()); + } +}; + +inline bool operator==(const BaseParameters::Bearing lhs, const BaseParameters::Bearing rhs) +{ + return lhs.bearing == rhs.bearing && lhs.range == rhs.range; +} +inline bool operator!=(const BaseParameters::Bearing lhs, const BaseParameters::Bearing rhs) +{ + return !(lhs == rhs); +} +} +} +} + +#endif // ROUTE_PARAMETERS_HPP diff --git a/include/engine/api/json_factory.hpp b/include/engine/api/json_factory.hpp new file mode 100644 index 000000000..987ff7e40 --- /dev/null +++ b/include/engine/api/json_factory.hpp @@ -0,0 +1,90 @@ +#ifndef ENGINE_RESPONSE_OBJECTS_HPP_ +#define ENGINE_RESPONSE_OBJECTS_HPP_ + +#include "extractor/turn_instructions.hpp" +#include "extractor/travel_mode.hpp" +#include "engine/polyline_compressor.hpp" +#include "util/coordinate.hpp" +#include "util/json_container.hpp" + +#include + +#include +#include +#include + +namespace osrm +{ +namespace engine +{ + +struct Hint; + +namespace guidance +{ +class RouteLeg; +class RouteStep; +class StepManeuver; +class Route; +class LegGeometry; +} + +namespace api +{ +namespace json +{ +namespace detail +{ + +std::string instructionToString(extractor::TurnInstruction instruction); + +util::json::Array coordinateToLonLat(const FixedPointCoordinate &coordinate); + +std::string modeToString(const extractor::TravelMode mode); + +} // namespace detail + +template util::json::String makePolyline(ForwardIter begin, ForwardIter end) +{ + util::json::String polyline; + polyline.value = encodePolyline(begin, end); + return polyline; +} + +template +util::json::Object makeGeoJSONLineString(ForwardIter begin, ForwardIter end) +{ + util::json::Object geojson; + geojson.values["type"] = "LineString"; + util::json::Array coordinates; + std::transform(begin, end, std::back_inserter(coordinates.values), + [](const util::FixedPointCoordinate loc) + { + return detail::coordinateToLonLat(loc); + }); + geojson.values["coordinates"] = std::move(coordinates); + return geojson; +} + +util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver); + +util::json::Object makeRouteStep(guidance::RouteStep &&step, + boost::optional geometry); + +util::json::Object makeRoute(const guidance::Route &route, + util::json::Array &&legs, + boost::optional geometry); + +util::json::Object +makeWaypoint(const FixedPointCoordinate location, std::string &&way_name, const Hint &hint); + +util::json::Object makeRouteLeg(guidance::RouteLeg &&leg, util::json::Array &&steps); + +util::json::Array makeRouteLegs(std::vector &&legs, + const std::vector &leg_geometries); +} +} +} // namespace engine +} // namespace osrm + +#endif // ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_ diff --git a/include/engine/api/match_parameters.hpp b/include/engine/api/match_parameters.hpp new file mode 100644 index 000000000..77486cc3d --- /dev/null +++ b/include/engine/api/match_parameters.hpp @@ -0,0 +1,25 @@ +#ifndef ENGINE_API_MATCH_PARAMETERS_HPP +#define ENGINE_API_MATCH_PARAMETERS_HPP + +#include "engine/api/route_parameters.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +struct MatchParameters : public RouteParameters +{ + std::vector timestamps; + bool IsValid() const; +}; + +} +} +} + +#endif diff --git a/include/engine/api/nearest_parameters.hpp b/include/engine/api/nearest_parameters.hpp new file mode 100644 index 000000000..dd255d02b --- /dev/null +++ b/include/engine/api/nearest_parameters.hpp @@ -0,0 +1,23 @@ +#ifndef ENGINE_API_NEAREST_PARAMETERS_HPP +#define ENGINE_API_NEAREST_PARAMETERS_HPP + +#include "engine/api/base_parameters.hpp" + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +struct NearestParameters : public BaseParameters +{ + unsigned number_of_results; + + bool IsValid() const; +}; +} +} +} + +#endif // ROUTE_PARAMETERS_HPP diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp new file mode 100644 index 000000000..ebe7a8979 --- /dev/null +++ b/include/engine/api/route_api.hpp @@ -0,0 +1,135 @@ +#ifndef ENGINE_API_ROUTE_HPP +#define ENGINE_API_ROUTE_HPP + +#include "engine/api/base_api.hpp" +#include "engine/api/route_parameters.hpp" +#include "engine/api/json_factory.hpp" + +#include "engine/datafacade/datafacade_base.hpp" + +#include "engine/guidance/assemble_leg.hpp" +#include "engine/guidance/assemble_route.hpp" +#include "engine/guidance/assemble_geometry.hpp" +#include "engine/guidance/assemble_overview.hpp" +#include "engine/guidance/assemble_steps.hpp" + +#include "engine/internal_route_result.hpp" + +#include "util/integer_range.hpp" + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +namespace detail +{ +template class RouteAPI_ : public BaseAPI_> +{ + using BaseT = BaseAPI_>; + + public: + RouteAPI_(const datafacade::BaseDataFacade &facade_, const RouteParameters ¶meters_) + : BaseT(facade_, parameters_), parameters(parameters_) + { + } + + virtual void MakeResponse(const InternalRouteResult &raw_route, + util::json::Object &response) const + { + auto number_of_routes = raw_route.has_alternative() ? 2UL : 1UL; + util::json::Array routes; + routes.values.resize(number_of_routes); + routes.values[0] = + MakeRoute(raw_route.segment_end_coordinates, raw_route.unpacked_path_segments, + raw_route.source_traversed_in_reverse, raw_route.target_traversed_in_reverse); + if (raw_route.has_alternative()) + { + std::vector> wrapped_leg(1); + wrapped_leg.front() = std::move(raw_route.unpacked_alternative); + routes.values[1] = MakeRoute(raw_route.segment_end_coordinates, wrapped_leg, + raw_route.alt_source_traversed_in_reverse, + raw_route.alt_target_traversed_in_reverse); + } + response.values["waypoints"] = BaseT::MakeWaypoints(raw_route.segment_end_coordinates); + response.values["routes"] = std::move(routes); + response.values["code"] = "ok"; + } + + protected: + template + util::json::Value MakeGeometry(ForwardIter begin, ForwardIter end) const + { + if (parameters.geometries == RouteParameters::GeometriesType::Polyline) + { + return json::makePolyline(begin, end); + } + + BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON); + return json::makeGeoJSONLineString(begin, end); + } + + virtual util::json::Object + MakeRoute(const std::vector &segment_end_coordinates, + const std::vector> &unpacked_path_segments, + const std::vector &source_traversed_in_reverse, + const std::vector &target_traversed_in_reverse) const + { + std::vector legs; + std::vector leg_geometries; + auto number_of_legs = segment_end_coordinates.size(); + legs.reserve(number_of_legs); + leg_geometries.reserve(number_of_legs); + 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]; + + auto leg_geometry = guidance::assembleGeometry( + BaseT::facade, path_data, phantoms.source_phantom, phantoms.target_phantom); + auto leg = guidance::assembleLeg(BaseT::facade, path_data, leg_geometry, phantoms.source_phantom, + phantoms.target_phantom, reversed_source, reversed_target); + + if (parameters.steps) + { + leg.steps = guidance::assembleSteps(BaseT::facade, + path_data, leg_geometry, phantoms.source_phantom, phantoms.target_phantom, + reversed_source, reversed_target); + } + + leg_geometries.push_back(std::move(leg_geometry)); + legs.push_back(std::move(leg)); + } + auto route = guidance::assembleRoute(legs); + boost::optional json_overview; + if (parameters.overview != RouteParameters::OverviewType::False) + { + const auto use_simplification = + parameters.overview == RouteParameters::OverviewType::Simplified; + BOOST_ASSERT(use_simplification || + parameters.overview == RouteParameters::OverviewType::Full); + + auto overview = guidance::assembleOverview(leg_geometries, use_simplification); + json_overview = MakeGeometry(overview.begin(), overview.end()); + } + + return json::makeRoute(route, json::makeRouteLegs(std::move(legs), leg_geometries), + std::move(json_overview)); + } + + const RouteParameters ¶meters; +}; +} + +// Expose non-templated version +using RouteAPI = detail::RouteAPI_; + +} +} +} + +#endif diff --git a/include/engine/api/route_parameters.hpp b/include/engine/api/route_parameters.hpp new file mode 100644 index 000000000..1f18dcc4a --- /dev/null +++ b/include/engine/api/route_parameters.hpp @@ -0,0 +1,58 @@ +#ifndef ENGINE_API_ROUTE_PARAMETERS_HPP +#define ENGINE_API_ROUTE_PARAMETERS_HPP + +#include "engine/api/base_parameters.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +struct RouteParameters : public BaseParameters +{ + enum class GeometriesType + { + Polyline, + GeoJSON + }; + enum class OverviewType + { + Simplified, + Full, + False + }; + + RouteParameters() = default; + + template + RouteParameters(const bool steps_, + const bool alternative_, + const GeometriesType geometries_, + const OverviewType overview_, + std::vector> uturns_, Args... args_) + : BaseParameters{std::forward(args_)...}, steps{steps_}, alternative{alternative_}, geometries{geometries_}, + overview{overview_}, uturns{std::move(uturns_)} + { + } + + bool steps = true; + bool alternative = true; + GeometriesType geometries = GeometriesType::Polyline; + OverviewType overview = OverviewType::Simplified; + std::vector> uturns; + + bool IsValid() const + { + return coordinates.size() >= 2 && BaseParameters::IsValid() && + (uturns.empty() || uturns.size() == coordinates.size()); + } +}; +} +} +} + +#endif diff --git a/include/engine/api/table_parameters.hpp b/include/engine/api/table_parameters.hpp new file mode 100644 index 000000000..a34486738 --- /dev/null +++ b/include/engine/api/table_parameters.hpp @@ -0,0 +1,27 @@ +#ifndef ENGINE_API_TABLE_PARAMETERS_HPP +#define ENGINE_API_TABLE_PARAMETERS_HPP + +#include "engine/api/base_parameters.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +struct TableParameters : public BaseParameters +{ + std::vector is_source; + std::vector is_destination; + + bool IsValid() const; +}; + +} +} +} + +#endif // ROUTE_PARAMETERS_HPP diff --git a/include/engine/api/trip_parameters.hpp b/include/engine/api/trip_parameters.hpp new file mode 100644 index 000000000..9f3dbe929 --- /dev/null +++ b/include/engine/api/trip_parameters.hpp @@ -0,0 +1,24 @@ +#ifndef ENGINE_API_TRIP_PARAMETERS_HPP +#define ENGINE_API_TRIP_PARAMETERS_HPP + +#include "engine/api/route_parameters.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ + +struct TripParameters : public RouteParameters +{ + bool IsValid() const; +}; + +} +} +} + +#endif diff --git a/include/engine/api_response_generator.hpp b/include/engine/api_response_generator.hpp deleted file mode 100644 index 0de8a7a24..000000000 --- a/include/engine/api_response_generator.hpp +++ /dev/null @@ -1,299 +0,0 @@ -#ifndef ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_ -#define ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_ - -#include "guidance/segment_list.hpp" -#include "guidance/textual_route_annotation.hpp" - -#include "engine/internal_route_result.hpp" -#include "engine/object_encoder.hpp" -#include "engine/phantom_node.hpp" -#include "engine/polyline_formatter.hpp" -#include "engine/route_name_extraction.hpp" -#include "engine/segment_information.hpp" -#include "extractor/turn_instructions.hpp" -#include "osrm/coordinate.hpp" -#include "osrm/json_container.hpp" -#include "osrm/route_parameters.hpp" -#include "util/integer_range.hpp" -#include "util/typedefs.hpp" - -#include - -#include -#include -#include - -#include -#include -#include -#include - -namespace osrm -{ -namespace engine -{ -namespace detail -{ -struct Segment -{ - uint32_t name_id; - int32_t length; - std::size_t position; -}; -} // namespace detail - -template class ApiResponseGenerator -{ - public: - using DataFacade = DataFacadeT; - using Segments = guidance::SegmentList; - using Segment = detail::Segment; - - ApiResponseGenerator(DataFacade *facade); - - // This runs a full annotation, according to config. - // The output is tailored to the viaroute plugin. - void DescribeRoute(const RouteParameters &config, - const InternalRouteResult &raw_route, - util::json::Object &json_result); - - // The following functions allow access to the different parts of the Describe Route - // functionality. - // For own responses, they can be used to generate only subsets of the information. - // In the normal situation, Describe Route is the desired usecase. - - // generate an overview of a raw route - util::json::Object SummarizeRoute(const InternalRouteResult &raw_route, - const Segments &segment_list) const; - - // create an array containing all via-points/-indices used in the query - util::json::Array ListViaPoints(const InternalRouteResult &raw_route) const; - util::json::Array ListViaIndices(const Segments &segment_list) const; - - util::json::Value GetGeometry(const bool return_encoded, const Segments &segments) const; - - // TODO this dedicated creation seems unnecessary? Only used for route names - std::vector BuildRouteSegments(const Segments &segment_list) const; - - // adds checksum and locations - util::json::Object BuildHintData(const InternalRouteResult &raw_route) const; - - private: - // data access to translate ids back into names - DataFacade *facade; -}; - -template -ApiResponseGenerator::ApiResponseGenerator(DataFacadeT *facade_) - : facade(facade_) -{ -} - -template -void ApiResponseGenerator::DescribeRoute(const RouteParameters &config, - const InternalRouteResult &raw_route, - util::json::Object &json_result) -{ - if (!raw_route.is_valid()) - { - return; - } - const constexpr bool ALLOW_SIMPLIFICATION = true; - const constexpr bool EXTRACT_ROUTE = false; - const constexpr bool EXTRACT_ALTERNATIVE = true; - Segments segment_list(raw_route, EXTRACT_ROUTE, config.zoom_level, ALLOW_SIMPLIFICATION, - facade); - json_result.values["route_summary"] = SummarizeRoute(raw_route, segment_list); - json_result.values["via_points"] = ListViaPoints(raw_route); - json_result.values["via_indices"] = ListViaIndices(segment_list); - - if (config.geometry) - { - json_result.values["route_geometry"] = GetGeometry(config.compression, segment_list); - } - - if (config.print_instructions) - { - json_result.values["route_instructions"] = - guidance::AnnotateRoute(segment_list.Get(), facade); - } - - RouteNames route_names; - - if (raw_route.has_alternative()) - { - Segments alternate_segment_list(raw_route, EXTRACT_ALTERNATIVE, config.zoom_level, - ALLOW_SIMPLIFICATION, facade); - - // Alternative Route Summaries are stored in an array to (down the line) allow multiple - // alternatives - util::json::Array json_alternate_route_summary_array; - json_alternate_route_summary_array.values.emplace_back( - SummarizeRoute(raw_route, alternate_segment_list)); - json_result.values["alternative_summaries"] = json_alternate_route_summary_array; - json_result.values["alternative_indices"] = ListViaIndices(alternate_segment_list); - - if (config.geometry) - { - auto alternate_geometry_string = - GetGeometry(config.compression, alternate_segment_list); - util::json::Array json_alternate_geometries_array; - json_alternate_geometries_array.values.emplace_back( - std::move(alternate_geometry_string)); - json_result.values["alternative_geometries"] = json_alternate_geometries_array; - } - - if (config.print_instructions) - { - util::json::Array json_alternate_annotations_array; - json_alternate_annotations_array.values.emplace_back( - guidance::AnnotateRoute(alternate_segment_list.Get(), facade)); - json_result.values["alternative_instructions"] = json_alternate_annotations_array; - } - - // generate names for both the main path and the alternative route - auto path_segments = BuildRouteSegments(segment_list); - auto alternate_segments = BuildRouteSegments(alternate_segment_list); - route_names = extractRouteNames(path_segments, alternate_segments, facade); - - util::json::Array json_alternate_names_array; - util::json::Array json_alternate_names; - json_alternate_names.values.push_back(route_names.alternative_path_name_1); - json_alternate_names.values.push_back(route_names.alternative_path_name_2); - json_alternate_names_array.values.emplace_back(std::move(json_alternate_names)); - json_result.values["alternative_names"] = json_alternate_names_array; - json_result.values["found_alternative"] = util::json::True(); - } - else - { - json_result.values["found_alternative"] = util::json::False(); - // generate names for the main route on its own - auto path_segments = BuildRouteSegments(segment_list); - std::vector alternate_segments; - route_names = extractRouteNames(path_segments, alternate_segments, facade); - } - - util::json::Array json_route_names; - json_route_names.values.push_back(route_names.shortest_path_name_1); - json_route_names.values.push_back(route_names.shortest_path_name_2); - json_result.values["route_name"] = json_route_names; - - json_result.values["hint_data"] = BuildHintData(raw_route); -} - -template -util::json::Object -ApiResponseGenerator::SummarizeRoute(const InternalRouteResult &raw_route, - const Segments &segment_list) const -{ - util::json::Object json_route_summary; - if (!raw_route.segment_end_coordinates.empty()) - { - const auto start_name_id = raw_route.segment_end_coordinates.front().source_phantom.name_id; - json_route_summary.values["start_point"] = facade->get_name_for_id(start_name_id); - const auto destination_name_id = - raw_route.segment_end_coordinates.back().target_phantom.name_id; - json_route_summary.values["end_point"] = facade->get_name_for_id(destination_name_id); - } - json_route_summary.values["total_time"] = segment_list.GetDuration(); - json_route_summary.values["total_distance"] = segment_list.GetDistance(); - return json_route_summary; -} - -template -util::json::Array -ApiResponseGenerator::ListViaPoints(const InternalRouteResult &raw_route) const -{ - util::json::Array json_via_points_array; - util::json::Array json_first_coordinate; - json_first_coordinate.values.emplace_back( - raw_route.segment_end_coordinates.front().source_phantom.location.lat / - COORDINATE_PRECISION); - json_first_coordinate.values.emplace_back( - raw_route.segment_end_coordinates.front().source_phantom.location.lon / - COORDINATE_PRECISION); - json_via_points_array.values.emplace_back(std::move(json_first_coordinate)); - for (const PhantomNodes &nodes : raw_route.segment_end_coordinates) - { - std::string tmp; - util::json::Array json_coordinate; - json_coordinate.values.emplace_back(nodes.target_phantom.location.lat / - COORDINATE_PRECISION); - json_coordinate.values.emplace_back(nodes.target_phantom.location.lon / - COORDINATE_PRECISION); - json_via_points_array.values.emplace_back(std::move(json_coordinate)); - } - return json_via_points_array; -} - -template -util::json::Array -ApiResponseGenerator::ListViaIndices(const Segments &segment_list) const -{ - util::json::Array via_indices; - via_indices.values.insert(via_indices.values.end(), segment_list.GetViaIndices().begin(), - segment_list.GetViaIndices().end()); - return via_indices; -} - -template -util::json::Value ApiResponseGenerator::GetGeometry(const bool return_encoded, - const Segments &segments) const -{ - if (return_encoded) - return polylineEncodeAsJSON(segments.Get()); - else - return polylineUnencodedAsJSON(segments.Get()); -} - -template -std::vector -ApiResponseGenerator::BuildRouteSegments(const Segments &segment_list) const -{ - std::vector result; - for (const auto &segment : segment_list.Get()) - { - const auto current_turn = segment.turn_instruction; - if (extractor::isTurnNecessary(current_turn) && - (extractor::TurnInstruction::EnterRoundAbout != current_turn)) - { - - detail::Segment seg = {segment.name_id, - static_cast(segment.length), - static_cast(result.size())}; - result.emplace_back(std::move(seg)); - } - } - return result; -} - -template -util::json::Object -ApiResponseGenerator::BuildHintData(const InternalRouteResult &raw_route) const -{ - util::json::Object json_hint_object; - json_hint_object.values["checksum"] = facade->GetCheckSum(); - util::json::Array json_location_hint_array; - std::string hint; - for (const auto i : util::irange(0, raw_route.segment_end_coordinates.size())) - { - hint = encodeBase64(raw_route.segment_end_coordinates[i].source_phantom); - json_location_hint_array.values.push_back(std::move(hint)); - } - hint = encodeBase64(raw_route.segment_end_coordinates.back().target_phantom); - json_location_hint_array.values.emplace_back(std::move(hint)); - json_hint_object.values["locations"] = json_location_hint_array; - - return json_hint_object; -} - -template -ApiResponseGenerator MakeApiResponseGenerator(DataFacadeT *facade) -{ - return ApiResponseGenerator(facade); -} - -} // namespace engine -} // namespace osrm - -#endif // ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_ diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index 54ec8c847..942ce0125 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -5,6 +5,7 @@ #include "extractor/edge_based_node.hpp" #include "extractor/external_memory_node.hpp" +#include "contractor/query_edge.hpp" #include "engine/phantom_node.hpp" #include "extractor/turn_instructions.hpp" #include "util/integer_range.hpp" @@ -29,11 +30,11 @@ namespace datafacade using EdgeRange = util::range; -template class BaseDataFacade +class BaseDataFacade { public: + using EdgeData = contractor::QueryEdge::EdgeData; using RTreeLeaf = extractor::EdgeBasedNode; - using EdgeData = EdgeDataT; BaseDataFacade() {} virtual ~BaseDataFacade() {} @@ -46,7 +47,7 @@ template class BaseDataFacade virtual NodeID GetTarget(const EdgeID e) const = 0; - virtual const EdgeDataT &GetEdgeData(const EdgeID e) const = 0; + virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0; virtual EdgeID BeginEdges(const NodeID n) const = 0; @@ -94,10 +95,22 @@ template class BaseDataFacade const int bearing = 0, const int bearing_range = 180) = 0; + virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate) = 0; + + virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate, const double max_distance) = 0; + virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent( const util::FixedPointCoordinate input_coordinate, - const int bearing = 0, - const int bearing_range = 180) = 0; + const double max_distance, + const int bearing, + const int bearing_range) = 0; + + virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate, + const int bearing, + const int bearing_range) = 0; virtual unsigned GetCheckSum() const = 0; diff --git a/include/engine/datafacade/internal_datafacade.hpp b/include/engine/datafacade/internal_datafacade.hpp index 6aac20823..a5c2e28b7 100644 --- a/include/engine/datafacade/internal_datafacade.hpp +++ b/include/engine/datafacade/internal_datafacade.hpp @@ -46,17 +46,17 @@ namespace engine namespace datafacade { -template class InternalDataFacade final : public BaseDataFacade +class InternalDataFacade final : public BaseDataFacade { private: - using super = BaseDataFacade; + using super = BaseDataFacade; using QueryGraph = util::StaticGraph; using InputEdge = typename QueryGraph::InputEdge; using RTreeLeaf = typename super::RTreeLeaf; using InternalRTree = util::StaticRTree::vector, false>; - using InternalGeospatialQuery = GeospatialQuery>; + using InternalGeospatialQuery = GeospatialQuery; InternalDataFacade() {} @@ -293,7 +293,7 @@ template class InternalDataFacade final : public BaseDataFacad NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); } - EdgeDataT &GetEdgeData(const EdgeID e) const override final + EdgeData &GetEdgeData(const EdgeID e) const override final { return m_query_graph->GetEdgeData(e); } @@ -386,10 +386,52 @@ template class InternalDataFacade final : public BaseDataFacad bearing_range); } + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate, const double max_distance) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate, max_distance); + } + + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate); + } + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( const util::FixedPointCoordinate input_coordinate, - const int bearing = 0, - const int bearing_range = 180) override final + const double max_distance, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get()) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate, max_distance, bearing, bearing_range); + } + + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate, + const int bearing, + const int bearing_range) override final { if (!m_static_rtree.get()) { diff --git a/include/engine/datafacade/shared_datafacade.hpp b/include/engine/datafacade/shared_datafacade.hpp index d0567d30a..793dc9605 100644 --- a/include/engine/datafacade/shared_datafacade.hpp +++ b/include/engine/datafacade/shared_datafacade.hpp @@ -36,12 +36,11 @@ namespace engine namespace datafacade { -template class SharedDataFacade final : public BaseDataFacade +class SharedDataFacade final : public BaseDataFacade { private: - using EdgeData = EdgeDataT; - using super = BaseDataFacade; + using super = BaseDataFacade; using QueryGraph = util::StaticGraph; using GraphNode = typename QueryGraph::NodeArrayEntry; using GraphEdge = typename QueryGraph::EdgeArrayEntry; @@ -50,7 +49,7 @@ template class SharedDataFacade final : public BaseDataFacade< using RTreeLeaf = typename super::RTreeLeaf; using SharedRTree = util::StaticRTree::vector, true>; - using SharedGeospatialQuery = GeospatialQuery>; + using SharedGeospatialQuery = GeospatialQuery; using TimeStampedRTreePair = std::pair>; using RTreeNode = typename SharedRTree::TreeNode; @@ -333,7 +332,7 @@ template class SharedDataFacade final : public BaseDataFacade< NodeID GetTarget(const EdgeID e) const override final { return m_query_graph->GetTarget(e); } - EdgeDataT &GetEdgeData(const EdgeID e) const override final + EdgeData &GetEdgeData(const EdgeID e) const override final { return m_query_graph->GetEdgeData(e); } @@ -454,10 +453,52 @@ template class SharedDataFacade final : public BaseDataFacade< bearing_range); } + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate); + } + + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate, const double max_distance) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate, max_distance); + } + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( const util::FixedPointCoordinate input_coordinate, - const int bearing = 0, - const int bearing_range = 180) override final + const double max_distance, + const int bearing, + const int bearing_range) override final + { + if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) + { + LoadRTree(); + BOOST_ASSERT(m_geospatial_query.get()); + } + + return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( + input_coordinate, max_distance, bearing, bearing_range); + } + + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate, + const int bearing, + const int bearing_range) override final { if (!m_static_rtree.get() || CURRENT_TIMESTAMP != m_static_rtree->first) { @@ -469,6 +510,7 @@ template class SharedDataFacade final : public BaseDataFacade< input_coordinate, bearing, bearing_range); } + unsigned GetCheckSum() const override final { return m_check_sum; } unsigned GetNameIndexFromEdgeID(const unsigned id) const override final diff --git a/include/engine/douglas_peucker.hpp b/include/engine/douglas_peucker.hpp index a100f0f04..279a449c4 100644 --- a/include/engine/douglas_peucker.hpp +++ b/include/engine/douglas_peucker.hpp @@ -1,7 +1,7 @@ #ifndef DOUGLAS_PEUCKER_HPP_ #define DOUGLAS_PEUCKER_HPP_ -#include "engine/segment_information.hpp" +#include "util/coordinate.hpp" #include #include @@ -44,14 +44,16 @@ const constexpr auto DOUGLAS_PEUCKER_THRESHOLDS_SIZE = // Input is vector of pairs. Each pair consists of the point information and a // bit indicating if the points is present in the generalization. // Note: points may also be pre-selected*/ -void douglasPeucker(std::vector::iterator begin, - std::vector::iterator end, - const unsigned zoom_level); +std::vector +douglasPeucker(std::vector::const_iterator begin, + std::vector::const_iterator end, + const unsigned zoom_level); // Convenience range-based function -inline void douglasPeucker(std::vector &geometry, const unsigned zoom_level) +inline std::vector +douglasPeucker(const std::vector &geometry, const unsigned zoom_level) { - douglasPeucker(begin(geometry), end(geometry), zoom_level); + return douglasPeucker(begin(geometry), end(geometry), zoom_level); } } } diff --git a/include/engine/engine.hpp b/include/engine/engine.hpp index 1ca8ad609..ee75c5b69 100644 --- a/include/engine/engine.hpp +++ b/include/engine/engine.hpp @@ -1,10 +1,9 @@ #ifndef ENGINE_HPP #define ENGINE_HPP -#include "contractor/query_edge.hpp" - -#include "osrm/json_container.hpp" -#include "osrm/osrm.hpp" +#include "engine/status.hpp" +#include "storage/shared_barriers.hpp" +#include "util/json_container.hpp" #include #include @@ -13,11 +12,6 @@ namespace osrm { -namespace storage -{ -struct SharedBarriers; -} - namespace util { namespace json @@ -29,41 +23,43 @@ struct Object; namespace engine { struct EngineConfig; +namespace api +{ struct RouteParameters; +} namespace plugins { -class BasePlugin; +class ViaRoutePlugin; } namespace datafacade { -template class BaseDataFacade; +class BaseDataFacade; } class Engine final { - private: - using PluginMap = std::unordered_map>; - public: + struct EngineLock + { + // will only be initialized if shared memory is used + storage::SharedBarriers barrier; + // decrease number of concurrent queries + void DecreaseQueryCount(); + // increase number of concurrent queries + void IncreaseQueryCount(); + }; + Engine(EngineConfig &config_); Engine(const Engine &) = delete; Engine &operator=(const Engine &) = delete; - int RunQuery(const RouteParameters &route_parameters, util::json::Object &json_result); + Status Route(const api::RouteParameters &route_parameters, util::json::Object &json_result); private: - void RegisterPlugin(plugins::BasePlugin *plugin); - PluginMap plugin_map; - // will only be initialized if shared memory is used - std::unique_ptr barrier; - // base class pointer to the objects - datafacade::BaseDataFacade *query_data_facade; - - // decrease number of concurrent queries - void decrease_concurrent_query_count(); - // increase number of concurrent queries - void increase_concurrent_query_count(); + std::unique_ptr lock; + std::unique_ptr route_plugin; + std::unique_ptr query_data_facade; }; } } diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp index 75972366c..9477d9f00 100644 --- a/include/engine/geospatial_query.hpp +++ b/include/engine/geospatial_query.hpp @@ -84,16 +84,86 @@ template class GeospatialQuery // Returns the nearest phantom node. If this phantom node is not from a big component // a second phantom node is return that is the nearest coordinate in a big component. std::pair NearestPhantomNodeWithAlternativeFromBigComponent( - const util::FixedPointCoordinate input_coordinate, - const int bearing = 0, - const int bearing_range = 180) + const util::FixedPointCoordinate input_coordinate, const double max_distance) { bool has_small_component = false; bool has_big_component = false; auto results = rtree.Nearest( input_coordinate, - [this, bearing, bearing_range, &has_big_component, &has_small_component]( - const EdgeData &data) + [&has_big_component, &has_small_component](const EdgeData &data) + { + auto use_segment = + (!has_small_component || (!has_big_component && !data.component.is_tiny)); + auto use_directions = std::make_pair(use_segment, use_segment); + + has_big_component = has_big_component || !data.component.is_tiny; + has_small_component = has_small_component || data.component.is_tiny; + + return use_directions; + }, + [&has_big_component, max_distance](const std::size_t num_results, const double min_dist) + { + return (num_results > 0 && has_big_component) || min_dist > max_distance; + }); + + if (results.size() == 0) + { + return std::make_pair(PhantomNode{}, PhantomNode{}); + } + + BOOST_ASSERT(results.size() == 1 || results.size() == 2); + return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, + MakePhantomNode(input_coordinate, results.back()).phantom_node); + } + + // Returns the nearest phantom node. If this phantom node is not from a big component + // a second phantom node is return that is the nearest coordinate in a big component. + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate) + { + bool has_small_component = false; + bool has_big_component = false; + auto results = + rtree.Nearest(input_coordinate, + [&has_big_component, &has_small_component](const EdgeData &data) + { + auto use_segment = (!has_small_component || + (!has_big_component && !data.component.is_tiny)); + auto use_directions = std::make_pair(use_segment, use_segment); + + has_big_component = has_big_component || !data.component.is_tiny; + has_small_component = has_small_component || data.component.is_tiny; + + return use_directions; + }, + [&has_big_component](const std::size_t num_results, const double) + { + return num_results > 0 && has_big_component; + }); + + if (results.size() == 0) + { + return std::make_pair(PhantomNode{}, PhantomNode{}); + } + + BOOST_ASSERT(results.size() == 1 || results.size() == 2); + return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, + MakePhantomNode(input_coordinate, results.back()).phantom_node); + } + + // Returns the nearest phantom node. If this phantom node is not from a big component + // a second phantom node is return that is the nearest coordinate in a big component. + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate, + const int bearing, + const int bearing_range) + { + bool has_small_component = false; + bool has_big_component = false; + auto results = rtree.Nearest( + input_coordinate, + [this, bearing, bearing_range, &has_big_component, + &has_small_component](const EdgeData &data) { auto use_segment = (!has_small_component || (!has_big_component && !data.component.is_tiny)); @@ -126,6 +196,52 @@ template class GeospatialQuery MakePhantomNode(input_coordinate, results.back()).phantom_node); } + // Returns the nearest phantom node. If this phantom node is not from a big component + // a second phantom node is return that is the nearest coordinate in a big component. + std::pair NearestPhantomNodeWithAlternativeFromBigComponent( + const util::FixedPointCoordinate input_coordinate, + const double max_distance, + const int bearing, + const int bearing_range) + { + bool has_small_component = false; + bool has_big_component = false; + auto results = rtree.Nearest( + input_coordinate, + [this, bearing, bearing_range, &has_big_component, + &has_small_component](const EdgeData &data) + { + auto use_segment = + (!has_small_component || (!has_big_component && !data.component.is_tiny)); + auto use_directions = std::make_pair(use_segment, use_segment); + + if (use_segment) + { + use_directions = checkSegmentBearing(data, bearing, bearing_range); + if (use_directions.first || use_directions.second) + { + has_big_component = has_big_component || !data.component.is_tiny; + has_small_component = has_small_component || data.component.is_tiny; + } + } + + return use_directions; + }, + [&has_big_component, max_distance](const std::size_t num_results, const double min_dist) + { + return (num_results > 0 && has_big_component) || min_dist > max_distance; + }); + + if (results.size() == 0) + { + return std::make_pair(PhantomNode{}, PhantomNode{}); + } + + BOOST_ASSERT(results.size() > 0); + return std::make_pair(MakePhantomNode(input_coordinate, results.front()).phantom_node, + MakePhantomNode(input_coordinate, results.back()).phantom_node); + } + private: std::vector MakePhantomNodes(const util::FixedPointCoordinate input_coordinate, diff --git a/include/engine/guidance/assemble_geometry.hpp b/include/engine/guidance/assemble_geometry.hpp new file mode 100644 index 000000000..e123c2ee2 --- /dev/null +++ b/include/engine/guidance/assemble_geometry.hpp @@ -0,0 +1,78 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP +#define ENGINE_GUIDANCE_ASSEMBLE_GEOMETRY_HPP + +#include "engine/internal_route_result.hpp" +#include "engine/phantom_node.hpp" +#include "engine/guidance/route_step.hpp" +#include "engine/guidance/leg_geometry.hpp" +#include "util/coordinate_calculation.hpp" +#include "util/coordinate.hpp" +#include "extractor/turn_instructions.hpp" +#include "extractor/travel_mode.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +// Extracts the geometry for each segment and calculates the traveled distance +// Combines the geometry form the phantom node with the PathData +// to the full route geometry. +// +// turn 0 1 2 3 4 +// s...x...y...z...t +// |---|segment 0 +// |---| segment 1 +// |---| segment 2 +// |---| segment 3 +template +LegGeometry assembleGeometry(const DataFacadeT &facade, + const std::vector &leg_data, + const PhantomNode &source_node, + const PhantomNode &target_node) +{ + LegGeometry geometry; + + // segment 0 first and last + geometry.segment_offsets.push_back(0); + geometry.locations.push_back(source_node.location); + + auto current_distance = 0.; + auto prev_coordinate = geometry.locations.front(); + for (const auto &path_point : leg_data) + { + auto coordinate = facade.GetCoordinateOfNode(path_point.turn_via_node); + current_distance += + util::coordinate_calculation::haversineDistance(prev_coordinate, coordinate); + + if (path_point.turn_instruction != extractor::TurnInstruction::NoTurn) + { + geometry.segment_distances.push_back(current_distance); + geometry.segment_offsets.push_back(geometry.locations.size()); + current_distance = 0.; + } + + prev_coordinate = coordinate; + geometry.locations.push_back(std::move(coordinate)); + } + current_distance += + util::coordinate_calculation::haversineDistance(prev_coordinate, target_node.location); + // segment leading to the target node + geometry.segment_distances.push_back(current_distance); + geometry.segment_offsets.push_back(geometry.locations.size()); + geometry.locations.push_back(target_node.location); + + BOOST_ASSERT(geometry.segment_distances.size() == geometry.segment_offsets.size() - 1); + BOOST_ASSERT(geometry.locations.size() > geometry.segment_distances.size()); + + return geometry; +} +} +} +} + +#endif diff --git a/include/engine/guidance/assemble_leg.hpp b/include/engine/guidance/assemble_leg.hpp new file mode 100644 index 000000000..fc91aac7b --- /dev/null +++ b/include/engine/guidance/assemble_leg.hpp @@ -0,0 +1,165 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_ +#define ENGINE_GUIDANCE_ASSEMBLE_LEG_HPP_ + +#include "engine/guidance/route_leg.hpp" +#include "engine/guidance/route_step.hpp" +#include "engine/guidance/leg_geometry.hpp" +#include "engine/internal_route_result.hpp" + +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ +namespace detail +{ +const constexpr std::size_t MAX_USED_SEGMENTS = 2; +struct NamedSegment +{ + double duration; + std::uint32_t position; + std::uint32_t name_id; +}; + +template +std::array summarizeRoute(const std::vector &route_data) +{ + // merges segments with same name id + const auto collapse_segments = [](std::vector &segments) + { + auto out = segments.begin(); + auto end = segments.end(); + for (auto in = segments.begin(); in != end; ++in) + { + if (in->name_id == out->name_id) + { + out->duration += in->duration; + } + else + { + ++out; + BOOST_ASSERT(out != end); + *out = *in; + } + } + return out; + }; + + std::vector segments(route_data.size()); + std::uint32_t index = 0; + std::transform( + route_data.begin(), route_data.end(), segments.begin(), [&index](const PathData &point) + { + return NamedSegment{point.duration_until_turn / 10.0, index++, point.name_id}; + }); + // this makes sure that the segment with the lowest position comes first + std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs) + { + return lhs.name_id < rhs.name_id || + (lhs.name_id == rhs.name_id && lhs.position < rhs.position); + }); + auto new_end = collapse_segments(segments); + segments.resize(new_end - segments.begin()); + // sort descending + std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs) + { + return lhs.duration > rhs.duration; + }); + + // make sure the segments are sorted by position + segments.resize(std::min(segments.size(), SegmentNumber)); + std::sort(segments.begin(), segments.end(), [](const NamedSegment &lhs, const NamedSegment &rhs) + { + return lhs.position < rhs.position; + }); + + std::array summary; + std::fill(summary.begin(), summary.end(), 0); + std::transform(segments.begin(), segments.end(), summary.begin(), + [](const NamedSegment &segment) + { + return segment.name_id; + }); + return summary; +} +} + +template +RouteLeg assembleLeg(const DataFacadeT &facade, + const std::vector &route_data, + const LegGeometry &leg_geometry, + const PhantomNode &source_node, + const PhantomNode &target_node, + const bool source_traversed_in_reverse, + const bool target_traversed_in_reverse) +{ + const auto source_duration = + (source_traversed_in_reverse ? source_node.GetReverseWeightPlusOffset() + : source_node.GetForwardWeightPlusOffset()) / + 10.; + const auto target_duration = + (target_traversed_in_reverse ? target_node.GetReverseWeightPlusOffset() + : target_node.GetForwardWeightPlusOffset()) / + 10.; + + auto distance = std::accumulate(leg_geometry.segment_distances.begin(), + leg_geometry.segment_distances.end(), 0.); + auto duration = std::accumulate(route_data.begin(), route_data.end(), 0., + [](const double sum, const PathData &data) + { + return sum + data.duration_until_turn; + }) / + 10.; + + // s + // | + // Given a route a---b---c where there is a right turn at b. + // | + // d + // |--t + // e + // (a, b, c) gets compressed to (a,c) + // (c, d, e) gets compressed to (c,e) + // The duration of the turn (a,c) -> (c,e) will be the duration of (a,c) (e.g. the duration + // of (a,b,c)). + // The phantom node of s will contain: + // `forward_weight`: duration of (a,s) + // `forward_offset`: 0 (its the first segment) + // The phantom node of t will contain: + // `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 + // 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) + duration = duration - source_duration + target_duration; + auto summary_array = detail::summarizeRoute(route_data); + + BOOST_ASSERT(detail::MAX_USED_SEGMENTS > 0); + BOOST_ASSERT(summary_array.begin() != summary_array.end()); + std::string summary = + std::accumulate(std::next(summary_array.begin()), summary_array.end(), + facade.get_name_for_id(summary_array.front()), + [&facade](std::string previous, const std::uint32_t name_id) + { + if (name_id != 0) + { + previous += ", " + facade.get_name_for_id(name_id); + } + return previous; + }); + + return RouteLeg{duration, distance, summary, {}}; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_ diff --git a/include/engine/guidance/assemble_overview.hpp b/include/engine/guidance/assemble_overview.hpp new file mode 100644 index 000000000..c3bc85230 --- /dev/null +++ b/include/engine/guidance/assemble_overview.hpp @@ -0,0 +1,22 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP +#define ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP + +#include "engine/guidance/leg_geometry.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +std::vector +assembleOverview(const std::vector &leg_geometries, const bool use_simplification); + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif diff --git a/include/engine/guidance/assemble_route.hpp b/include/engine/guidance/assemble_route.hpp new file mode 100644 index 000000000..360d8e4d6 --- /dev/null +++ b/include/engine/guidance/assemble_route.hpp @@ -0,0 +1,37 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_ROUTE_HPP +#define ENGINE_GUIDANCE_ASSEMBLE_ROUTE_HPP + +#include "engine/guidance/route_leg.hpp" +#include "engine/guidance/route.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + inline Route assembleRoute(const std::vector &route_legs) + { + auto distance = std::accumulate(route_legs.begin(), + route_legs.end(), 0., + [](const double sum, const RouteLeg &leg) + { + return sum + leg.distance; + }); + auto duration = std::accumulate(route_legs.begin(), route_legs.end(), 0., + [](const double sum, const RouteLeg &leg) + { + return sum + leg.duration; + }); + + return Route{duration, distance}; + } + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp new file mode 100644 index 000000000..baa422706 --- /dev/null +++ b/include/engine/guidance/assemble_steps.hpp @@ -0,0 +1,135 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_ +#define ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_ + +#include "engine/guidance/route_step.hpp" +#include "engine/guidance/step_maneuver.hpp" +#include "engine/guidance/leg_geometry.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 "extractor/travel_mode.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ +namespace detail +{ +// FIXME move implementation to cpp +inline StepManeuver stepManeuverFromGeometry(const extractor::TurnInstruction instruction, + const LegGeometry &leg_geometry, + std::size_t segment_index) +{ + auto turn_index = leg_geometry.BackIndex(segment_index); + BOOST_ASSERT(turn_index > 0); + BOOST_ASSERT(turn_index < leg_geometry.locations.size() - 1); + + // TODO chose a bigger look-a-head to smooth complex geometry + const auto pre_turn_coordinate = leg_geometry.locations[turn_index - 1]; + const auto turn_coordinate = leg_geometry.locations[turn_index]; + const auto post_turn_coordinate = leg_geometry.locations[turn_index + 1]; + + const double pre_turn_heading = + util::coordinate_calculation::bearing(pre_turn_coordinate, turn_coordinate); + const double post_turn_heading = + util::coordinate_calculation::bearing(turn_coordinate, post_turn_coordinate); + + return StepManeuver{turn_coordinate, pre_turn_heading, post_turn_heading, instruction}; +} +} + +template +std::vector assembleSteps(const DataFacadeT &facade, + const std::vector &leg_data, + const LegGeometry &leg_geometry, + const PhantomNode &source_node, + const PhantomNode &target_node, + const bool source_traversed_in_reverse, + const bool target_traversed_in_reverse) +{ + const auto source_duration = + (source_traversed_in_reverse ? source_node.GetReverseWeightPlusOffset() + : source_node.GetForwardWeightPlusOffset()) / + 10.; + const auto source_mode = source_traversed_in_reverse ? source_node.backward_travel_mode + : source_node.forward_travel_mode; + + const auto target_duration = + (target_traversed_in_reverse ? target_node.GetReverseWeightPlusOffset() + : target_node.GetForwardWeightPlusOffset()) / + 10.; + const auto target_mode = target_traversed_in_reverse ? target_node.backward_travel_mode + : target_node.forward_travel_mode; + + const auto number_of_segments = leg_geometry.GetNumberOfSegments(); + + std::vector steps; + steps.reserve(number_of_segments); + + auto segment_index = 0; + if (leg_data.size() > 0) + { + StepManeuver maneuver = detail::stepManeuverFromGeometry(extractor::TurnInstruction::StartAtEndOfStreet, + leg_geometry, segment_index); + + // 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 + for (const auto &path_point : leg_data) + { + if (path_point.turn_instruction != extractor::TurnInstruction::NoTurn) + { + 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); + segment_index++; + } + } + 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, + leg_geometry.FrontIndex(segment_index), + leg_geometry.BackIndex(segment_index) + 1}); + } + else + { + // + // |-----s source_duration + // |-------------t target_duration + // x---*---*---*---z compressed edge + // |-------| duration + 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}); + } + + 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}, + leg_geometry.locations.size(), leg_geometry.locations.size()}); + + return steps; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_ diff --git a/include/engine/guidance/leg_geometry.hpp b/include/engine/guidance/leg_geometry.hpp new file mode 100644 index 000000000..71aaa5d22 --- /dev/null +++ b/include/engine/guidance/leg_geometry.hpp @@ -0,0 +1,54 @@ +#ifndef ENGINE_GUIDANCE_LEG_GEOMETRY_HPP +#define ENGINE_GUIDANCE_LEG_GEOMETRY_HPP + +#include "util/coordinate.hpp" +#include "util/integer_range.hpp" + +#include +#include + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +// locations 0---1---2-...-n-1---n +// turns s x y t +// segment | 0 | 1 | 2 | senitel +// offsets 0 2 n-1 n +struct LegGeometry +{ + std::vector locations; + // segment_offset[i] .. segment_offset[i+1] (inclusive) + // contains the geometry of segment i + std::vector segment_offsets; + // length of the segment in meters + std::vector segment_distances; + + std::size_t FrontIndex(std::size_t segment_index) const + { + return segment_offsets[segment_index]; + } + + std::size_t BackIndex(std::size_t segment_index) const + { + return segment_offsets[segment_index + 1]; + } + + std::size_t GetNumberOfSegments() const + { + BOOST_ASSERT(segment_offsets.size() > 0); + return segment_offsets.size() - 1; + } + +}; +} +} +} + +#endif diff --git a/include/engine/guidance/route.hpp b/include/engine/guidance/route.hpp new file mode 100644 index 000000000..e75162cd0 --- /dev/null +++ b/include/engine/guidance/route.hpp @@ -0,0 +1,18 @@ +#ifndef ROUTE_HPP +#define ROUTE_HPP + +namespace osrm { +namespace engine { +namespace guidance { + +struct Route +{ + double duration; + double distance; +}; + +} +} +} + +#endif diff --git a/include/engine/guidance/route_leg.hpp b/include/engine/guidance/route_leg.hpp new file mode 100644 index 000000000..3e449ee5b --- /dev/null +++ b/include/engine/guidance/route_leg.hpp @@ -0,0 +1,30 @@ +#ifndef ROUTE_LEG_HPP +#define ROUTE_LEG_HPP + +#include "engine/guidance/route_step.hpp" + +#include + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +struct RouteLeg +{ + double duration; + double distance; + std::string summary; + std::vector steps; +}; + +} +} +} + +#endif diff --git a/include/engine/guidance/route_step.hpp b/include/engine/guidance/route_step.hpp new file mode 100644 index 000000000..b514c8a47 --- /dev/null +++ b/include/engine/guidance/route_step.hpp @@ -0,0 +1,39 @@ +#ifndef ROUTE_STEP_HPP +#define ROUTE_STEP_HPP + +#include "extractor/travel_mode.hpp" +#include "engine/guidance/step_maneuver.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ +// Given the following turn from a,b to b,c over b: +// a --> b --> c +// this struct saves the information of the segment b,c. +// Notable exceptions are Departure and Arrival steps. +// Departue: s --> a --> b. Represents the segment s,a with location being s. +// Arrive: a --> b --> t. The segment (b,t) is already covered by the previous segment. +struct RouteStep +{ + unsigned name_id; + std::string way_name; + double duration; + double distance; + extractor::TravelMode mode; + StepManeuver maneuver; + // indices into the locations array stored the LegGeometry + std::size_t geometry_begin; + std::size_t geometry_end; +}; + +} +} +} + +#endif diff --git a/include/engine/guidance/segment_list.hpp b/include/engine/guidance/segment_list.hpp deleted file mode 100644 index 0d7270121..000000000 --- a/include/engine/guidance/segment_list.hpp +++ /dev/null @@ -1,347 +0,0 @@ -#ifndef ENGINE_GUIDANCE_SEGMENT_LIST_HPP_ -#define ENGINE_GUIDANCE_SEGMENT_LIST_HPP_ - -#include "osrm/coordinate.hpp" - -#include "engine/douglas_peucker.hpp" -#include "engine/internal_route_result.hpp" -#include "engine/phantom_node.hpp" -#include "engine/segment_information.hpp" -#include "util/integer_range.hpp" -#include "util/coordinate_calculation.hpp" -#include "util/for_each_pair.hpp" -#include "extractor/turn_instructions.hpp" - -#include - -#include -#include -#include -#include -#include - -// transfers the internal edge based data structures to a more useable format -namespace osrm -{ -namespace engine -{ -namespace guidance -{ -template class SegmentList -{ - public: - using DataFacade = DataFacadeT; - SegmentList(const InternalRouteResult &raw_route, - const bool extract_alternative, - const unsigned zoom_level, - const bool allow_simplification, - const DataFacade *facade); - - const std::vector &GetViaIndices() const; - std::uint32_t GetDistance() const; - std::uint32_t GetDuration() const; - - const std::vector &Get() const; - - private: - void InitRoute(const PhantomNode &phantom_node, const bool traversed_in_reverse); - void AddLeg(const std::vector &leg_data, - const PhantomNode &target_node, - const bool traversed_in_reverse, - const bool is_via_leg, - const DataFacade *facade); - - void AppendSegment(const FixedPointCoordinate coordinate, const PathData &path_point); - void Finalize(const bool extract_alternative, - const InternalRouteResult &raw_route, - const unsigned zoom_level, - const bool allow_simplification); - - // journey length in tenth of a second - std::uint32_t total_distance; - // journey distance in meter (TODO: verify) - std::uint32_t total_duration; - - // segments that are required to keep - std::vector via_indices; - - // a list of node based segments - std::vector segments; -}; - -template -SegmentList::SegmentList(const InternalRouteResult &raw_route, - const bool extract_alternative, - const unsigned zoom_level, - const bool allow_simplification, - const DataFacade *facade) - : total_distance(0), total_duration(0) -{ - if (!raw_route.is_valid()) - { - return; - } - - if (extract_alternative) - { - BOOST_ASSERT(raw_route.has_alternative()); - InitRoute(raw_route.segment_end_coordinates.front().source_phantom, - raw_route.alt_source_traversed_in_reverse.front()); - AddLeg(raw_route.unpacked_alternative, - raw_route.segment_end_coordinates.back().target_phantom, - raw_route.alt_source_traversed_in_reverse.back(), false, facade); - } - else - { - InitRoute(raw_route.segment_end_coordinates.front().source_phantom, - raw_route.source_traversed_in_reverse.front()); - for (std::size_t raw_index = 0; raw_index < raw_route.segment_end_coordinates.size(); - ++raw_index) - { - AddLeg(raw_route.unpacked_path_segments[raw_index], - raw_route.segment_end_coordinates[raw_index].target_phantom, - raw_route.target_traversed_in_reverse[raw_index], - raw_route.is_via_leg(raw_index), facade); - if (raw_route.is_via_leg(raw_index)) - { - const auto &source_phantom = - raw_route.segment_end_coordinates[raw_index].target_phantom; - if (raw_route.target_traversed_in_reverse[raw_index] != - raw_route.source_traversed_in_reverse[raw_index + 1]) - { - bool traversed_in_reverse = raw_route.target_traversed_in_reverse[raw_index]; - const extractor::TravelMode travel_mode = - (traversed_in_reverse ? source_phantom.backward_travel_mode - : source_phantom.forward_travel_mode); - const bool constexpr IS_NECESSARY = true; - const bool constexpr IS_VIA_LOCATION = true; - segments.emplace_back(source_phantom.location, source_phantom.name_id, 0, 0.f, - extractor::TurnInstruction::UTurn, IS_NECESSARY, - IS_VIA_LOCATION, travel_mode); - } - } - } - } - - if (!allow_simplification) - { - // to prevent any simplifications, we mark all segments as necessary - for (auto &segment : segments) - { - segment.necessary = true; - } - } - - Finalize(extract_alternative, raw_route, zoom_level, allow_simplification); -} - -template -void SegmentList::InitRoute(const PhantomNode &node, const bool traversed_in_reverse) -{ - const auto segment_duration = - (traversed_in_reverse ? node.reverse_weight : node.forward_weight); - const auto travel_mode = - (traversed_in_reverse ? node.backward_travel_mode : node.forward_travel_mode); - - AppendSegment(node.location, PathData(0, node.name_id, extractor::TurnInstruction::HeadOn, - segment_duration, travel_mode)); -} - -template -void SegmentList::AddLeg(const std::vector &leg_data, - const PhantomNode &target_node, - const bool traversed_in_reverse, - const bool is_via_leg, - const DataFacade *facade) -{ - for (const auto &path_data : leg_data) - { - AppendSegment(facade->GetCoordinateOfNode(path_data.node), path_data); - } - - const EdgeWeight segment_duration = - (traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight); - const extractor::TravelMode travel_mode = - (traversed_in_reverse ? target_node.backward_travel_mode : target_node.forward_travel_mode); - const bool constexpr IS_NECESSARY = true; - const bool constexpr IS_VIA_LOCATION = true; - segments.emplace_back(target_node.location, target_node.name_id, segment_duration, 0.f, - is_via_leg ? extractor::TurnInstruction::ReachViaLocation - : extractor::TurnInstruction::NoTurn, - IS_NECESSARY, IS_VIA_LOCATION, travel_mode); -} - -template std::uint32_t SegmentList::GetDistance() const -{ - return total_distance; -} -template std::uint32_t SegmentList::GetDuration() const -{ - return total_duration; -} - -template -std::vector const &SegmentList::GetViaIndices() const -{ - return via_indices; -} - -template -std::vector const &SegmentList::Get() const -{ - return segments; -} - -template -void SegmentList::AppendSegment(const FixedPointCoordinate coordinate, - const PathData &path_point) -{ - const auto turn = path_point.turn_instruction; - - segments.emplace_back(coordinate, path_point.name_id, path_point.segment_duration, 0.f, turn, - path_point.travel_mode); -} - -template -void SegmentList::Finalize(const bool extract_alternative, - const InternalRouteResult &raw_route, - const unsigned zoom_level, - const bool allow_simplification) -{ - if (segments.empty()) - return; - - // check if first two segments can be merged - BOOST_ASSERT(segments.size() >= 2); - if (segments[0].location == segments[1].location && - segments[1].turn_instruction == extractor::TurnInstruction::NoTurn) - { - segments[0].travel_mode = segments[1].travel_mode; - segments[0].name_id = segments[1].name_id; - // Other data?? - segments.erase(segments.begin() + 1); - } - - // announce mode changes - for (std::size_t i = 0; i + 1 < segments.size(); ++i) - { - auto &segment = segments[i]; - const auto next_mode = segments[i + 1].travel_mode; - if (segment.travel_mode != next_mode && - segment.turn_instruction == extractor::TurnInstruction::NoTurn) - { - segment.turn_instruction = extractor::TurnInstruction::GoStraight; - segment.necessary = true; - } - } - - segments[0].length = 0.f; - for (const auto i : util::irange(1, segments.size())) - { - // move down names by one, q&d hack - segments[i - 1].name_id = segments[i].name_id; - segments[i].length = util::coordinate_calculation::greatCircleDistance( - segments[i - 1].location, segments[i].location); - } - - float segment_length = 0.; - EdgeWeight segment_duration = 0; - std::size_t segment_start_index = 0; - - double path_length = 0; - - for (const auto i : util::irange(1, segments.size())) - { - path_length += segments[i].length; - segment_length += segments[i].length; - segment_duration += segments[i].duration; - segments[segment_start_index].length = segment_length; - segments[segment_start_index].duration = segment_duration; - - if (extractor::TurnInstruction::NoTurn != segments[i].turn_instruction) - { - BOOST_ASSERT(segments[i].necessary); - segment_length = 0; - segment_duration = 0; - segment_start_index = i; - - if (segments[i].turn_instruction == extractor::TurnInstruction::NameChanges) - { - segments[i].turn_instruction = - extractor::TurnInstruction::GoStraight; // to not break the api - } - } - } - - total_distance = static_cast(std::round(path_length)); - total_duration = static_cast(std::round( - (extract_alternative ? raw_route.alternative_path_length : raw_route.shortest_path_length) / - 10.)); - - // Post-processing to remove empty or nearly empty path segments - if (segments.size() > 2 && std::numeric_limits::epsilon() > segments.back().length && - !(segments.end() - 2)->is_via_location) - { - segments.pop_back(); - segments.back().necessary = true; - segments.back().turn_instruction = extractor::TurnInstruction::NoTurn; - } - - if (segments.size() > 2 && std::numeric_limits::epsilon() > segments.front().length && - !(segments.begin() + 1)->is_via_location) - { - segments.erase(segments.begin()); - segments.front().turn_instruction = extractor::TurnInstruction::HeadOn; - segments.front().necessary = true; - } - - if (allow_simplification) - { - douglasPeucker(segments, zoom_level); - } - - std::uint32_t necessary_segments = 0; // a running index that counts the necessary pieces - via_indices.push_back(0); - const auto markNecessarySegments = [this, &necessary_segments](SegmentInformation &first, - const SegmentInformation &second) - { - if (!first.necessary) - return; - - // mark the end of a leg (of several segments) - if (first.is_via_location) - via_indices.push_back(necessary_segments); - - const double post_turn_bearing = - util::coordinate_calculation::bearing(first.location, second.location); - const double pre_turn_bearing = - util::coordinate_calculation::bearing(second.location, first.location); - first.post_turn_bearing = static_cast(post_turn_bearing * 10); - first.pre_turn_bearing = static_cast(pre_turn_bearing * 10); - - ++necessary_segments; - }; - - // calculate which segments are necessary and update segments for bearings - util::for_each_pair(segments, markNecessarySegments); - via_indices.push_back(necessary_segments); - - BOOST_ASSERT(via_indices.size() >= 2); -} - -template -SegmentList MakeSegmentList(const InternalRouteResult &raw_route, - const bool extract_alternative, - const unsigned zoom_level, - const bool allow_simplification, - const DataFacadeT *facade) -{ - return SegmentList(raw_route, extract_alternative, zoom_level, - allow_simplification, facade); -} - -} // namespace guidance -} // namespace engine -} // namespace osrm - -#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_ diff --git a/include/engine/guidance/step_maneuver.hpp b/include/engine/guidance/step_maneuver.hpp new file mode 100644 index 000000000..a441f80e7 --- /dev/null +++ b/include/engine/guidance/step_maneuver.hpp @@ -0,0 +1,26 @@ +#ifndef ENGINE_GUIDANCE_STEP_MANEUVER_HPP +#define ENGINE_GUIDANCE_STEP_MANEUVER_HPP + +#include "util/coordinate.hpp" +#include "extractor/turn_instructions.hpp" + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ + +struct StepManeuver +{ + util::FixedPointCoordinate location; + double heading_before; + double heading_after; + extractor::TurnInstruction instruction; +}; + +} +} +} +#endif + diff --git a/include/engine/guidance/textual_route_annotation.hpp b/include/engine/guidance/textual_route_annotation.hpp deleted file mode 100644 index e9fe49691..000000000 --- a/include/engine/guidance/textual_route_annotation.hpp +++ /dev/null @@ -1,147 +0,0 @@ -#ifndef ENGINE_GUIDANCE_TEXTUAL_ROUTE_ANNOTATIONS_HPP_ -#define ENGINE_GUIDANCE_TEXTUAL_ROUTE_ANNOTATIONS_HPP_ - -#include "engine/segment_information.hpp" -#include "engine/guidance/segment_list.hpp" -#include "extractor/turn_instructions.hpp" -#include "osrm/json_container.hpp" -#include "util/bearing.hpp" -#include "util/cast.hpp" - -#include - -#include -#include -#include -#include - -namespace osrm -{ -namespace engine -{ -namespace guidance -{ -template -inline util::json::Array AnnotateRoute(const std::vector &route_segments, - DataFacadeT *facade) -{ - util::json::Array json_instruction_array; - if (route_segments.empty()) - return json_instruction_array; - // Segment information has following format: - //["instruction id","streetname",length,position,time,"length","earth_direction",azimuth] - std::int32_t necessary_segments_running_index = 0; - - struct RoundAbout - { - std::int32_t start_index; - std::uint32_t name_id; - std::int32_t leave_at_exit; - } round_about; - - round_about = {std::numeric_limits::max(), 0, 0}; - std::string temp_dist, temp_length, temp_duration, temp_bearing, temp_instruction; - extractor::TravelMode last_travel_mode = TRAVEL_MODE_DEFAULT; - - // Generate annotations for every segment - for (std::size_t i = 0; i < route_segments.size(); ++i) - { - const auto &segment = route_segments[i]; - util::json::Array json_instruction_row; - extractor::TurnInstruction current_instruction = segment.turn_instruction; - if (extractor::isTurnNecessary(current_instruction)) - { - if (extractor::TurnInstruction::EnterRoundAbout == current_instruction) - { - round_about.name_id = segment.name_id; - round_about.start_index = necessary_segments_running_index; - } - else - { - std::string current_turn_instruction; - if (extractor::TurnInstruction::LeaveRoundAbout == current_instruction) - { - temp_instruction = std::to_string(util::cast::enum_to_underlying( - extractor::TurnInstruction::EnterRoundAbout)); - current_turn_instruction += temp_instruction; - current_turn_instruction += "-"; - temp_instruction = std::to_string(round_about.leave_at_exit + 1); - current_turn_instruction += temp_instruction; - round_about.leave_at_exit = 0; - } - else - { - temp_instruction = - std::to_string(util::cast::enum_to_underlying(current_instruction)); - current_turn_instruction += temp_instruction; - } - json_instruction_row.values.emplace_back(std::move(current_turn_instruction)); - - json_instruction_row.values.push_back(facade->get_name_for_id(segment.name_id)); - json_instruction_row.values.push_back(std::round(segment.length)); - json_instruction_row.values.push_back(necessary_segments_running_index); - json_instruction_row.values.push_back(std::round(segment.duration / 10.)); - json_instruction_row.values.push_back( - std::to_string(static_cast(segment.length)) + "m"); - - // post turn bearing - const double post_turn_bearing_value = (segment.post_turn_bearing / 10.); - json_instruction_row.values.push_back(util::bearing::get(post_turn_bearing_value)); - json_instruction_row.values.push_back( - static_cast(std::round(post_turn_bearing_value))); - - if (i + 1 < route_segments.size()) - { - // anounce next travel mode with turn - json_instruction_row.values.push_back(route_segments[i + 1].travel_mode); - last_travel_mode = segment.travel_mode; - } - else - { - json_instruction_row.values.push_back(segment.travel_mode); - last_travel_mode = segment.travel_mode; - } - - // pre turn bearing - const double pre_turn_bearing_value = (segment.pre_turn_bearing / 10.); - json_instruction_row.values.push_back(util::bearing::get(pre_turn_bearing_value)); - json_instruction_row.values.push_back( - static_cast(std::round(pre_turn_bearing_value))); - - json_instruction_array.values.push_back(json_instruction_row); - } - } - else if (extractor::TurnInstruction::StayOnRoundAbout == current_instruction) - { - ++round_about.leave_at_exit; - } - if (segment.necessary) - { - ++necessary_segments_running_index; - } - } - - util::json::Array json_last_instruction_row; - temp_instruction = std::to_string( - util::cast::enum_to_underlying(extractor::TurnInstruction::ReachedYourDestination)); - json_last_instruction_row.values.emplace_back(std::move(temp_instruction)); - json_last_instruction_row.values.push_back(""); - json_last_instruction_row.values.push_back(0); - json_last_instruction_row.values.push_back(necessary_segments_running_index - 1); - json_last_instruction_row.values.push_back(0); - json_last_instruction_row.values.push_back("0m"); - json_last_instruction_row.values.push_back(util::bearing::get(0.0)); - json_last_instruction_row.values.push_back(0.); - json_last_instruction_row.values.push_back(last_travel_mode); - json_last_instruction_row.values.push_back(util::bearing::get(0.0)); - json_last_instruction_row.values.push_back(0.); - json_instruction_array.values.emplace_back(std::move(json_last_instruction_row)); - - return json_instruction_array; -} - -} // namespace guidance -} // namespace engine -} // namespace osrm - -#endif diff --git a/include/engine/hint.hpp b/include/engine/hint.hpp new file mode 100644 index 000000000..cd88c4a75 --- /dev/null +++ b/include/engine/hint.hpp @@ -0,0 +1,61 @@ +#ifndef ENGINE_HINT_HPP +#define ENGINE_HINT_HPP + +#include "engine/object_encoder.hpp" +#include "engine/phantom_node.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ + +// Is returned as a temporary identifier for snapped coodinates +struct Hint +{ + FixedPointCoordinate input_coordinate; + PhantomNode phantom; + std::uint32_t data_checksum; + + template + bool IsValid(const FixedPointCoordinate new_input_coordinates, DataFacadeT &facade) const + { + auto is_same_input_coordinate = new_input_coordinates.lat == input_coordinate.lat && + new_input_coordinates.lon == input_coordinate.lon; + return is_same_input_coordinate && phantom.IsValid(facade.GetNumberOfNodes()) && + facade.GetCheckSum() == data_checksum; + } + + std::string ToBase64() const + { + std::string encoded = encodeBase64(*this); + return encoded; + } + + static Hint FromBase64(const std::string &base64Hint) + { + BOOST_ASSERT_MSG(base64Hint.size() == + static_cast(std::ceil(sizeof(Hint) / 3.) * 4), + "Hint has invalid size"); + + auto decoded = decodeBase64(base64Hint); + return decoded; + } +}; + +#ifndef _MSC_VER +constexpr std::size_t ENCODED_HINT_SIZE = 88; +static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint), + "ENCODED_HINT_SIZE does not match size of Hint"); +#else +// PhantomNode is bigger under windows because MSVC does not support bit packing +constexpr std::size_t ENCODED_HINT_SIZE = 84; +static_assert(ENCODED_HINT_SIZE / 4 * 3 >= sizeof(Hint), + "ENCODED_HINT_SIZE does not match size of Hint"); +#endif +} +} + +#endif diff --git a/include/engine/internal_route_result.hpp b/include/engine/internal_route_result.hpp index 25a1c9605..d9620fba4 100644 --- a/include/engine/internal_route_result.hpp +++ b/include/engine/internal_route_result.hpp @@ -17,26 +17,15 @@ namespace engine struct PathData { - PathData() - : node(SPECIAL_NODEID), name_id(INVALID_EDGE_WEIGHT), segment_duration(INVALID_EDGE_WEIGHT), - turn_instruction(extractor::TurnInstruction::NoTurn), - travel_mode(TRAVEL_MODE_INACCESSIBLE) - { - } - - PathData(NodeID node, - unsigned name_id, - extractor::TurnInstruction turn_instruction, - EdgeWeight segment_duration, - extractor::TravelMode travel_mode) - : node(node), name_id(name_id), segment_duration(segment_duration), - turn_instruction(turn_instruction), travel_mode(travel_mode) - { - } - NodeID node; + // id of via node of the turn + NodeID turn_via_node; + // name of the street that leads to the turn unsigned name_id; - EdgeWeight segment_duration; + // 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; + // travel mode of the street that leads to the turn extractor::TravelMode travel_mode : 4; }; diff --git a/include/engine/phantom_node.hpp b/include/engine/phantom_node.hpp index ecb30d696..066ff2a6b 100644 --- a/include/engine/phantom_node.hpp +++ b/include/engine/phantom_node.hpp @@ -94,7 +94,7 @@ struct PhantomNode bool operator==(const PhantomNode &other) const { return location == other.location; } template - PhantomNode(const OtherT &other, int forward_weight_, int forward_offset_, int reverse_weight_, int reverse_offset_, const util::FixedPointCoordinate foot_point) + explicit PhantomNode(const OtherT &other, int forward_weight_, int forward_offset_, int reverse_weight_, int reverse_offset_, const util::FixedPointCoordinate foot_point) { forward_node_id = other.forward_edge_based_node_id; reverse_node_id = other.reverse_edge_based_node_id; diff --git a/include/engine/plugins/plugin_base.hpp b/include/engine/plugins/plugin_base.hpp index 0eb36e948..920e7f7be 100644 --- a/include/engine/plugins/plugin_base.hpp +++ b/include/engine/plugins/plugin_base.hpp @@ -1,11 +1,14 @@ #ifndef BASE_PLUGIN_HPP #define BASE_PLUGIN_HPP +#include "engine/datafacade/datafacade_base.hpp" +#include "engine/api/base_parameters.hpp" #include "engine/phantom_node.hpp" +#include "engine/status.hpp" -#include "osrm/coordinate.hpp" -#include "osrm/json_container.hpp" -#include "osrm/route_parameters.hpp" +#include "util/coordinate.hpp" +#include "util/json_container.hpp" +#include "util/integer_range.hpp" #include #include @@ -20,38 +23,32 @@ namespace plugins class BasePlugin { - public: - enum class Status : int - { - Ok = 200, - EmptyResult = 207, - NoSegment = 208, - Error = 400 - }; + protected: + datafacade::BaseDataFacade &facade; + BasePlugin(datafacade::BaseDataFacade &facade_) : facade(facade_) {} - BasePlugin() {} - // Maybe someone can explain the pure virtual destructor thing to me (dennis) - virtual ~BasePlugin() {} - virtual const std::string GetDescriptor() const = 0; - virtual Status HandleRequest(const RouteParameters &, util::json::Object &) = 0; - virtual bool check_all_coordinates(const std::vector &coordinates, - const unsigned min = 2) const final + bool CheckAllCoordinates(const std::vector &coordinates) { - if (min > coordinates.size() || std::any_of(std::begin(coordinates), std::end(coordinates), - [](const util::FixedPointCoordinate coordinate) - { - return !coordinate.IsValid(); - })) - { - return false; - } - return true; + return !std::any_of(std::begin(coordinates), std::end(coordinates), + [](const util::FixedPointCoordinate &coordinate) + { + return !coordinate.IsValid(); + }); + } + + Status Error(const std::string &code, + const std::string &message, + util::json::Object &json_result) const + { + json_result.values["code"] = code; + json_result.values["message"] = message; + return Status::Error; } // Decides whether to use the phantom node from a big or small component if both are found. // Returns true if all phantom nodes are in the same component after snapping. - std::vector snapPhantomNodes( - const std::vector> &phantom_node_pair_list) const + std::vector + SnapPhantomNodes(const std::vector &phantom_node_pair_list) const { const auto check_component_id_is_tiny = [](const std::pair &phantom_pair) @@ -111,6 +108,69 @@ class BasePlugin return snapped_phantoms; } + + std::vector GetPhantomNodes(const api::BaseParameters ¶meters) + { + std::vector phantom_node_pairs(parameters.coordinates.size()); + + const bool use_hints = !parameters.hints.empty(); + const bool use_bearings = !parameters.bearings.empty(); + const bool use_radiuses = !parameters.radiuses.empty(); + + BOOST_ASSERT(parameters.IsValid()); + for (const auto i : util::irange(0, parameters.coordinates.size())) + { + if (use_hints && parameters.hints[i] && + parameters.hints[i]->IsValid(parameters.coordinates[i], facade)) + { + phantom_node_pairs[i].first = parameters.hints[i]->phantom; + // we don't set the second one - it will be marked as invalid + continue; + } + + if (use_bearings && parameters.bearings[i]) + { + if (use_radiuses && parameters.radiuses[i]) + { + phantom_node_pairs[i] = + facade.NearestPhantomNodeWithAlternativeFromBigComponent( + parameters.coordinates[i], *parameters.radiuses[i], + parameters.bearings[i]->bearing, parameters.bearings[i]->range); + } + else + phantom_node_pairs[i] = + facade.NearestPhantomNodeWithAlternativeFromBigComponent( + parameters.coordinates[i], parameters.bearings[i]->bearing, + parameters.bearings[i]->range); + { + } + } + else + { + if (use_radiuses && parameters.radiuses[i]) + { + phantom_node_pairs[i] = + facade.NearestPhantomNodeWithAlternativeFromBigComponent( + parameters.coordinates[i], *parameters.radiuses[i]); + } + else + phantom_node_pairs[i] = + facade.NearestPhantomNodeWithAlternativeFromBigComponent( + parameters.coordinates[i]); + { + } + } + + // we didn't found a fitting node, return error + if (!phantom_node_pairs[i].first.IsValid(facade.GetNumberOfNodes())) + { + break; + } + BOOST_ASSERT(phantom_node_pairs[i].first.IsValid(facade.GetNumberOfNodes())); + BOOST_ASSERT(phantom_node_pairs[i].second.IsValid(facade.GetNumberOfNodes())); + } + return phantom_node_pairs; + } }; } } diff --git a/include/engine/plugins/viaroute.hpp b/include/engine/plugins/viaroute.hpp index 72a5e2bf4..06404e62f 100644 --- a/include/engine/plugins/viaroute.hpp +++ b/include/engine/plugins/viaroute.hpp @@ -1,17 +1,16 @@ #ifndef VIA_ROUTE_HPP #define VIA_ROUTE_HPP +#include "engine/datafacade/datafacade_base.hpp" #include "engine/plugins/plugin_base.hpp" +#include "engine/api/route_api.hpp" -#include "engine/api_response_generator.hpp" #include "engine/object_encoder.hpp" -#include "engine/search_engine.hpp" -#include "util/for_each_pair.hpp" -#include "util/integer_range.hpp" -#include "util/make_unique.hpp" -#include "util/simple_logger.hpp" -#include "util/timing_util.hpp" -#include "osrm/json_container.hpp" +#include "engine/search_engine_data.hpp" +#include "engine/routing_algorithms/shortest_path.hpp" +#include "engine/routing_algorithms/alternative_path.hpp" +#include "engine/routing_algorithms/direct_shortest_path.hpp" +#include "util/json_container.hpp" #include @@ -27,146 +26,20 @@ namespace engine namespace plugins { -template class ViaRoutePlugin final : public BasePlugin +class ViaRoutePlugin final : public BasePlugin { private: - std::string descriptor_string; - std::unique_ptr> search_engine_ptr; - DataFacadeT *facade; + SearchEngineData heaps; + routing_algorithms::ShortestPathRouting shortest_path; + routing_algorithms::AlternativeRouting alternative_path; + routing_algorithms::DirectShortestPathRouting direct_shortest_path; int max_locations_viaroute; public: - explicit ViaRoutePlugin(DataFacadeT *facade, int max_locations_viaroute) - : descriptor_string("viaroute"), facade(facade), - max_locations_viaroute(max_locations_viaroute) - { - search_engine_ptr = util::make_unique>(facade); - } + explicit ViaRoutePlugin(datafacade::BaseDataFacade &facade, int max_locations_viaroute); - virtual ~ViaRoutePlugin() {} - - const std::string GetDescriptor() const override final { return descriptor_string; } - - Status HandleRequest(const RouteParameters &route_parameters, - util::json::Object &json_result) override final - { - if (max_locations_viaroute > 0 && - (static_cast(route_parameters.coordinates.size()) > max_locations_viaroute)) - { - json_result.values["status_message"] = - "Number of entries " + std::to_string(route_parameters.coordinates.size()) + - " is higher than current maximum (" + std::to_string(max_locations_viaroute) + ")"; - return Status::Error; - } - - if (!check_all_coordinates(route_parameters.coordinates)) - { - json_result.values["status_message"] = "Invalid coordinates"; - return Status::Error; - } - - const auto &input_bearings = route_parameters.bearings; - if (input_bearings.size() > 0 && - route_parameters.coordinates.size() != input_bearings.size()) - { - json_result.values["status_message"] = - "Number of bearings does not match number of coordinate"; - return Status::Error; - } - - std::vector phantom_node_pair_list(route_parameters.coordinates.size()); - const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum()); - - for (const auto i : util::irange(0, route_parameters.coordinates.size())) - { - if (checksum_OK && i < route_parameters.hints.size() && - !route_parameters.hints[i].empty()) - { - phantom_node_pair_list[i].first = - decodeBase64(route_parameters.hints[i]); - if (phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes())) - { - continue; - } - } - const int bearing = input_bearings.size() > 0 ? input_bearings[i].first : 0; - const int range = input_bearings.size() > 0 - ? (input_bearings[i].second ? *input_bearings[i].second : 10) - : 180; - phantom_node_pair_list[i] = facade->NearestPhantomNodeWithAlternativeFromBigComponent( - route_parameters.coordinates[i], bearing, range); - // we didn't found a fitting node, return error - if (!phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes())) - { - json_result.values["status_message"] = - std::string("Could not find a matching segment for coordinate ") + - std::to_string(i); - return Status::NoSegment; - } - BOOST_ASSERT(phantom_node_pair_list[i].first.IsValid(facade->GetNumberOfNodes())); - BOOST_ASSERT(phantom_node_pair_list[i].second.IsValid(facade->GetNumberOfNodes())); - } - - auto snapped_phantoms = snapPhantomNodes(phantom_node_pair_list); - - InternalRouteResult raw_route; - auto build_phantom_pairs = - [&raw_route](const PhantomNode &first_node, const PhantomNode &second_node) - { - raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node}); - }; - util::for_each_pair(snapped_phantoms, build_phantom_pairs); - - if (1 == raw_route.segment_end_coordinates.size()) - { - if (route_parameters.alternate_route && facade->GetCoreSize() == 0) - { - search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(), - raw_route); - } - else - { - search_engine_ptr->direct_shortest_path(raw_route.segment_end_coordinates, - route_parameters.uturns, raw_route); - } - } - else - { - search_engine_ptr->shortest_path(raw_route.segment_end_coordinates, - route_parameters.uturns, raw_route); - } - - // we can only know this after the fact, different SCC ids still - // allow for connection in one direction. - if (raw_route.is_valid()) - { - auto generator = MakeApiResponseGenerator(facade); - generator.DescribeRoute(route_parameters, raw_route, json_result); - json_result.values["status_message"] = "Found route between points"; - } - else - { - auto first_component_id = snapped_phantoms.front().component.id; - auto not_in_same_component = - std::any_of(snapped_phantoms.begin(), snapped_phantoms.end(), - [first_component_id](const PhantomNode &node) - { - return node.component.id != first_component_id; - }); - - if (not_in_same_component) - { - json_result.values["status_message"] = "Impossible route between points"; - return Status::EmptyResult; - } - else - { - json_result.values["status_message"] = "No route found between points"; - return Status::Error; - } - } - return Status::Ok; - } + Status HandleRequest(const api::RouteParameters &route_parameters, + util::json::Object &json_result); }; } } diff --git a/include/engine/polyline_compressor.hpp b/include/engine/polyline_compressor.hpp index 8f7b695bf..bba2ab199 100644 --- a/include/engine/polyline_compressor.hpp +++ b/include/engine/polyline_compressor.hpp @@ -2,7 +2,6 @@ #define POLYLINECOMPRESSOR_H_ #include "osrm/coordinate.hpp" -#include "engine/segment_information.hpp" #include #include @@ -11,13 +10,21 @@ namespace osrm { 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; +} + +using CoordVectorForwardIter = std::vector::const_iterator; // Encodes geometry into polyline format. // See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm -std::string polylineEncode(const std::vector &geometry); +std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter end); // Decodes geometry from polyline format // See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm -std::vector polylineDecode(const std::string &polyline); +std::vector decodePolyline(const std::string &polyline); } } diff --git a/include/engine/polyline_formatter.hpp b/include/engine/polyline_formatter.hpp deleted file mode 100644 index 434d792fe..000000000 --- a/include/engine/polyline_formatter.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef POLYLINE_FORMATTER_HPP -#define POLYLINE_FORMATTER_HPP - -#include "engine/segment_information.hpp" -#include "osrm/json_container.hpp" - -#include -#include - -namespace osrm -{ -namespace engine -{ - -// Encodes geometry into polyline format, returning an encoded JSON object -// See: https://developers.google.com/maps/documentation/utilities/polylinealgorithm -util::json::String polylineEncodeAsJSON(const std::vector &geometry); - -// Does not encode the geometry in polyline format, instead returning an unencoded JSON object -util::json::Array polylineUnencodedAsJSON(const std::vector &geometry); -} -} - -#endif /* POLYLINE_FORMATTER_HPP */ diff --git a/include/engine/route_name_extraction.hpp b/include/engine/route_name_extraction.hpp deleted file mode 100644 index 490155a8f..000000000 --- a/include/engine/route_name_extraction.hpp +++ /dev/null @@ -1,150 +0,0 @@ -#ifndef EXTRACT_ROUTE_NAMES_H -#define EXTRACT_ROUTE_NAMES_H - -#include - -#include -#include -#include -#include - -namespace osrm -{ -namespace engine -{ - -struct RouteNames -{ - std::string shortest_path_name_1; - std::string shortest_path_name_2; - std::string alternative_path_name_1; - std::string alternative_path_name_2; -}; - -namespace detail -{ - -template -SegmentT pickNextLongestSegment(const std::vector &segment_list, - const unsigned blocked_name_id) -{ - SegmentT result_segment; - result_segment.name_id = blocked_name_id; // make sure we get a valid name - result_segment.length = 0; - - for (const SegmentT &segment : segment_list) - { - if (segment.name_id != blocked_name_id && segment.length > result_segment.length && - segment.name_id != 0) - { - result_segment = segment; - } - } - return result_segment; -} - -} // ns detail - -template -RouteNames extractRouteNames(std::vector &shortest_path_segments, - std::vector &alternative_path_segments, - const DataFacadeT *facade) -{ - RouteNames route_names; - - if (shortest_path_segments.empty()) - { - return route_names; - } - - SegmentT shortest_segment_1, shortest_segment_2; - SegmentT alternative_segment_1, alternative_segment_2; - - const auto length_comperator = [](const SegmentT &a, const SegmentT &b) - { - return a.length > b.length; - }; - const auto name_id_comperator = [](const SegmentT &a, const SegmentT &b) - { - return a.name_id < b.name_id; - }; - - // pick the longest segment for the shortest path. - std::sort(shortest_path_segments.begin(), shortest_path_segments.end(), length_comperator); - shortest_segment_1 = shortest_path_segments[0]; - - if (!alternative_path_segments.empty()) - { - std::sort(alternative_path_segments.begin(), alternative_path_segments.end(), - length_comperator); - - // also pick the longest segment for the alternative path - alternative_segment_1 = alternative_path_segments[0]; - } - - // compute the set difference (for shortest path) depending on names between shortest and - // alternative - std::vector shortest_path_set_difference(shortest_path_segments.size()); - std::sort(shortest_path_segments.begin(), shortest_path_segments.end(), name_id_comperator); - std::sort(alternative_path_segments.begin(), alternative_path_segments.end(), - name_id_comperator); - std::set_difference(shortest_path_segments.begin(), shortest_path_segments.end(), - alternative_path_segments.begin(), alternative_path_segments.end(), - shortest_path_set_difference.begin(), name_id_comperator); - - std::sort(shortest_path_set_difference.begin(), shortest_path_set_difference.end(), - length_comperator); - shortest_segment_2 = - pickNextLongestSegment(shortest_path_set_difference, shortest_segment_1.name_id); - - // compute the set difference (for alternative path) depending on names between shortest and - // alternative - // vectors are still sorted, no need to do again - BOOST_ASSERT(std::is_sorted(shortest_path_segments.begin(), shortest_path_segments.end(), - name_id_comperator)); - BOOST_ASSERT(std::is_sorted(alternative_path_segments.begin(), alternative_path_segments.end(), - name_id_comperator)); - - std::vector alternative_path_set_difference(alternative_path_segments.size()); - std::set_difference(alternative_path_segments.begin(), alternative_path_segments.end(), - shortest_path_segments.begin(), shortest_path_segments.end(), - alternative_path_set_difference.begin(), name_id_comperator); - - std::sort(alternative_path_set_difference.begin(), alternative_path_set_difference.end(), - length_comperator); - - if (!alternative_path_segments.empty()) - { - alternative_segment_2 = - pickNextLongestSegment(alternative_path_set_difference, alternative_segment_1.name_id); - } - - // move the segments into the order in which they occur. - if (shortest_segment_1.position > shortest_segment_2.position) - { - std::swap(shortest_segment_1, shortest_segment_2); - } - - // fetching names for the selected segments - route_names.shortest_path_name_1 = facade->get_name_for_id(shortest_segment_1.name_id); - route_names.shortest_path_name_2 = facade->get_name_for_id(shortest_segment_2.name_id); - - if (!alternative_path_segments.empty()) - { - if (alternative_segment_1.position > alternative_segment_2.position) - { - std::swap(alternative_segment_1, alternative_segment_2); - } - - route_names.alternative_path_name_1 = - facade->get_name_for_id(alternative_segment_1.name_id); - route_names.alternative_path_name_2 = - facade->get_name_for_id(alternative_segment_2.name_id); - } - - return route_names; -} -} -} - -#endif // EXTRACT_ROUTE_NAMES_H diff --git a/include/engine/route_parameters.hpp b/include/engine/route_parameters.hpp deleted file mode 100644 index 401828539..000000000 --- a/include/engine/route_parameters.hpp +++ /dev/null @@ -1,129 +0,0 @@ -/* - -Copyright (c) 2016, Project OSRM contributors -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, -are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list -of conditions and the following disclaimer. -Redistributions in binary form must reproduce the above copyright notice, this -list of conditions and the following disclaimer in the documentation and/or -other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR -ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON -ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -*/ - -#ifndef ROUTE_PARAMETERS_HPP -#define ROUTE_PARAMETERS_HPP - -#include "osrm/coordinate.hpp" - -#include - -#include -#include - -namespace osrm -{ -namespace engine -{ - -struct RouteParameters -{ - RouteParameters(); - - void SetZoomLevel(const short level); - - void SetNumberOfResults(const short number); - - void SetAlternateRouteFlag(const bool flag); - - void SetUTurn(const bool flag); - - void SetAllUTurns(const bool flag); - - void SetClassify(const bool classify); - - void SetMatchingBeta(const double beta); - - void SetGPSPrecision(const double precision); - - void SetDeprecatedAPIFlag(const std::string &); - - void SetChecksum(const unsigned check_sum); - - void SetInstructionFlag(const bool flag); - - void SetService(const std::string &service); - - void SetOutputFormat(const std::string &format); - - void SetJSONpParameter(const std::string ¶meter); - - void AddHint(const std::string &hint); - - void AddTimestamp(const unsigned timestamp); - - bool AddBearing(int bearing, boost::optional range); - - void SetLanguage(const std::string &language); - - void SetGeometryFlag(const bool flag); - - void SetCompressionFlag(const bool flag); - - void AddCoordinate(const double latitude, const double longitude); - - void AddDestination(const double latitude, const double longitude); - - void AddSource(const double latitude, const double longitude); - - void SetCoordinatesFromGeometry(const std::string &geometry_string); - - bool SetX(const int x); - bool SetZ(const int z); - bool SetY(const int y); - - short zoom_level; - bool print_instructions; - bool alternate_route; - bool geometry; - bool compression; - bool deprecatedAPI; - bool uturn_default; - bool classify; - double matching_beta; - double gps_precision; - unsigned check_sum; - short num_results; - std::string service; - std::string output_format; - std::string jsonp_parameter; - std::string language; - std::vector hints; - std::vector timestamps; - std::vector>> bearings; - std::vector uturns; - std::vector coordinates; - std::vector is_destination; - std::vector is_source; - int z; - int x; - int y; -}; -} -} - -#endif // ROUTE_PARAMETERS_HPP diff --git a/include/engine/routing_algorithms/direct_shortest_path.hpp b/include/engine/routing_algorithms/direct_shortest_path.hpp index 931b6a5da..2e928cdc9 100644 --- a/include/engine/routing_algorithms/direct_shortest_path.hpp +++ b/include/engine/routing_algorithms/direct_shortest_path.hpp @@ -40,11 +40,8 @@ class DirectShortestPathRouting final ~DirectShortestPathRouting() {} void operator()(const std::vector &phantom_nodes_vector, - const std::vector &uturn_indicators, InternalRouteResult &raw_route_data) const { - (void)uturn_indicators; // unused - // Get distance to next pair of target nodes. BOOST_ASSERT_MSG(1 == phantom_nodes_vector.size(), "Direct Shortest Path Query only accepts a single source and target pair. " diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index 797499b98..39e7ba66a 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -24,13 +24,6 @@ namespace osrm namespace engine { -SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_1; -SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_1; -SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_2; -SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_2; -SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_3; -SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_3; - namespace routing_algorithms { @@ -327,13 +320,13 @@ template class BasicRoutingInterface BOOST_ASSERT(start_index < end_index); for (std::size_t i = start_index; i < end_index; ++i) { - unpacked_path.emplace_back(id_vector[i], name_index, - extractor::TurnInstruction::NoTurn, weight_vector[i], - travel_mode); + unpacked_path.push_back(PathData{id_vector[i], name_index, weight_vector[i], + extractor::TurnInstruction::NoTurn, + travel_mode}); } BOOST_ASSERT(unpacked_path.size() > 0); unpacked_path.back().turn_instruction = turn_instruction; - unpacked_path.back().segment_duration += (ed.distance - total_weight); + unpacked_path.back().duration_until_turn += (ed.distance - total_weight); } } std::vector id_vector; @@ -373,7 +366,7 @@ template class BasicRoutingInterface 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, - extractor::TurnInstruction::NoTurn, 0, + 0, extractor::TurnInstruction::NoTurn, target_traversed_in_reverse ? phantom_node_pair.target_phantom.backward_travel_mode : phantom_node_pair.target_phantom.forward_travel_mode}); @@ -392,7 +385,7 @@ template class BasicRoutingInterface // looks like a trivially true check but tests for underflow BOOST_ASSERT(last_index > second_to_last_index); - if (unpacked_path[last_index].node == unpacked_path[second_to_last_index].node) + if (unpacked_path[last_index].turn_via_node == unpacked_path[second_to_last_index].turn_via_node) { unpacked_path.pop_back(); } @@ -755,7 +748,7 @@ template class BasicRoutingInterface double distance = 0; for (const auto &p : unpacked_path) { - current_coordinate = facade->GetCoordinateOfNode(p.node); + current_coordinate = facade->GetCoordinateOfNode(p.turn_via_node); distance += util::coordinate_calculation::haversineDistance(previous_coordinate, current_coordinate); previous_coordinate = current_coordinate; diff --git a/include/engine/routing_algorithms/shortest_path.hpp b/include/engine/routing_algorithms/shortest_path.hpp index 066fddf92..1cb761f4f 100644 --- a/include/engine/routing_algorithms/shortest_path.hpp +++ b/include/engine/routing_algorithms/shortest_path.hpp @@ -9,6 +9,7 @@ #include "util/integer_range.hpp" #include +#include namespace osrm { @@ -236,11 +237,13 @@ class ShortestPathRouting final } } + static const constexpr bool UTURN_DEFAULT = false; + void operator()(const std::vector &phantom_nodes_vector, - const std::vector &uturn_indicators, + const std::vector> &uturn_indicators, InternalRouteResult &raw_route_data) const { - BOOST_ASSERT(uturn_indicators.size() == phantom_nodes_vector.size() + 1); + BOOST_ASSERT(uturn_indicators.empty() || uturn_indicators.size() == phantom_nodes_vector.size() + 1); engine_working_data.InitializeOrClearFirstThreadLocalStorage( super::facade->GetNumberOfNodes()); engine_working_data.InitializeOrClearSecondThreadLocalStorage( @@ -266,6 +269,8 @@ class ShortestPathRouting final std::vector total_packed_path_to_reverse; std::vector packed_leg_to_reverse_begin; + const bool use_uturn_indicators = !uturn_indicators.empty(); + std::size_t current_leg = 0; // this implements a dynamic program that finds the shortest route through // a list of vias @@ -280,8 +285,8 @@ class ShortestPathRouting final const auto &source_phantom = phantom_node_pair.source_phantom; const auto &target_phantom = phantom_node_pair.target_phantom; - BOOST_ASSERT(current_leg + 1 < uturn_indicators.size()); - const bool allow_u_turn_at_via = uturn_indicators[current_leg + 1]; + const bool use_uturn_default = !use_uturn_indicators || !uturn_indicators[current_leg + 1]; + const bool allow_u_turn_at_via = (use_uturn_default && UTURN_DEFAULT) || (!use_uturn_default && *uturn_indicators[current_leg + 1]); bool search_to_forward_node = target_phantom.forward_node_id != SPECIAL_NODEID; bool search_to_reverse_node = target_phantom.reverse_node_id != SPECIAL_NODEID; diff --git a/include/engine/search_engine.hpp b/include/engine/search_engine.hpp deleted file mode 100644 index c15b8535f..000000000 --- a/include/engine/search_engine.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef SEARCH_ENGINE_HPP -#define SEARCH_ENGINE_HPP - -#include "engine/search_engine_data.hpp" -#include "engine/routing_algorithms/alternative_path.hpp" -#include "engine/routing_algorithms/many_to_many.hpp" -#include "engine/routing_algorithms/map_matching.hpp" -#include "engine/routing_algorithms/shortest_path.hpp" -#include "engine/routing_algorithms/direct_shortest_path.hpp" - -#include - -namespace osrm -{ -namespace engine -{ - -template class SearchEngine -{ - private: - DataFacadeT *facade; - SearchEngineData engine_working_data; - - public: - routing_algorithms::ShortestPathRouting shortest_path; - routing_algorithms::DirectShortestPathRouting direct_shortest_path; - routing_algorithms::AlternativeRouting alternative_path; - routing_algorithms::ManyToManyRouting distance_table; - routing_algorithms::MapMatching map_matching; - - explicit SearchEngine(DataFacadeT *facade) - : facade(facade), shortest_path(facade, engine_working_data), - direct_shortest_path(facade, engine_working_data), - alternative_path(facade, engine_working_data), - distance_table(facade, engine_working_data), map_matching(facade, engine_working_data) - { - static_assert(!std::is_pointer::value, "don't instantiate with ptr type"); - static_assert(std::is_object::value, - "don't instantiate with void, function, or reference"); - } - - ~SearchEngine() {} -}; -} -} - -#endif // SEARCH_ENGINE_HPP diff --git a/include/engine/segment_information.hpp b/include/engine/segment_information.hpp deleted file mode 100644 index 842f5a3e3..000000000 --- a/include/engine/segment_information.hpp +++ /dev/null @@ -1,60 +0,0 @@ -#ifndef SEGMENT_INFORMATION_HPP -#define SEGMENT_INFORMATION_HPP - -#include "extractor/turn_instructions.hpp" -#include "extractor/travel_mode.hpp" -#include "util/typedefs.hpp" - -#include "osrm/coordinate.hpp" -#include - -namespace osrm -{ -namespace engine -{ - -// Struct fits everything in one cache line -struct SegmentInformation -{ - util::FixedPointCoordinate location; - NodeID name_id; - EdgeWeight duration; - float length; - short pre_turn_bearing; // more than enough [0..3600] fits into 12 bits - short post_turn_bearing; - extractor::TurnInstruction turn_instruction; - extractor::TravelMode travel_mode; - bool necessary; - bool is_via_location; - - explicit SegmentInformation(util::FixedPointCoordinate location, - const NodeID name_id, - const EdgeWeight duration, - const float length, - const extractor::TurnInstruction turn_instruction, - const bool necessary, - const bool is_via_location, - const extractor::TravelMode travel_mode) - : location(std::move(location)), name_id(name_id), duration(duration), length(length), - pre_turn_bearing(0), post_turn_bearing(0), turn_instruction(turn_instruction), - travel_mode(travel_mode), necessary(necessary), is_via_location(is_via_location) - { - } - - explicit SegmentInformation(util::FixedPointCoordinate location, - const NodeID name_id, - const EdgeWeight duration, - const float length, - const extractor::TurnInstruction turn_instruction, - const extractor::TravelMode travel_mode) - : location(std::move(location)), name_id(name_id), duration(duration), length(length), - pre_turn_bearing(0), post_turn_bearing(0), turn_instruction(turn_instruction), - travel_mode(travel_mode), - necessary(turn_instruction != extractor::TurnInstruction::NoTurn), is_via_location(false) - { - } -}; -} -} - -#endif /* SEGMENT_INFORMATION_HPP */ diff --git a/include/engine/status.hpp b/include/engine/status.hpp new file mode 100644 index 000000000..ed636c266 --- /dev/null +++ b/include/engine/status.hpp @@ -0,0 +1,18 @@ +#ifndef ENGINE_STATUS_HPP +#define ENGINE_STATUS_HPP + +namespace osrm +{ +namespace engine +{ + +enum class Status +{ + Ok, + Error +}; + +} +} + +#endif diff --git a/include/osrm/osrm.hpp b/include/osrm/osrm.hpp index a1594b902..1d28787f3 100644 --- a/include/osrm/osrm.hpp +++ b/include/osrm/osrm.hpp @@ -28,13 +28,12 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef OSRM_HPP #define OSRM_HPP +#include "osrm/status.hpp" + #include namespace osrm { - -struct LibOSRMConfig; - namespace util { namespace json @@ -47,11 +46,14 @@ namespace engine { class Engine; struct EngineConfig; +namespace api +{ struct RouteParameters; } +} using engine::EngineConfig; -using engine::RouteParameters; +using engine::api::RouteParameters; namespace json = util::json; class OSRM @@ -61,8 +63,8 @@ class OSRM public: OSRM(EngineConfig &lib_config); - ~OSRM(); // needed because we need to define it with the implementation of OSRM_impl - int RunQuery(const RouteParameters &route_parameters, json::Object &json_result); + ~OSRM(); // needed for unique_ptr + impl abstraction + Status Route(const RouteParameters &route_parameters, json::Object &json_result); }; } diff --git a/include/osrm/route_parameters.hpp b/include/osrm/route_parameters.hpp index 57c884d5f..c348f6d16 100644 --- a/include/osrm/route_parameters.hpp +++ b/include/osrm/route_parameters.hpp @@ -1,11 +1,11 @@ #ifndef GLOBAL_ROUTE_PARAMETERS_HPP #define GLOBAL_ROUTE_PARAMETERS_HPP -#include "engine/route_parameters.hpp" +#include "engine/api/route_parameters.hpp" namespace osrm { - using engine::RouteParameters; + using engine::api::RouteParameters; } #endif diff --git a/include/osrm/status.hpp b/include/osrm/status.hpp new file mode 100644 index 000000000..184a18760 --- /dev/null +++ b/include/osrm/status.hpp @@ -0,0 +1,11 @@ +#ifndef OSRM_STATUS_HPP +#define OSRM_STATUS_HPP + +#include "engine/status.hpp" + +namespace osrm +{ + using engine::Status; +} + +#endif diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp new file mode 100644 index 000000000..ced3b4404 --- /dev/null +++ b/include/server/api/base_parameters_grammar.hpp @@ -0,0 +1,82 @@ +#ifndef SERVER_API_BASE_PARAMETERS_GRAMMAR_HPP +#define SERVER_API_BASE_PARAMETERS_GRAMMAR_HPP + +#include "engine/api/base_parameters.hpp" + +#include "engine/polyline_compressor.hpp" +#include "engine/hint.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace qi = boost::spirit::qi; +struct BaseParametersGrammar : boost::spirit::qi::grammar +{ + using Iterator = std::string::iterator; + using RadiusesT = std::vector>; + + BaseParametersGrammar(qi::rule &child_rule, + engine::api::BaseParameters ¶meters_) + : BaseParametersGrammar::base_type(child_rule), base_parameters(parameters_) + { + const auto add_bearing = [this](const boost::optional> &bearing_range) { + boost::optional bearing; + if (bearing_range) + { + bearing = engine::api::BaseParameters::Bearing {boost::fusion::at_c<0>(*bearing_range), boost::fusion::at_c<1>(*bearing_range)}; + } + base_parameters.bearings.push_back(std::move(bearing)); + }; + const auto set_radiuses = [this](RadiusesT& radiuses) { + base_parameters.radiuses = std::move(radiuses); + }; + const auto add_hint = [this](const std::string& hint_string) { + if (hint_string.size() > 0) + { + base_parameters.hints.push_back(engine::Hint::FromBase64(hint_string)); + } + }; + + alpha_numeral = +qi::char_("a-zA-Z0-9"); + base64_char = qi::char_("a-zA-Z0-9--_"); + + radiuses_rule = qi::lit("radiuses=") >> -qi::double_ % ";"; + hints_rule = qi::lit("hints=") >> qi::as_string[qi::repeat(engine::ENCODED_HINT_SIZE)[base64_char]][add_hint] % ";"; + bearings_rule = + qi::lit("bearings=") >> -((qi::short_ >> ',' >> qi::short_))[add_bearing] % ";"; + base_rule = bearings_rule | radiuses_rule[set_radiuses] | hints_rule; + } + + qi::rule base_rule; + +private: + engine::api::BaseParameters &base_parameters; + qi::rule bearings_rule; + qi::rule hints_rule; + qi::rule radiuses_rule; + qi::rule base64_char; + qi::rule alpha_numeral; +}; +} +} +} + +#endif diff --git a/include/server/api/parsed_url.hpp b/include/server/api/parsed_url.hpp new file mode 100644 index 000000000..bf8d317b7 --- /dev/null +++ b/include/server/api/parsed_url.hpp @@ -0,0 +1,29 @@ +#ifndef SERVER_API_PARSED_URL_HPP +#define SERVER_API_PARSED_URL_HPP + +#include "util/coordinate.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +struct ParsedURL +{ + std::string service; + unsigned version; + std::string profile; + std::vector coordinates; + std::string options; +}; + +} +} +} + +#endif diff --git a/include/server/api/route_parameters_grammar.hpp b/include/server/api/route_parameters_grammar.hpp new file mode 100644 index 000000000..8a0313960 --- /dev/null +++ b/include/server/api/route_parameters_grammar.hpp @@ -0,0 +1,92 @@ +#ifndef ROUTE_PARAMETERS_GRAMMAR_HPP +#define ROUTE_PARAMETERS_GRAMMAR_HPP + +#include "engine/api/route_parameters.hpp" + +#include "server/api/base_parameters_grammar.hpp" + +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace qi = boost::spirit::qi; +struct RouteParametersGrammar : public BaseParametersGrammar +{ + using Iterator = std::string::iterator; + using StepsT = bool; + using AlternativeT = bool; + using GeometriesT = engine::api::RouteParameters::GeometriesType; + using OverviewT = engine::api::RouteParameters::OverviewType; + using UturnsT = std::vector>; + + RouteParametersGrammar() + : BaseParametersGrammar(root_rule, route_parameters) + { + const auto set_geojson_type = [this]() + { + route_parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON; + }; + const auto set_polyline_type = [this]() + { + route_parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline; + }; + + const auto set_simplified_type = [this]() + { + route_parameters.overview = engine::api::RouteParameters::OverviewType::Simplified; + }; + const auto set_full_type = [this]() + { + route_parameters.overview = engine::api::RouteParameters::OverviewType::Full; + }; + const auto set_false_type = [this]() + { + route_parameters.overview = engine::api::RouteParameters::OverviewType::False; + }; + const auto set_steps = [this](const StepsT steps) + { + route_parameters.steps = steps; + }; + const auto set_alternative = [this](const AlternativeT alternative) + { + route_parameters.alternative = alternative; + }; + const auto set_uturns = [this](UturnsT &uturns) + { + route_parameters.uturns = std::move(uturns); + }; + + alternative_rule = qi::lit("alternative=") >> qi::bool_; + steps_rule = qi::lit("steps=") >> qi::bool_; + geometries_rule = qi::lit("geometries=geojson")[set_geojson_type] | + qi::lit("geometries=polyline")[set_polyline_type]; + overview_rule = qi::lit("overview=simplified")[set_simplified_type] | + qi::lit("overview=full")[set_full_type] | + qi::lit("overview=false")[set_false_type]; + uturns_rule = qi::lit("uturns=") >> -qi::bool_ % ";"; + route_rule = steps_rule[set_steps] | alternative_rule[set_alternative] | geometries_rule | + overview_rule | uturns_rule[set_uturns]; + root_rule = -((base_rule | route_rule) % '&'); + } + + engine::api::RouteParameters route_parameters; + private: + qi::rule root_rule, route_rule, geometries_rule, overview_rule; + qi::rule uturns_rule; + qi::rule steps_rule; + qi::rule alternative_rule; +}; +} +} +} + +#endif diff --git a/include/server/api/route_parameters_parser.hpp b/include/server/api/route_parameters_parser.hpp new file mode 100644 index 000000000..467c3e345 --- /dev/null +++ b/include/server/api/route_parameters_parser.hpp @@ -0,0 +1,28 @@ +#ifndef SERVER_API_ROUTE_PARAMETERS_PARSER_HPP +#define SERVER_API_ROUTE_PARAMETERS_PARSER_HPP + +#include "engine/api/route_parameters.hpp" +#include "server/api/route_parameters_parser.hpp" + +namespace osrm +{ +namespace server +{ +namespace api +{ + +// Starts parsing and iter and modifies it until iter == end or parsing failed +boost::optional parseRouteParameters(std::string::iterator& iter, std::string::iterator end); + +// copy on purpose because we need mutability +inline boost::optional parseRouteParameters(std::string options_string) +{ + auto iter = options_string.begin(); + return parseRouteParameters(iter, options_string.end()); +} + +} +} +} + +#endif diff --git a/include/server/api/url_parser.hpp b/include/server/api/url_parser.hpp new file mode 100644 index 000000000..58a400b98 --- /dev/null +++ b/include/server/api/url_parser.hpp @@ -0,0 +1,30 @@ +#ifndef SERVER_URL_PARSER_HPP +#define SERVER_URL_PARSER_HPP + +#include "server/api/parsed_url.hpp" + +#include + +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +// Starts parsing and iter and modifies it until iter == end or parsing failed +boost::optional parseURL(std::string::iterator& iter, std::string::iterator end); +// copy on purpose because we need mutability +inline boost::optional parseURL(std::string url_string) +{ + auto iter = url_string.begin(); + return parseURL(iter, url_string.end()); +} + +} +} +} + +#endif diff --git a/include/server/api_grammar.hpp b/include/server/api_grammar.hpp deleted file mode 100644 index 41563e029..000000000 --- a/include/server/api_grammar.hpp +++ /dev/null @@ -1,151 +0,0 @@ -#ifndef API_GRAMMAR_HPP -#define API_GRAMMAR_HPP - -#include -#include -#include - -namespace osrm -{ -namespace server -{ - -namespace qi = boost::spirit::qi; - -template struct APIGrammar : qi::grammar -{ - explicit APIGrammar(HandlerT *h) : APIGrammar::base_type(api_call), handler(h) - { - const auto set_x_wrapper = [this](const int x, bool &pass) - { - pass = handler->SetX(x); - }; - const auto set_y_wrapper = [this](const int y, bool &pass) - { - pass = handler->SetY(y); - }; - const auto set_z_wrapper = [this](const int z, bool &pass) - { - pass = handler->SetZ(z); - }; - const auto add_bearing_wrapper = [this]( - const boost::fusion::vector> &received_bearing, bool &pass) - { - const int bearing = boost::fusion::at_c<0>(received_bearing); - const boost::optional range = boost::fusion::at_c<1>(received_bearing); - pass = handler->AddBearing(bearing, range); - }; - const auto add_coordinate_wrapper = - [this](const boost::fusion::vector &received_coordinate) - { - handler->AddCoordinate(boost::fusion::at_c<0>(received_coordinate), - boost::fusion::at_c<1>(received_coordinate)); - }; - const auto add_source_wrapper = - [this](const boost::fusion::vector &received_coordinate) - { - handler->AddSource(boost::fusion::at_c<0>(received_coordinate), - boost::fusion::at_c<1>(received_coordinate)); - }; - const auto add_destination_wrapper = - [this](const boost::fusion::vector &received_coordinate) - { - handler->AddDestination(boost::fusion::at_c<0>(received_coordinate), - boost::fusion::at_c<1>(received_coordinate)); - }; - - api_call = - qi::lit('/') >> string[boost::bind(&HandlerT::SetService, handler, ::_1)] >> -query; - query = ('?') >> +(zoom | output | jsonp | checksum | uturns | location_with_options | - destination_with_options | source_with_options | cmp | language | - instruction | geometry | alt_route | old_API | num_results | - matching_beta | gps_precision | classify | locs | x | y | z); - // all combinations of timestamp, uturn, hint and bearing without duplicates - t_u = (u >> -timestamp) | (timestamp >> -u); - t_h = (hint >> -timestamp) | (timestamp >> -hint); - u_h = (u >> -hint) | (hint >> -u); - t_u_h = (hint >> -t_u) | (u >> -t_h) | (timestamp >> -u_h); - location_options = - (bearing >> -t_u_h) | (t_u_h >> -bearing) | // - (u >> bearing >> -t_h) | (timestamp >> bearing >> -u_h) | (hint >> bearing >> t_u) | // - (t_h >> bearing >> -u) | (u_h >> bearing >> -timestamp) | (t_u >> bearing >> -hint); - location_with_options = location >> -location_options; - source_with_options = source >> -location_options; - destination_with_options = destination >> -location_options; - zoom = (-qi::lit('&')) >> qi::lit('z') >> '=' >> - qi::short_[boost::bind(&HandlerT::SetZoomLevel, handler, ::_1)]; - output = (-qi::lit('&')) >> qi::lit("output=json"); - jsonp = (-qi::lit('&')) >> qi::lit("jsonp") >> '=' >> - stringwithPercent[boost::bind(&HandlerT::SetJSONpParameter, handler, ::_1)]; - checksum = (-qi::lit('&')) >> qi::lit("checksum") >> '=' >> - qi::uint_[boost::bind(&HandlerT::SetChecksum, handler, ::_1)]; - instruction = (-qi::lit('&')) >> qi::lit("instructions") >> '=' >> - qi::bool_[boost::bind(&HandlerT::SetInstructionFlag, handler, ::_1)]; - geometry = (-qi::lit('&')) >> qi::lit("geometry") >> '=' >> - qi::bool_[boost::bind(&HandlerT::SetGeometryFlag, handler, ::_1)]; - cmp = (-qi::lit('&')) >> qi::lit("compression") >> '=' >> - qi::bool_[boost::bind(&HandlerT::SetCompressionFlag, handler, ::_1)]; - location = (-qi::lit('&')) >> qi::lit("loc") >> '=' >> - (qi::double_ >> qi::lit(',') >> - qi::double_)[boost::bind(add_coordinate_wrapper, ::_1)]; - destination = (-qi::lit('&')) >> qi::lit("dst") >> '=' >> - (qi::double_ >> qi::lit(',') >> - qi::double_)[boost::bind(add_destination_wrapper, ::_1)]; - source = (-qi::lit('&')) >> qi::lit("src") >> '=' >> - (qi::double_ >> qi::lit(',') >> - qi::double_)[boost::bind(add_source_wrapper, ::_1)]; - hint = (-qi::lit('&')) >> qi::lit("hint") >> '=' >> - stringwithDot[boost::bind(&HandlerT::AddHint, handler, ::_1)]; - timestamp = (-qi::lit('&')) >> qi::lit("t") >> '=' >> - qi::uint_[boost::bind(&HandlerT::AddTimestamp, handler, ::_1)]; - bearing = (-qi::lit('&')) >> qi::lit("b") >> '=' >> - (qi::int_ >> -(qi::lit(',') >> qi::int_ | - qi::attr(10)))[boost::bind(add_bearing_wrapper, ::_1, ::_3)]; - u = (-qi::lit('&')) >> qi::lit("u") >> '=' >> - qi::bool_[boost::bind(&HandlerT::SetUTurn, handler, ::_1)]; - uturns = (-qi::lit('&')) >> qi::lit("uturns") >> '=' >> - qi::bool_[boost::bind(&HandlerT::SetAllUTurns, handler, ::_1)]; - language = (-qi::lit('&')) >> qi::lit("hl") >> '=' >> - string[boost::bind(&HandlerT::SetLanguage, handler, ::_1)]; - alt_route = (-qi::lit('&')) >> qi::lit("alt") >> '=' >> - qi::bool_[boost::bind(&HandlerT::SetAlternateRouteFlag, handler, ::_1)]; - old_API = (-qi::lit('&')) >> qi::lit("geomformat") >> '=' >> - string[boost::bind(&HandlerT::SetDeprecatedAPIFlag, handler, ::_1)]; - num_results = (-qi::lit('&')) >> qi::lit("num_results") >> '=' >> - qi::short_[boost::bind(&HandlerT::SetNumberOfResults, handler, ::_1)]; - matching_beta = (-qi::lit('&')) >> qi::lit("matching_beta") >> '=' >> - qi::float_[boost::bind(&HandlerT::SetMatchingBeta, handler, ::_1)]; - gps_precision = (-qi::lit('&')) >> qi::lit("gps_precision") >> '=' >> - qi::float_[boost::bind(&HandlerT::SetGPSPrecision, handler, ::_1)]; - classify = (-qi::lit('&')) >> qi::lit("classify") >> '=' >> - qi::bool_[boost::bind(&HandlerT::SetClassify, handler, ::_1)]; - locs = (-qi::lit('&')) >> qi::lit("locs") >> '=' >> - stringforPolyline[boost::bind(&HandlerT::SetCoordinatesFromGeometry, handler, ::_1)]; - - x = (-qi::lit('&')) >> qi::lit("tx") >> '=' >> - qi::int_[boost::bind(set_x_wrapper, ::_1, ::_3)]; - y = (-qi::lit('&')) >> qi::lit("ty") >> '=' >> - qi::int_[boost::bind(set_y_wrapper, ::_1, ::_3)]; - z = (-qi::lit('&')) >> qi::lit("tz") >> '=' >> - qi::int_[boost::bind(set_z_wrapper, ::_1, ::_3)]; - - string = +(qi::char_("a-zA-Z")); - stringwithDot = +(qi::char_("a-zA-Z0-9_.-")); - stringwithPercent = +(qi::char_("a-zA-Z0-9_.-") | qi::char_('[') | qi::char_(']') | - (qi::char_('%') >> qi::char_("0-9A-Z") >> qi::char_("0-9A-Z"))); - stringforPolyline = +(qi::char_("a-zA-Z0-9_.-[]{}@?|\\%~`^")); - } - - qi::rule api_call, query, location_options, location_with_options, - destination_with_options, source_with_options, t_u, t_h, u_h, t_u_h; - qi::rule service, zoom, output, string, jsonp, checksum, location, - destination, source, hint, timestamp, bearing, stringwithDot, stringwithPercent, language, - geometry, cmp, alt_route, u, uturns, old_API, num_results, matching_beta, gps_precision, - classify, locs, instruction, stringforPolyline, x, y, z; - - HandlerT *handler; -}; -} -} - -#endif /* API_GRAMMAR_HPP */ diff --git a/include/server/request_handler.hpp b/include/server/request_handler.hpp index 16b3886a6..a02736188 100644 --- a/include/server/request_handler.hpp +++ b/include/server/request_handler.hpp @@ -1,18 +1,14 @@ #ifndef REQUEST_HANDLER_HPP #define REQUEST_HANDLER_HPP +#include "server/service_handler.hpp" + #include namespace osrm { -class OSRM; -namespace engine -{ -struct RouteParameters; -} namespace server { -template struct APIGrammar; namespace http { @@ -24,17 +20,16 @@ class RequestHandler { public: - using APIGrammarParser = APIGrammar; - RequestHandler(); RequestHandler(const RequestHandler &) = delete; RequestHandler &operator=(const RequestHandler &) = delete; - void handle_request(const http::request ¤t_request, http::reply ¤t_reply); - void RegisterRoutingMachine(OSRM *osrm); + void RegisterServiceHandler(std::unique_ptr service_handler); + + void HandleRequest(const http::request ¤t_request, http::reply ¤t_reply); private: - OSRM *routing_machine; + std::unique_ptr service_handler; }; } } diff --git a/include/server/server.hpp b/include/server/server.hpp index d6bb51af6..881307420 100644 --- a/include/server/server.hpp +++ b/include/server/server.hpp @@ -3,6 +3,7 @@ #include "server/connection.hpp" #include "server/request_handler.hpp" +#include "server/service_handler.hpp" #include "util/integer_range.hpp" #include "util/simple_logger.hpp" @@ -76,7 +77,10 @@ class Server void Stop() { io_service.stop(); } - RequestHandler &GetRequestHandlerPtr() { return request_handler; } + void RegisterServiceHandler(std::unique_ptr service_handler_) + { + request_handler.RegisterServiceHandler(std::move(service_handler_)); + } private: void HandleAccept(const boost::system::error_code &e) diff --git a/include/server/service/base_service.hpp b/include/server/service/base_service.hpp new file mode 100644 index 000000000..21d094941 --- /dev/null +++ b/include/server/service/base_service.hpp @@ -0,0 +1,36 @@ +#ifndef SERVER_SERVICE_BASE_SERVICE_HPP +#define SERVER_SERVICE_BASE_SERVICE_HPP + +#include "engine/status.hpp" +#include "util/coordinate.hpp" +#include "osrm/osrm.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +class BaseService +{ + public: + BaseService(OSRM &routing_machine) : routing_machine(routing_machine) {} + virtual ~BaseService() = default; + + virtual engine::Status RunQuery(std::vector coordinates, + std::string &options, + util::json::Object &json_result) = 0; + virtual unsigned GetVersion() = 0; + + protected: + OSRM &routing_machine; +}; +} +} +} + +#endif diff --git a/include/server/service/route_service.hpp b/include/server/service/route_service.hpp new file mode 100644 index 000000000..4993c8d8b --- /dev/null +++ b/include/server/service/route_service.hpp @@ -0,0 +1,34 @@ +#ifndef SERVER_SERVICE_ROUTE_SERVICE_HPP +#define SERVER_SERVICE_ROUTE_SERVICE_HPP + +#include "server/service/base_service.hpp" + +#include "engine/status.hpp" +#include "util/coordinate.hpp" +#include "osrm/osrm.hpp" + +#include +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +class RouteService final : public BaseService +{ + public: + RouteService(OSRM& routing_machine) : BaseService(routing_machine) {} + + virtual engine::Status RunQuery(std::vector coordinates, + std::string &options, + util::json::Object &json_result) final override; + virtual unsigned GetVersion() final override { return 1; } +}; +} +} +} + +#endif diff --git a/include/server/service_handler.hpp b/include/server/service_handler.hpp new file mode 100644 index 000000000..1e22bf756 --- /dev/null +++ b/include/server/service_handler.hpp @@ -0,0 +1,40 @@ +#ifndef SERVER_SERVICE_HANLDER_HPP +#define SERVER_SERVICE_HANLDER_HPP + +#include "server/service/base_service.hpp" + +#include "osrm/osrm.hpp" + +#include + +namespace osrm +{ +namespace util +{ +namespace json +{ +struct Object; +} +} +namespace server +{ +namespace api +{ +struct ParsedURL; +} + +class ServiceHandler +{ + public: + ServiceHandler(osrm::EngineConfig &config); + + engine::Status RunQuery(api::ParsedURL parsed_url, util::json::Object &json_result); + + private: + std::unordered_map> service_map; + OSRM routing_machine; +}; +} +} + +#endif diff --git a/include/util/coordinate.hpp b/include/util/coordinate.hpp index ca6b26e71..8ca1636a6 100644 --- a/include/util/coordinate.hpp +++ b/include/util/coordinate.hpp @@ -35,7 +35,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. namespace osrm { -constexpr const double COORDINATE_PRECISION = 1000000.0; +constexpr const double COORDINATE_PRECISION = 1e6; namespace util { @@ -52,6 +52,7 @@ struct FixedPointCoordinate FixedPointCoordinate(const T &coordinate) : lat(coordinate.lat), lon(coordinate.lon) { + static_assert(!std::is_same::value, "This constructor should not be used for FixedPointCoordinates"); static_assert(std::is_same::value, "coordinate types incompatible"); static_assert(std::is_same::value, @@ -59,11 +60,13 @@ struct FixedPointCoordinate } bool IsValid() const; - bool operator==(const FixedPointCoordinate &other) const; - friend std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate &coordinate); + friend bool operator==(const FixedPointCoordinate lhs, const FixedPointCoordinate rhs); + friend bool operator!=(const FixedPointCoordinate lhs, const FixedPointCoordinate rhs); + friend std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate coordinate); }; -std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate &coordinate); +bool operator==(const FixedPointCoordinate lhs, const FixedPointCoordinate rhs); +std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate coordinate); } } diff --git a/include/util/json_util.hpp b/include/util/json_util.hpp index 1d10471dd..b9e1cf096 100644 --- a/include/util/json_util.hpp +++ b/include/util/json_util.hpp @@ -59,7 +59,7 @@ template <> Array make_array(const std::vector &vector) } // Easy acces to object hierachies -Value &get(Value &value) { return value; } +inline Value &get(Value &value) { return value; } template Value &get(Value &value, const char *key, Keys... keys) { diff --git a/include/util/tiles.hpp b/include/util/tiles.hpp new file mode 100644 index 000000000..ce4afee68 --- /dev/null +++ b/include/util/tiles.hpp @@ -0,0 +1,85 @@ +#ifndef UTIL_TILES_HPP +#define UTIL_TILES_HPP + +#include + +#include +#include + +// This is a port of the tilebelt algorithm https://github.com/mapbox/tilebelt +namespace osrm +{ +namespace util +{ +namespace tiles +{ +struct Tile +{ + unsigned x; + unsigned y; + unsigned z; +}; + +namespace detail +{ +// optimized for 32bit integers +static constexpr unsigned MAX_ZOOM = 32; + +// Returns 1-indexed 1..32 of MSB if value > 0 or 0 if value == 0 +inline unsigned getMSBPosition(std::uint32_t value) +{ + if (value == 0) + return 0; + std::uint8_t pos = 1; + while (value >>= 1) + pos++; + return pos; +} + +inline unsigned getBBMaxZoom(const Tile top_left, const Tile bottom_left) +{ + auto x_xor = top_left.x ^ bottom_left.x; + auto y_xor = top_left.y ^ bottom_left.y; + auto lon_msb = detail::getMSBPosition(x_xor); + auto lat_msb = detail::getMSBPosition(y_xor); + return MAX_ZOOM - std::max(lon_msb, lat_msb); +} +} + +inline Tile pointToTile(const double lon, const double lat) +{ + auto sin_lat = std::sin(lat * M_PI / 180.); + auto p2z = std::pow(2, detail::MAX_ZOOM); + unsigned x = p2z * (lon / 360. + 0.5); + unsigned y = p2z * (0.5 - 0.25 * std::log((1 + sin_lat) / (1 - sin_lat)) / M_PI); + + return Tile{x, y, detail::MAX_ZOOM}; +} + +inline Tile getBBMaxZoomTile(const double min_lon, + const double min_lat, + const double max_lon, + const double max_lat) +{ + const auto top_left = pointToTile(min_lon, min_lat); + const auto bottom_left = pointToTile(max_lon, max_lat); + BOOST_ASSERT(top_left.z == detail::MAX_ZOOM); + BOOST_ASSERT(bottom_left.z == detail::MAX_ZOOM); + + const auto max_zoom = detail::getBBMaxZoom(top_left, bottom_left); + + if (max_zoom == 0) + { + return Tile{0, 0, 0}; + } + + auto x = top_left.x >> (detail::MAX_ZOOM - max_zoom); + auto y = top_left.y >> (detail::MAX_ZOOM - max_zoom); + + return Tile{x, y, max_zoom}; +} +} +} +} + +#endif diff --git a/src/engine/api/josn_factory.cpp b/src/engine/api/josn_factory.cpp new file mode 100644 index 000000000..5b271b3f9 --- /dev/null +++ b/src/engine/api/josn_factory.cpp @@ -0,0 +1,222 @@ +#include "engine/api/json_factory.hpp" + +#include "engine/guidance/route_step.hpp" +#include "engine/guidance/step_maneuver.hpp" +#include "engine/guidance/route_leg.hpp" +#include "engine/guidance/route.hpp" +#include "engine/guidance/leg_geometry.hpp" +#include "engine/polyline_compressor.hpp" +#include "engine/hint.hpp" + +#include +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ +namespace json +{ +namespace detail +{ + +std::string instructionToString(extractor::TurnInstruction instruction) +{ + 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; + + 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; + } + + return token; +} + +util::json::Array coordinateToLonLat(const FixedPointCoordinate &coordinate) +{ + util::json::Array array; + array.values.push_back(coordinate.lon / COORDINATE_PRECISION); + array.values.push_back(coordinate.lat / COORDINATE_PRECISION); + return array; +} + +util::json::Array coordinateToLatLon(const FixedPointCoordinate &coordinate) +{ + util::json::Array array; + array.values.push_back(coordinate.lat / COORDINATE_PRECISION); + array.values.push_back(coordinate.lon / COORDINATE_PRECISION); + return array; +} + +// FIXME this actually needs to be configurable from the profiles +std::string modeToString(const extractor::TravelMode mode) +{ + std::string token; + switch (mode) + { + case TRAVEL_MODE_DEFAULT: + token = "default"; + break; + case TRAVEL_MODE_INACCESSIBLE: + token = "inaccessible"; + break; + default: + token = "other"; + break; + } + return token; +} + +} // namespace detail + +util::json::Object makeStepManeuver(const guidance::StepManeuver &maneuver) +{ + util::json::Object step_maneuver; + step_maneuver.values["type"] = detail::instructionToString(maneuver.instruction); + step_maneuver.values["location"] = detail::coordinateToLatLon(maneuver.location); + step_maneuver.values["heading_before"] = maneuver.heading_before; + step_maneuver.values["heading_after"] = maneuver.heading_after; + return step_maneuver; +} + +util::json::Object makeRouteStep(guidance::RouteStep &&step, + boost::optional geometry) +{ + util::json::Object route_step; + route_step.values["distance"] = step.distance; + route_step.values["duration"] = step.duration; + route_step.values["way_name"] = std::move(step.way_name); + route_step.values["mode"] = detail::modeToString(step.mode); + route_step.values["maneuver"] = makeStepManeuver(step.maneuver); + if (geometry) + { + route_step.values["geometry"] = std::move(*geometry); + } + return route_step; +} + +util::json::Object makeRoute(const guidance::Route &route, + util::json::Array &&legs, + boost::optional geometry) +{ + util::json::Object json_route; + json_route.values["distance"] = route.distance; + json_route.values["duration"] = route.duration; + json_route.values["legs"] = std::move(legs); + if (geometry) + { + json_route.values["geometry"] = std::move(*geometry); + } + return json_route; +} + +util::json::Object +makeWaypoint(const FixedPointCoordinate location, std::string &&way_name, const Hint &hint) +{ + util::json::Object waypoint; + waypoint.values["location"] = detail::coordinateToLatLon(location); + waypoint.values["way_name"] = std::move(way_name); + waypoint.values["hint"] = hint.ToBase64(); + return waypoint; +} + +util::json::Object makeRouteLeg(guidance::RouteLeg &&leg, util::json::Array &&steps) +{ + util::json::Object route_leg; + route_leg.values["distance"] = leg.distance; + route_leg.values["duration"] = leg.duration; + route_leg.values["summary"] = std::move(leg.summary); + route_leg.values["steps"] = std::move(steps); + return route_leg; +} + +util::json::Array makeRouteLegs(std::vector &&legs, + const std::vector &leg_geometries) +{ + util::json::Array json_legs; + for (const auto idx : boost::irange(0UL, legs.size())) + { + auto &&leg = std::move(legs[idx]); + const auto &leg_geometry = leg_geometries[idx]; + util::json::Array json_steps; + json_steps.values.reserve(leg.steps.size()); + const auto begin_iter = leg_geometry.locations.begin(); + std::transform( + std::make_move_iterator(leg.steps.begin()), std::make_move_iterator(leg.steps.end()), + std::back_inserter(json_steps.values), [&begin_iter](guidance::RouteStep &&step) + { + // FIXME we only support polyline here + auto geometry = boost::make_optional( + makePolyline(begin_iter + step.geometry_begin, begin_iter + step.geometry_end)); + return makeRouteStep(std::move(step), std::move(geometry)); + }); + json_legs.values.push_back(makeRouteLeg(std::move(leg), std::move(json_steps))); + } + + return json_legs; +} +} +} +} // namespace engine +} // namespace osrm diff --git a/src/engine/douglas_peucker.cpp b/src/engine/douglas_peucker.cpp index 2685b4a2a..e2dc953b6 100644 --- a/src/engine/douglas_peucker.cpp +++ b/src/engine/douglas_peucker.cpp @@ -1,8 +1,9 @@ #include "engine/douglas_peucker.hpp" #include "util/coordinate_calculation.hpp" +#include "util/coordinate.hpp" #include -#include "osrm/coordinate.hpp" +#include #include #include @@ -17,8 +18,12 @@ namespace engine namespace { +// FIXME This algorithm is a very naive approximation that leads to +// problems like (almost) co-linear points not being simplified. +// Switch to real-point-segment distance of projected coordinates struct CoordinatePairCalculator { + CoordinatePairCalculator() = delete; CoordinatePairCalculator(const util::FixedPointCoordinate coordinate_a, const util::FixedPointCoordinate coordinate_b) { @@ -56,44 +61,29 @@ struct CoordinatePairCalculator }; } -void douglasPeucker(std::vector::iterator begin, - std::vector::iterator end, +std::vector +douglasPeucker(std::vector::const_iterator begin, + std::vector::const_iterator end, const unsigned zoom_level) { - using Iter = decltype(begin); - using GeometryRange = std::pair; - - std::stack recursion_stack; + BOOST_ASSERT_MSG(zoom_level < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE, + "unsupported zoom level"); const auto size = std::distance(begin, end); if (size < 2) { - return; + return {}; } - begin->necessary = true; - std::prev(end)->necessary = true; + std::vector is_necessary(size, false); + BOOST_ASSERT(is_necessary.size() >= 2); + is_necessary.front() = true; + is_necessary.back() = true; + using GeometryRange = std::pair; - { - BOOST_ASSERT_MSG(zoom_level < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE, - "unsupported zoom level"); - auto left_border = begin; - auto right_border = std::next(begin); - // Sweep over array and identify those ranges that need to be checked - do - { - // traverse list until new border element found - if (right_border->necessary) - { - // sanity checks - BOOST_ASSERT(left_border->necessary); - BOOST_ASSERT(right_border->necessary); - recursion_stack.emplace(left_border, right_border); - left_border = right_border; - } - ++right_border; - } while (right_border != end); - } + std::stack recursion_stack; + + recursion_stack.emplace(0UL, size-1); // mark locations as 'necessary' by divide-and-conquer while (!recursion_stack.empty()) @@ -102,25 +92,24 @@ void douglasPeucker(std::vector::iterator begin, const GeometryRange pair = recursion_stack.top(); recursion_stack.pop(); // sanity checks - BOOST_ASSERT_MSG(pair.first->necessary, "left border must be necessary"); - BOOST_ASSERT_MSG(pair.second->necessary, "right border must be necessary"); - BOOST_ASSERT_MSG(std::distance(pair.second, end) > 0, "right border outside of geometry"); - BOOST_ASSERT_MSG(std::distance(pair.first, pair.second) >= 0, - "left border on the wrong side"); + BOOST_ASSERT_MSG(is_necessary[pair.first], "left border must be necessary"); + BOOST_ASSERT_MSG(is_necessary[pair.second], "right border must be necessary"); + BOOST_ASSERT_MSG(pair.second < size, "right border outside of geometry"); + BOOST_ASSERT_MSG(pair.first <= pair.second, "left border on the wrong side"); int max_int_distance = 0; - auto farthest_entry_it = pair.second; - const CoordinatePairCalculator dist_calc(pair.first->location, pair.second->location); + auto farthest_entry_index = pair.second; + const CoordinatePairCalculator dist_calc(begin[pair.first], begin[pair.second]); // sweep over range to find the maximum - for (auto it = std::next(pair.first); it != pair.second; ++it) + for (auto idx = pair.first + 1; idx != pair.second; ++idx) { - const int distance = dist_calc(it->location); + const int distance = dist_calc(begin[idx]); // found new feasible maximum? if (distance > max_int_distance && distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) { - farthest_entry_it = it; + farthest_entry_index = idx; max_int_distance = distance; } } @@ -129,17 +118,29 @@ void douglasPeucker(std::vector::iterator begin, if (max_int_distance > detail::DOUGLAS_PEUCKER_THRESHOLDS[zoom_level]) { // mark idx as necessary - farthest_entry_it->necessary = true; - if (1 < std::distance(pair.first, farthest_entry_it)) + is_necessary[farthest_entry_index] = true; + if (pair.first < farthest_entry_index) { - recursion_stack.emplace(pair.first, farthest_entry_it); + recursion_stack.emplace(pair.first, farthest_entry_index); } - if (1 < std::distance(farthest_entry_it, pair.second)) + if (farthest_entry_index < pair.second) { - recursion_stack.emplace(farthest_entry_it, pair.second); + recursion_stack.emplace(farthest_entry_index, pair.second); } } } + + auto simplified_size = std::count(is_necessary.begin(), is_necessary.end(), true); + std::vector simplified_geometry; + simplified_geometry.reserve(simplified_size); + for (auto idx : boost::irange(0UL, size)) + { + if (is_necessary[idx]) + { + simplified_geometry.push_back(begin[idx]); + } + } + return simplified_geometry; } } // ns engine } // ns osrm diff --git a/src/engine/engine.cpp b/src/engine/engine.cpp index 14bd19ecf..48c1a72e5 100644 --- a/src/engine/engine.cpp +++ b/src/engine/engine.cpp @@ -1,15 +1,16 @@ #include "engine/engine.hpp" #include "engine/engine_config.hpp" -#include "engine/route_parameters.hpp" +#include "engine/api/route_parameters.hpp" +#include "engine/status.hpp" -#include "engine/plugins/distance_table.hpp" -#include "engine/plugins/hello_world.hpp" -#include "engine/plugins/nearest.hpp" -#include "engine/plugins/timestamp.hpp" -#include "engine/plugins/trip.hpp" +//#include "engine/plugins/distance_table.hpp" +//#include "engine/plugins/hello_world.hpp" +//#include "engine/plugins/nearest.hpp" +//#include "engine/plugins/timestamp.hpp" +//#include "engine/plugins/trip.hpp" #include "engine/plugins/viaroute.hpp" -#include "engine/plugins/match.hpp" -#include "engine/plugins/tile.hpp" +//#include "engine/plugins/match.hpp" +//#include "engine/plugins/tile.hpp" #include "engine/datafacade/datafacade_base.hpp" #include "engine/datafacade/internal_datafacade.hpp" @@ -35,120 +36,93 @@ namespace osrm namespace engine { +// Abstracted away the query locking into a template function +// Works the same for every plugin. +template +Status RunQuery(const std::unique_ptr &lock, + datafacade::BaseDataFacade &facade, + const ParameterT ¶meters, + PluginT &plugin, + util::json::Object &json_result) +{ + if (!lock) + { + return plugin.HandleRequest(parameters, json_result); + } + + BOOST_ASSERT(lock); + lock->IncreaseQueryCount(); + + auto& shared_facade = static_cast(facade); + shared_facade.CheckAndReloadFacade(); + // Get a shared data lock so that other threads won't update + // things while the query is running + boost::shared_lock data_lock{shared_facade.data_mutex}; + + Status status = plugin.HandleRequest(parameters, json_result); + + lock->DecreaseQueryCount(); + return status; +} + + Engine::Engine(EngineConfig &config) { if (config.use_shared_memory) { - barrier = util::make_unique(); - query_data_facade = new datafacade::SharedDataFacade(); + lock = util::make_unique(); + query_data_facade = util::make_unique(); } else { // populate base path util::populate_base_path(config.server_paths); - query_data_facade = new datafacade::InternalDataFacade( - config.server_paths); + query_data_facade = util::make_unique(config.server_paths); } - using DataFacade = datafacade::BaseDataFacade; - - // The following plugins handle all requests. - RegisterPlugin(new plugins::DistanceTablePlugin( - query_data_facade, config.max_locations_distance_table)); - RegisterPlugin(new plugins::HelloWorldPlugin()); - RegisterPlugin(new plugins::NearestPlugin(query_data_facade)); - RegisterPlugin(new plugins::MapMatchingPlugin(query_data_facade, - config.max_locations_map_matching)); - RegisterPlugin(new plugins::TimestampPlugin(query_data_facade)); - RegisterPlugin( - new plugins::ViaRoutePlugin(query_data_facade, config.max_locations_viaroute)); - RegisterPlugin( - new plugins::RoundTripPlugin(query_data_facade, config.max_locations_trip)); - RegisterPlugin(new plugins::TilePlugin(query_data_facade)); + route_plugin = util::make_unique(*query_data_facade, config.max_locations_viaroute); } -void Engine::RegisterPlugin(plugins::BasePlugin *raw_plugin_ptr) +Status Engine::Route(const api::RouteParameters &route_parameters, + util::json::Object &result) { - std::unique_ptr plugin_ptr(raw_plugin_ptr); - util::SimpleLogger().Write() << "loaded plugin: " << plugin_ptr->GetDescriptor(); - plugin_map[plugin_ptr->GetDescriptor()] = std::move(plugin_ptr); -} - -int Engine::RunQuery(const RouteParameters &route_parameters, util::json::Object &json_result) -{ - const auto &plugin_iterator = plugin_map.find(route_parameters.service); - - if (plugin_map.end() == plugin_iterator) - { - json_result.values["status_message"] = "Service not found"; - return 400; - } - - osrm::engine::plugins::BasePlugin::Status return_code; - increase_concurrent_query_count(); - if (barrier) - { - // Get a shared data lock so that other threads won't update - // things while the query is running - boost::shared_lock data_lock{ - (static_cast *>( - query_data_facade))->data_mutex}; - return_code = plugin_iterator->second->HandleRequest(route_parameters, json_result); - } - else - { - return_code = plugin_iterator->second->HandleRequest(route_parameters, json_result); - } - decrease_concurrent_query_count(); - return static_cast(return_code); + return RunQuery(lock, *query_data_facade, route_parameters, *route_plugin, result); } // decrease number of concurrent queries -void Engine::decrease_concurrent_query_count() +void Engine::EngineLock::DecreaseQueryCount() { - if (!barrier) - { - return; - } // lock query boost::interprocess::scoped_lock query_lock( - barrier->query_mutex); + barrier.query_mutex); // decrement query count - --(barrier->number_of_queries); - BOOST_ASSERT_MSG(0 <= barrier->number_of_queries, "invalid number of queries"); + --(barrier.number_of_queries); + BOOST_ASSERT_MSG(0 <= barrier.number_of_queries, "invalid number of queries"); // notify all processes that were waiting for this condition - if (0 == barrier->number_of_queries) + if (0 == barrier.number_of_queries) { - barrier->no_running_queries_condition.notify_all(); + barrier.no_running_queries_condition.notify_all(); } } // increase number of concurrent queries -void Engine::increase_concurrent_query_count() +void Engine::EngineLock::IncreaseQueryCount() { - if (!barrier) - { - return; - } - // lock update pending boost::interprocess::scoped_lock pending_lock( - barrier->pending_update_mutex); + barrier.pending_update_mutex); // lock query boost::interprocess::scoped_lock query_lock( - barrier->query_mutex); + barrier.query_mutex); // unlock update pending pending_lock.unlock(); // increment query count - ++(barrier->number_of_queries); - - (static_cast *>( - query_data_facade))->CheckAndReloadFacade(); + ++(barrier.number_of_queries); } } } diff --git a/src/engine/guidance/assemble_overview.cpp b/src/engine/guidance/assemble_overview.cpp new file mode 100644 index 000000000..1b4e1ea06 --- /dev/null +++ b/src/engine/guidance/assemble_overview.cpp @@ -0,0 +1,99 @@ +#ifndef ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP +#define ENGINE_GUIDANCE_ASSEMBLE_OVERVIEW_HPP + +#include "engine/guidance/leg_geometry.hpp" +#include "engine/douglas_peucker.hpp" +#include "util/tiles.hpp" + +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace guidance +{ +namespace +{ + +unsigned calculateOverviewZoomLevel(const std::vector &leg_geometries) +{ + int min_lon = std::numeric_limits::max(); + int min_lat = std::numeric_limits::max(); + int max_lon = -std::numeric_limits::max(); + int max_lat = -std::numeric_limits::max(); + + for (const auto &leg_geometry : leg_geometries) + { + for (const auto coord : leg_geometry.locations) + { + min_lon = std::min(min_lon, coord.lon); + max_lon = std::max(max_lon, coord.lon); + min_lat = std::min(min_lat, coord.lat); + max_lat = std::max(max_lat, coord.lat); + } + } + + return util::tiles::getBBMaxZoomTile(min_lon, min_lat, max_lon, max_lat).z; +} + +std::vector simplifyGeometry(const std::vector &leg_geometries, const unsigned zoom_level) +{ + std::vector overview_geometry; + auto leg_index = 0UL; + for (const auto geometry : leg_geometries) + { + auto simplified_geometry = + douglasPeucker(geometry.locations.begin(), geometry.locations.end(), zoom_level); + // not the last leg + if (leg_index < leg_geometries.size() - 1) + { + simplified_geometry.pop_back(); + } + overview_geometry.insert(overview_geometry.end(), simplified_geometry.begin(), + simplified_geometry.end()); + } + return overview_geometry; +} +} + +std::vector +assembleOverview(const std::vector &leg_geometries, const bool use_simplification) +{ + if (use_simplification) + { + const auto zoom_level = calculateOverviewZoomLevel(leg_geometries); + return simplifyGeometry(leg_geometries, zoom_level); + } + BOOST_ASSERT(!use_simplification); + + auto overview_size = std::accumulate(leg_geometries.begin(), leg_geometries.end(), 0, + [](const std::size_t sum, const LegGeometry &leg_geometry) + { + return sum + leg_geometry.locations.size(); + }) - + leg_geometries.size() + 1; + std::vector overview_geometry; + overview_geometry.reserve(overview_size); + + auto leg_index = 0UL; + for (const auto geometry : leg_geometries) + { + auto begin = geometry.locations.begin(); + auto end = geometry.locations.end(); + if (leg_index < leg_geometries.size() - 1) + { + end = std::prev(end); + } + overview_geometry.insert(overview_geometry.end(), begin, end); + } + + return overview_geometry; +} + +} // namespace guidance +} // namespace engine +} // namespace osrm + +#endif diff --git a/src/engine/plugins/viaroute.cpp b/src/engine/plugins/viaroute.cpp new file mode 100644 index 000000000..f558a26c7 --- /dev/null +++ b/src/engine/plugins/viaroute.cpp @@ -0,0 +1,115 @@ +#include "engine/plugins/viaroute.hpp" +#include "engine/datafacade/datafacade_base.hpp" +#include "engine/api/route_api.hpp" +#include "engine/object_encoder.hpp" +#include "engine/status.hpp" + +#include "util/for_each_pair.hpp" +#include "util/integer_range.hpp" +#include "util/json_container.hpp" + +#include + +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ +namespace plugins +{ + +ViaRoutePlugin::ViaRoutePlugin(datafacade::BaseDataFacade &facade_, int max_locations_viaroute) + : BasePlugin(facade_), shortest_path(&facade_, heaps), alternative_path(&facade_, heaps), + direct_shortest_path(&facade_, heaps), max_locations_viaroute(max_locations_viaroute) +{ +} + +Status ViaRoutePlugin::HandleRequest(const api::RouteParameters &route_parameters, + util::json::Object &json_result) +{ + if (max_locations_viaroute > 0 && + (static_cast(route_parameters.coordinates.size()) > max_locations_viaroute)) + { + return Error("too-big", + "Number of entries " + std::to_string(route_parameters.coordinates.size()) + + " is higher than current maximum (" + + std::to_string(max_locations_viaroute) + ")", + json_result); + } + + if (!CheckAllCoordinates(route_parameters.coordinates)) + { + return Error("invalid-value", "Invalid coordinate value.", json_result); + } + + auto phantom_node_pairs = GetPhantomNodes(route_parameters); + if (phantom_node_pairs.size() != route_parameters.coordinates.size()) + { + return Error("no-segment", + std::string("Could not find a matching segment for coordinate ") + + std::to_string(phantom_node_pairs.size()), + json_result); + } + BOOST_ASSERT(phantom_node_pairs.size() == route_parameters.coordinates.size()); + + auto snapped_phantoms = SnapPhantomNodes(phantom_node_pairs); + + InternalRouteResult raw_route; + auto build_phantom_pairs = [&raw_route](const PhantomNode &first_node, + const PhantomNode &second_node) + { + raw_route.segment_end_coordinates.push_back(PhantomNodes{first_node, second_node}); + }; + util::for_each_pair(snapped_phantoms, build_phantom_pairs); + + if (1 == raw_route.segment_end_coordinates.size()) + { + if (route_parameters.alternative && facade.GetCoreSize() == 0) + { + alternative_path(raw_route.segment_end_coordinates.front(), raw_route); + } + else + { + direct_shortest_path(raw_route.segment_end_coordinates, raw_route); + } + } + else + { + shortest_path(raw_route.segment_end_coordinates, route_parameters.uturns, raw_route); + } + + // we can only know this after the fact, different SCC ids still + // allow for connection in one direction. + if (raw_route.is_valid()) + { + api::RouteAPI route_api{BasePlugin::facade, route_parameters}; + route_api.MakeResponse(raw_route, json_result); + } + else + { + auto first_component_id = snapped_phantoms.front().component.id; + auto not_in_same_component = std::any_of(snapped_phantoms.begin(), snapped_phantoms.end(), + [first_component_id](const PhantomNode &node) + { + return node.component.id != first_component_id; + }); + + if (not_in_same_component) + { + return Error("no-route", "Impossible route between points", json_result); + } + else + { + return Error("no-route", "No route found between points", json_result); + } + } + + return Status::Ok; +} +} +} +} diff --git a/src/engine/polyline_compressor.cpp b/src/engine/polyline_compressor.cpp index f057c0522..12887030a 100644 --- a/src/engine/polyline_compressor.cpp +++ b/src/engine/polyline_compressor.cpp @@ -1,8 +1,10 @@ #include "engine/polyline_compressor.hpp" +#include #include #include #include +#include namespace osrm { @@ -55,31 +57,30 @@ std::string encode(std::vector &numbers) } } // anonymous ns -std::string polylineEncode(const std::vector &polyline) + +std::string encodePolyline(CoordVectorForwardIter begin, CoordVectorForwardIter end) { - if (polyline.empty()) + auto size = std::distance(begin, end); + if (size == 0) { return {}; } std::vector delta_numbers; - delta_numbers.reserve((polyline.size() - 1) * 2); + BOOST_ASSERT(size > 0); + delta_numbers.reserve((size - 1) * 2); util::FixedPointCoordinate previous_coordinate = {0, 0}; - for (const auto &segment : polyline) + std::for_each(begin, end, [&delta_numbers, &previous_coordinate](const FixedPointCoordinate loc) { - if (segment.necessary) - { - const int lat_diff = segment.location.lat - previous_coordinate.lat; - const int lon_diff = segment.location.lon - previous_coordinate.lon; + const int lat_diff = (loc.lat - previous_coordinate.lat) * detail::COORDINATE_TO_POLYLINE; + const int lon_diff = (loc.lon - previous_coordinate.lon) * detail::COORDINATE_TO_POLYLINE; delta_numbers.emplace_back(lat_diff); delta_numbers.emplace_back(lon_diff); - previous_coordinate = segment.location; - } - } + previous_coordinate = loc; + }); return encode(delta_numbers); } - -std::vector polylineDecode(const std::string &geometry_string) +std::vector decodePolyline(const std::string &geometry_string) { std::vector new_coordinates; int index = 0, len = geometry_string.size(); @@ -109,8 +110,8 @@ std::vector polylineDecode(const std::string &geomet lng += dlng; util::FixedPointCoordinate p; - p.lat = COORDINATE_PRECISION * (((double)lat / 1E6)); - p.lon = COORDINATE_PRECISION * (((double)lng / 1E6)); + p.lat = lat * detail::POLYLINE_TO_COORDINATE; + p.lon = lng * detail::POLYLINE_TO_COORDINATE; new_coordinates.push_back(p); } diff --git a/src/engine/polyline_formatter.cpp b/src/engine/polyline_formatter.cpp deleted file mode 100644 index 5039e2375..000000000 --- a/src/engine/polyline_formatter.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "engine/polyline_formatter.hpp" -#include "engine/polyline_compressor.hpp" -#include "osrm/coordinate.hpp" - -namespace osrm -{ -namespace engine -{ - -util::json::String polylineEncodeAsJSON(const std::vector &polyline) -{ - return util::json::String(polylineEncode(polyline)); -} - -util::json::Array polylineUnencodedAsJSON(const std::vector &polyline) -{ - util::json::Array json_geometry_array; - for (const auto &segment : polyline) - { - if (segment.necessary) - { - util::json::Array json_coordinate; - json_coordinate.values.push_back(segment.location.lat / COORDINATE_PRECISION); - json_coordinate.values.push_back(segment.location.lon / COORDINATE_PRECISION); - json_geometry_array.values.push_back(json_coordinate); - } - } - return json_geometry_array; -} -} -} diff --git a/src/engine/route_parameters.cpp b/src/engine/route_parameters.cpp deleted file mode 100644 index e0541d7d0..000000000 --- a/src/engine/route_parameters.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "engine/route_parameters.hpp" -#include "util/coordinate.hpp" - -#include "engine/polyline_compressor.hpp" - -#include -#include - -namespace osrm -{ -namespace engine -{ - -RouteParameters::RouteParameters() - : zoom_level(18), print_instructions(false), alternate_route(true), geometry(true), - compression(true), deprecatedAPI(false), uturn_default(false), classify(false), - matching_beta(5), gps_precision(5), check_sum(-1), num_results(1) -{ -} - -void RouteParameters::SetZoomLevel(const short level) -{ - if (18 >= level && 0 <= level) - { - zoom_level = level; - } -} - -void RouteParameters::SetNumberOfResults(const short number) -{ - if (number > 0 && number <= 100) - { - num_results = number; - } -} - -void RouteParameters::SetAlternateRouteFlag(const bool flag) { alternate_route = flag; } - -void RouteParameters::SetUTurn(const bool flag) -{ - // the API grammar should make sure this never happens - BOOST_ASSERT(!uturns.empty()); - uturns.back() = flag; -} - -void RouteParameters::SetAllUTurns(const bool flag) -{ - // if the flag flips the default, then we erase everything. - if (flag) - { - uturn_default = flag; - uturns.clear(); - uturns.resize(coordinates.size(), uturn_default); - } -} - -void RouteParameters::SetDeprecatedAPIFlag(const std::string & /*unused*/) { deprecatedAPI = true; } - -void RouteParameters::SetChecksum(const unsigned sum) { check_sum = sum; } - -void RouteParameters::SetInstructionFlag(const bool flag) { print_instructions = flag; } - -void RouteParameters::SetService(const std::string &service_string) { service = service_string; } - -void RouteParameters::SetClassify(const bool flag) { classify = flag; } - -void RouteParameters::SetMatchingBeta(const double beta) { matching_beta = beta; } - -void RouteParameters::SetGPSPrecision(const double precision) { gps_precision = precision; } - -void RouteParameters::SetOutputFormat(const std::string &format) { output_format = format; } - -void RouteParameters::SetJSONpParameter(const std::string ¶meter) -{ - jsonp_parameter = parameter; -} - -void RouteParameters::AddHint(const std::string &hint) -{ - hints.resize(coordinates.size()); - if (!hints.empty()) - { - hints.back() = hint; - } -} - -void RouteParameters::AddTimestamp(const unsigned timestamp) -{ - timestamps.resize(coordinates.size()); - if (!timestamps.empty()) - { - timestamps.back() = timestamp; - } -} - -bool RouteParameters::AddBearing(int bearing, boost::optional range) -{ - if (bearing < 0 || bearing > 359) - return false; - if (range && (*range < 0 || *range > 180)) - return false; - bearings.emplace_back(std::make_pair(bearing, range)); - return true; -} - -void RouteParameters::SetLanguage(const std::string &language_string) -{ - language = language_string; -} - -void RouteParameters::SetGeometryFlag(const bool flag) { geometry = flag; } - -void RouteParameters::SetCompressionFlag(const bool flag) { compression = flag; } - -void RouteParameters::AddCoordinate(const double latitude, const double longitude) -{ - coordinates.emplace_back(static_cast(COORDINATE_PRECISION * latitude), - static_cast(COORDINATE_PRECISION * longitude)); - is_source.push_back(true); - is_destination.push_back(true); - uturns.push_back(uturn_default); -} - -void RouteParameters::AddDestination(const double latitude, const double longitude) -{ - coordinates.emplace_back(static_cast(COORDINATE_PRECISION * latitude), - static_cast(COORDINATE_PRECISION * longitude)); - is_source.push_back(false); - is_destination.push_back(true); - uturns.push_back(uturn_default); -} - -void RouteParameters::AddSource(const double latitude, const double longitude) -{ - coordinates.emplace_back(static_cast(COORDINATE_PRECISION * latitude), - static_cast(COORDINATE_PRECISION * longitude)); - is_source.push_back(true); - is_destination.push_back(false); - uturns.push_back(uturn_default); -} - -void RouteParameters::SetCoordinatesFromGeometry(const std::string &geometry_string) -{ - coordinates = polylineDecode(geometry_string); - uturns.clear(); - uturns.resize(coordinates.size(), uturn_default); -} - -bool RouteParameters::SetX(const int x_) -{ - x = x_; - return true; -} -bool RouteParameters::SetZ(const int z_) -{ - if (z_ < 12) - { - return false; - } - - z = z_; - return true; -} -bool RouteParameters::SetY(const int y_) -{ - y = y_; - return true; -} -} -} diff --git a/src/engine/search_engine_data.cpp b/src/engine/search_engine_data.cpp index 9aeb87d39..72963bbba 100644 --- a/src/engine/search_engine_data.cpp +++ b/src/engine/search_engine_data.cpp @@ -7,6 +7,13 @@ namespace osrm namespace engine { +SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_1; +SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_1; +SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_2; +SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_2; +SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_3; +SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_3; + void SearchEngineData::InitializeOrClearFirstThreadLocalStorage(const unsigned number_of_nodes) { if (forward_heap_1.get()) diff --git a/src/osrm/osrm.cpp b/src/osrm/osrm.cpp index 1100b1df9..35dc46949 100644 --- a/src/osrm/osrm.cpp +++ b/src/osrm/osrm.cpp @@ -1,7 +1,8 @@ #include "osrm/osrm.hpp" #include "engine/engine.hpp" +#include "engine/status.hpp" #include "engine/engine_config.hpp" -#include "engine/plugins/plugin_base.hpp" +#include "engine/plugins/viaroute.hpp" #include "storage/shared_barriers.hpp" #include "util/make_unique.hpp" @@ -14,9 +15,9 @@ OSRM::OSRM(engine::EngineConfig &config_) : engine_(util::make_uniqueRunQuery(route_parameters, json_result); + return engine_->Route(route_parameters, json_result); } } diff --git a/src/server/api/route_parameters_parser.cpp b/src/server/api/route_parameters_parser.cpp new file mode 100644 index 000000000..1a0ff2075 --- /dev/null +++ b/src/server/api/route_parameters_parser.cpp @@ -0,0 +1,28 @@ +#include "server/api/route_parameters_parser.hpp" +#include "server/api/route_parameters_grammar.hpp" + +namespace osrm +{ +namespace server +{ +namespace api +{ + +boost::optional parseRouteParameters(std::string::iterator& iter, std::string::iterator end) +{ + RouteParametersGrammar grammar; + const auto result = boost::spirit::qi::parse(iter, end, grammar); + + boost::optional parameters; + if (result && iter == end) + { + parameters = std::move(grammar.route_parameters); + } + + return parameters; +} + +} +} +} + diff --git a/src/server/api/url_parser.cpp b/src/server/api/url_parser.cpp new file mode 100644 index 000000000..90768bd35 --- /dev/null +++ b/src/server/api/url_parser.cpp @@ -0,0 +1,104 @@ +#include "server/api/url_parser.hpp" + +#include "engine/polyline_compressor.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace server +{ +namespace api +{ + +namespace +{ + +namespace qi = boost::spirit::qi; +using Iterator = std::string::iterator; +struct URLGrammar : boost::spirit::qi::grammar +{ + URLGrammar() : URLGrammar::base_type(url_rule) + { + const auto set_service = [this](std::string &service) + { + parsed_url.service = std::move(service); + }; + const auto set_version = [this](const unsigned version) + { + parsed_url.version = version; + }; + const auto set_profile = [this](std::string &profile) + { + parsed_url.profile = std::move(profile); + }; + const auto set_options = [this](std::string &options) + { + parsed_url.options = std::move(options); + }; + const auto add_coordinate = [this](const boost::fusion::vector &lonLat) + { + parsed_url.coordinates.emplace_back( + util::FixedPointCoordinate(boost::fusion::at_c<1>(lonLat) * COORDINATE_PRECISION, + boost::fusion::at_c<0>(lonLat) * COORDINATE_PRECISION)); + }; + const auto polyline_to_coordinates = [this](const std::string &polyline) + { + parsed_url.coordinates = engine::decodePolyline(polyline); + }; + + alpha_numeral = qi::char_("a-zA-Z0-9"); + polyline_chars = qi::char_("a-zA-Z0-9_.--[]{}@?|\\%~`^"); + all_chars = polyline_chars | qi::char_("=,;:&"); + + polyline_rule = qi::as_string[qi::lit("polyline(") >> +polyline_chars >> + qi::lit(")")][polyline_to_coordinates]; + location_rule = (qi::double_ >> qi::lit(',') >> qi::double_)[add_coordinate]; + query_rule = (location_rule % ';') | polyline_rule; + + service_rule = +alpha_numeral; + version_rule = qi::uint_; + profile_rule = +alpha_numeral; + options_rule = *all_chars; + + url_rule = qi::lit('/') >> service_rule[set_service] >> qi::lit('/') >> qi::lit('v') >> + version_rule[set_version] >> qi::lit('/') >> profile_rule[set_profile] >> + qi::lit('/') >> query_rule >> -(qi::lit('?') >> options_rule[set_options]); + } + + ParsedURL parsed_url; + + qi::rule url_rule; + qi::rule options_rule, service_rule, profile_rule; + qi::rule query_rule, polyline_rule, location_rule; + qi::rule version_rule; + qi::rule alpha_numeral, all_chars, polyline_chars; +}; +} + +boost::optional parseURL(std::string::iterator &iter, std::string::iterator end) +{ + boost::optional parsed_url; + + URLGrammar grammar; + const auto result = boost::spirit::qi::parse(iter, end, grammar); + + if (result && iter == end) + { + parsed_url = std::move(grammar.parsed_url); + } + + return parsed_url; +} +} +} +} diff --git a/src/server/connection.cpp b/src/server/connection.cpp index 9115ea4c4..e7725fb40 100644 --- a/src/server/connection.cpp +++ b/src/server/connection.cpp @@ -50,7 +50,7 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t if (result == RequestParser::RequestStatus::valid) { current_request.endpoint = TCP_socket.remote_endpoint().address(); - request_handler.handle_request(current_request, current_reply); + request_handler.HandleRequest(current_request, current_reply); // compress the result w/ gzip/deflate if requested switch (compression_type) diff --git a/src/server/http/reply.cpp b/src/server/http/reply.cpp index efea8f628..88478f9b4 100644 --- a/src/server/http/reply.cpp +++ b/src/server/http/reply.cpp @@ -10,9 +10,9 @@ namespace http { const char ok_html[] = ""; -const char bad_request_html[] = "{\"status\": 400,\"status_message\":\"Bad Request\"}"; +const char bad_request_html[] = ""; const char internal_server_error_html[] = - "{\"status\": 500,\"status_message\":\"Internal Server Error\"}"; + "{\"code\": \"internal-error\",\"message\":\"Internal Server Error\"}"; const char seperators[] = {':', ' '}; const char crlf[] = {'\r', '\n'}; const std::string http_ok_string = "HTTP/1.0 200 OK\r\n"; diff --git a/src/server/request_handler.cpp b/src/server/request_handler.cpp index 4651c877d..b413ecf03 100644 --- a/src/server/request_handler.cpp +++ b/src/server/request_handler.cpp @@ -1,6 +1,7 @@ #include "server/request_handler.hpp" +#include "server/service_handler.hpp" -#include "server/api_grammar.hpp" +#include "server/api/url_parser.hpp" #include "server/http/reply.hpp" #include "server/http/request.hpp" @@ -9,7 +10,7 @@ #include "util/string_util.hpp" #include "util/typedefs.hpp" -#include "engine/route_parameters.hpp" +#include "engine/status.hpp" #include "util/json_container.hpp" #include "osrm/osrm.hpp" @@ -28,11 +29,22 @@ namespace osrm namespace server { -RequestHandler::RequestHandler() : routing_machine(nullptr) {} -void RequestHandler::handle_request(const http::request ¤t_request, +void RequestHandler::RegisterServiceHandler(std::unique_ptr service_handler_) +{ + service_handler = std::move(service_handler_); +} + +void RequestHandler::HandleRequest(const http::request ¤t_request, http::reply ¤t_reply) { + if (!service_handler) + { + current_reply = http::reply::stock_reply(http::reply::internal_server_error); + util::SimpleLogger().Write(logWARNING) << "No service handler registered." << std::endl; + return; + } + util::json::Object json_result; // parse command @@ -67,84 +79,49 @@ void RequestHandler::handle_request(const http::request ¤t_request, << current_request.agent << (0 == current_request.agent.length() ? "- " : " ") << request_string; - engine::RouteParameters route_parameters; - APIGrammarParser api_parser(&route_parameters); - auto api_iterator = request_string.begin(); - const bool result = - boost::spirit::qi::parse(api_iterator, request_string.end(), api_parser); + auto maybe_parsed_url = api::parseURL(api_iterator, request_string.end());; // check if the was an error with the request - if (result && api_iterator == request_string.end()) + if (maybe_parsed_url && api_iterator == request_string.end()) { - // parsing done, lets call the right plugin to handle the request - BOOST_ASSERT_MSG(routing_machine != nullptr, "pointer not init'ed"); - - if (!route_parameters.jsonp_parameter.empty()) - { // prepend response with jsonp parameter - const std::string json_p = (route_parameters.jsonp_parameter + "("); - current_reply.content.insert(current_reply.content.end(), json_p.begin(), - json_p.end()); - } - - const int return_code = routing_machine->RunQuery(route_parameters, json_result); - json_result.values["status"] = return_code; - // 4xx bad request return code - if (return_code / 100 == 4) + const engine::Status status = service_handler->RunQuery(std::move(*maybe_parsed_url), json_result); + if (status != engine::Status::Ok) { + // 4xx bad request return code current_reply.status = http::reply::bad_request; - current_reply.content.clear(); - route_parameters.output_format.clear(); } else { - // 2xx valid request - BOOST_ASSERT(return_code / 100 == 2); + BOOST_ASSERT(status == engine::Status::Ok); } } else { const auto position = std::distance(request_string.begin(), api_iterator); + const auto context_begin = request_string.begin() + std::max(position - 3UL, 0UL); + const auto context_end = request_string.begin() + std::min(position + 3UL, request_string.size()); + std::string context(context_begin, context_end); current_reply.status = http::reply::bad_request; - json_result.values["status"] = http::reply::bad_request; - json_result.values["status_message"] = - "Query string malformed close to position " + std::to_string(position); + json_result.values["code"] = "invalid-url"; + json_result.values["message"] = + "URL string malformed close to position " + std::to_string(position) + ": \"" + context + "\""; } current_reply.headers.emplace_back("Access-Control-Allow-Origin", "*"); current_reply.headers.emplace_back("Access-Control-Allow-Methods", "GET"); current_reply.headers.emplace_back("Access-Control-Allow-Headers", "X-Requested-With, Content-Type"); + current_reply.headers.emplace_back("Content-Type", "application/json; charset=UTF-8"); + current_reply.headers.emplace_back("Content-Disposition", + "inline; filename=\"response.json\""); - if (route_parameters.service == "tile" && json_result.values.find("pbf") != json_result.values.end()) - { - std::copy(json_result.values["pbf"].get().value.cbegin(), - json_result.values["pbf"].get().value.cend(), - std::back_inserter(current_reply.content)); + util::json::render(current_reply.content, json_result); - current_reply.headers.emplace_back("Content-Type", "application/x-protobuf"); - } - else if (route_parameters.jsonp_parameter.empty()) - { // json file - util::json::render(current_reply.content, json_result); - current_reply.headers.emplace_back("Content-Type", "application/json; charset=UTF-8"); - current_reply.headers.emplace_back("Content-Disposition", - "inline; filename=\"response.json\""); - } - else - { // jsonp - util::json::render(current_reply.content, json_result); - current_reply.headers.emplace_back("Content-Type", "text/javascript; charset=UTF-8"); - current_reply.headers.emplace_back("Content-Disposition", - "inline; filename=\"response.js\""); - } + // set headers current_reply.headers.emplace_back("Content-Length", std::to_string(current_reply.content.size())); - if (!route_parameters.jsonp_parameter.empty()) - { // append brace to jsonp response - current_reply.content.push_back(')'); - } } catch (const std::exception &e) { @@ -154,6 +131,5 @@ void RequestHandler::handle_request(const http::request ¤t_request, } } -void RequestHandler::RegisterRoutingMachine(OSRM *osrm) { routing_machine = osrm; } } } diff --git a/src/server/service/route_service.cpp b/src/server/service/route_service.cpp new file mode 100644 index 000000000..dd72daa62 --- /dev/null +++ b/src/server/service/route_service.cpp @@ -0,0 +1,83 @@ +#include "server/service/route_service.hpp" + +#include "engine/api/route_parameters.hpp" +#include "server/api/route_parameters_parser.hpp" + +#include "util/json_container.hpp" + +#include + +namespace osrm +{ +namespace server +{ +namespace service +{ + +namespace { + +const constexpr char PARAMETER_SIZE_MISMATCH_MSG[] = "Number of elements in %1% size %2% does not match coordinate size %3%"; + +template +bool constrainParamSize(const char* msg_template, const char* name, const ParamT& param, const std::size_t target_size, std::string& help) +{ + if (param.size() > 0 && param.size() != target_size) + { + help = (boost::format(msg_template) % name % param.size() % target_size).str(); + return true; + } + return false; +} + +std::string getWrongOptionHelp(const engine::api::RouteParameters& parameters) +{ + std::string help; + + const auto coord_size = parameters.coordinates.size(); + + const bool param_size_mismatch = constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "hints", parameters.hints, coord_size, help) + || constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "bearings", parameters.bearings, coord_size, help) + || constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "radiuses", parameters.radiuses, coord_size, help) + || constrainParamSize(PARAMETER_SIZE_MISMATCH_MSG, "uturns", parameters.uturns, coord_size, help); + + if (!param_size_mismatch && parameters.coordinates.size() < 2) + { + help = "Number of coordinates needs to be at least two."; + } + + return help; +} +} // anon. ns + +engine::Status RouteService::RunQuery(std::vector coordinates, + std::string &options, + util::json::Object &json_result) +{ + + auto options_iterator = options.begin(); + auto parameters = api::parseRouteParameters(options_iterator, options.end()); + if (!parameters || options_iterator != options.end()) + { + const auto position = std::distance(options.begin(), options_iterator); + json_result.values["code"] = "invalid-options"; + json_result.values["message"] = + "Options string malformed close to position " + std::to_string(position); + return engine::Status::Error; + } + + BOOST_ASSERT(parameters); + parameters->coordinates = std::move(coordinates); + + if (!parameters->IsValid()) + { + json_result.values["code"] = "invalid-options"; + json_result.values["message"] = getWrongOptionHelp(*parameters); + return engine::Status::Error; + } + BOOST_ASSERT(parameters->IsValid()); + + return BaseService::routing_machine.Route(*parameters, json_result); +} +} +} +} diff --git a/src/server/service_handler.cpp b/src/server/service_handler.cpp new file mode 100644 index 000000000..49365edb4 --- /dev/null +++ b/src/server/service_handler.cpp @@ -0,0 +1,39 @@ +#include "server/service_handler.hpp" + +#include "server/service/route_service.hpp" + +#include "server/api/parsed_url.hpp" +#include "util/json_util.hpp" +#include "util/make_unique.hpp" + +namespace osrm +{ +namespace server +{ + ServiceHandler::ServiceHandler(osrm::EngineConfig &config) : routing_machine(config) + { + service_map["route"] = util::make_unique(routing_machine); + } + + engine::Status ServiceHandler::RunQuery(api::ParsedURL parsed_url, util::json::Object &json_result) + { + const auto& service_iter = service_map.find(parsed_url.service); + if (service_iter == service_map.end()) + { + json_result.values["code"] = "invalid-service"; + json_result.values["message"] = "Service " + parsed_url.service + " not found!"; + return engine::Status::Error; + } + auto &service = service_iter->second; + + if (service->GetVersion() != parsed_url.version) + { + json_result.values["code"] = "invalid-version"; + json_result.values["message"] = "Service " + parsed_url.service + " not found!"; + return engine::Status::Error; + } + + return service->RunQuery(std::move(parsed_url.coordinates), parsed_url.options, json_result); + } +} +} diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp index 9f53a50e7..f7cd366c4 100644 --- a/src/storage/storage.cpp +++ b/src/storage/storage.cpp @@ -39,7 +39,7 @@ namespace storage { using RTreeLeaf = - typename engine::datafacade::BaseDataFacade::RTreeLeaf; + typename engine::datafacade::BaseDataFacade::RTreeLeaf; using RTreeNode = util::StaticRTree::vector, true>::TreeNode; diff --git a/src/tools/routed.cpp b/src/tools/routed.cpp index dba1e43cb..f8b98e8c0 100644 --- a/src/tools/routed.cpp +++ b/src/tools/routed.cpp @@ -1,5 +1,6 @@ #include "server/server.hpp" #include "util/routed_options.hpp" +#include "util/make_unique.hpp" #include "util/simple_logger.hpp" #include "osrm/osrm.hpp" @@ -101,10 +102,10 @@ int main(int argc, const char *argv[]) try pthread_sigmask(SIG_BLOCK, &new_mask, &old_mask); #endif - OSRM osrm_lib(config); auto routing_server = server::Server::CreateServer(ip_address, ip_port, requested_thread_num); + auto service_handler = util::make_unique(config); - routing_server->GetRequestHandlerPtr().RegisterRoutingMachine(&osrm_lib); + routing_server->RegisterServiceHandler(std::move(service_handler)); if (trial_run) { diff --git a/src/util/coordinate.cpp b/src/util/coordinate.cpp index da1f9db9e..2d6c75df5 100644 --- a/src/util/coordinate.cpp +++ b/src/util/coordinate.cpp @@ -45,12 +45,17 @@ bool FixedPointCoordinate::IsValid() const lon > 180 * COORDINATE_PRECISION || lon < -180 * COORDINATE_PRECISION); } -bool FixedPointCoordinate::operator==(const FixedPointCoordinate &other) const +bool operator==(const FixedPointCoordinate lhs, const FixedPointCoordinate rhs) { - return lat == other.lat && lon == other.lon; + return lhs.lat == rhs.lat && lhs.lon == rhs.lon; } -std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate &coordinate) +bool operator!=(const FixedPointCoordinate lhs, const FixedPointCoordinate rhs) +{ + return !(lhs == rhs); +} + +std::ostream &operator<<(std::ostream &out, const FixedPointCoordinate coordinate) { out << "(" << static_cast(coordinate.lat / COORDINATE_PRECISION) << "," << static_cast(coordinate.lon / COORDINATE_PRECISION) << ")"; diff --git a/unit_tests/engine/douglas_peucker.cpp b/unit_tests/engine/douglas_peucker.cpp index ec25a88c6..93143d071 100644 --- a/unit_tests/engine/douglas_peucker.cpp +++ b/unit_tests/engine/douglas_peucker.cpp @@ -1,5 +1,4 @@ #include "engine/douglas_peucker.hpp" -#include "engine/segment_information.hpp" #include #include @@ -13,13 +12,7 @@ BOOST_AUTO_TEST_SUITE(douglas_peucker_simplification) using namespace osrm; using namespace osrm::engine; -SegmentInformation getTestInfo(int lat, int lon, bool necessary) -{ - return SegmentInformation(util::FixedPointCoordinate(lat, lon), 0, 0, 0, - extractor::TurnInstruction::HeadOn, necessary, false, 0); -} - -BOOST_AUTO_TEST_CASE(all_necessary_test) +BOOST_AUTO_TEST_CASE(removed_middle_test) { /* x @@ -27,19 +20,23 @@ BOOST_AUTO_TEST_CASE(all_necessary_test) x \ / \ x x - / */ - std::vector info = {getTestInfo(5, 5, true), - getTestInfo(6, 6, true), - getTestInfo(10, 10, true), - getTestInfo(5, 15, true)}; - for (unsigned z = 0; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++) + std::vector coordinates = { + util::FixedPointCoordinate(5 * COORDINATE_PRECISION, 5 * COORDINATE_PRECISION), + util::FixedPointCoordinate(6 * COORDINATE_PRECISION, 6 * COORDINATE_PRECISION), + util::FixedPointCoordinate(10 * COORDINATE_PRECISION, 10 * COORDINATE_PRECISION), + util::FixedPointCoordinate(5 * COORDINATE_PRECISION, 15 * COORDINATE_PRECISION)}; + + // FIXME this test fails for everything below z4 because the DP algorithms + // only used a naive distance measurement + //for (unsigned z = 0; z < detail::DOUGLAS_PEUCKER_THRESHOLDS_SIZE; z++) + for (unsigned z = 0; z < 2; z++) { - douglasPeucker(info, z); - for (const auto &i : info) - { - BOOST_CHECK_EQUAL(i.necessary, true); - } + auto result = douglasPeucker(coordinates, z); + BOOST_CHECK_EQUAL(result.size(), 3); + BOOST_CHECK_EQUAL(result[0], coordinates[0]); + BOOST_CHECK_EQUAL(result[1], coordinates[2]); + BOOST_CHECK_EQUAL(result[2], coordinates[3]); } } @@ -54,26 +51,27 @@ BOOST_AUTO_TEST_CASE(remove_second_node_test) | x */ - std::vector info = { - getTestInfo(5 * COORDINATE_PRECISION, 5 * COORDINATE_PRECISION, true), - getTestInfo(5 * COORDINATE_PRECISION, - 5 * COORDINATE_PRECISION + detail::DOUGLAS_PEUCKER_THRESHOLDS[z], false), - getTestInfo(10 * COORDINATE_PRECISION, 10 * COORDINATE_PRECISION, false), - getTestInfo(10 * COORDINATE_PRECISION, - 10 + COORDINATE_PRECISION + detail::DOUGLAS_PEUCKER_THRESHOLDS[z] * 2, - false), - getTestInfo(5 * COORDINATE_PRECISION, 15 * COORDINATE_PRECISION, false), - getTestInfo(5 * COORDINATE_PRECISION + detail::DOUGLAS_PEUCKER_THRESHOLDS[z], - 15 * COORDINATE_PRECISION, true), + std::vector input = { + util::FixedPointCoordinate(5 * COORDINATE_PRECISION, 5 * COORDINATE_PRECISION), + util::FixedPointCoordinate(5 * COORDINATE_PRECISION, + 5 * COORDINATE_PRECISION + + detail::DOUGLAS_PEUCKER_THRESHOLDS[z]), + util::FixedPointCoordinate(10 * COORDINATE_PRECISION, 10 * COORDINATE_PRECISION), + util::FixedPointCoordinate(10 * COORDINATE_PRECISION, + 10 + COORDINATE_PRECISION + + detail::DOUGLAS_PEUCKER_THRESHOLDS[z] * 2), + util::FixedPointCoordinate(5 * COORDINATE_PRECISION, 15 * COORDINATE_PRECISION), + util::FixedPointCoordinate(5 * COORDINATE_PRECISION + + detail::DOUGLAS_PEUCKER_THRESHOLDS[z], + 15 * COORDINATE_PRECISION), }; BOOST_TEST_MESSAGE("Threshold (" << z << "): " << detail::DOUGLAS_PEUCKER_THRESHOLDS[z]); - douglasPeucker(info, z); - BOOST_CHECK_EQUAL(info[0].necessary, true); - BOOST_CHECK_EQUAL(info[1].necessary, false); - BOOST_CHECK_EQUAL(info[2].necessary, true); - BOOST_CHECK_EQUAL(info[3].necessary, true); - BOOST_CHECK_EQUAL(info[4].necessary, false); - BOOST_CHECK_EQUAL(info[5].necessary, true); + auto result = douglasPeucker(input, z); + BOOST_CHECK_EQUAL(result.size(), 4); + BOOST_CHECK_EQUAL(input[0], result[0]); + BOOST_CHECK_EQUAL(input[2], result[1]); + BOOST_CHECK_EQUAL(input[3], result[2]); + BOOST_CHECK_EQUAL(input[5], result[3]); } } diff --git a/unit_tests/engine/geometry_string.cpp b/unit_tests/engine/geometry_string.cpp index 235df9b6b..b4fde0287 100644 --- a/unit_tests/engine/geometry_string.cpp +++ b/unit_tests/engine/geometry_string.cpp @@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE(decode) { // Polyline string for the 5 coordinates const std::string polyline = "_gjaR_gjaR_pR_ibE_pR_ibE_pR_ibE_pR_ibE"; - const auto coords = polylineDecode(polyline); + const auto coords = decodePolyline(polyline); // Test coordinates; these would be the coordinates we give the loc parameter, // e.g. loc=10.00,10.0&loc=10.01,10.1... diff --git a/unit_tests/server/route_parameters_parser.cpp b/unit_tests/server/route_parameters_parser.cpp new file mode 100644 index 000000000..339d7d796 --- /dev/null +++ b/unit_tests/server/route_parameters_parser.cpp @@ -0,0 +1,168 @@ +#include "server/api/route_parameters_parser.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace api +{ +std::ostream &operator<<(std::ostream &out, api::RouteParameters::GeometriesType geometries) +{ + switch (geometries) + { + case api::RouteParameters::GeometriesType::GeoJSON: + out << "GeoJSON"; + break; + case api::RouteParameters::GeometriesType::Polyline: + out << "Polyline"; + break; + default: + BOOST_ASSERT_MSG(false, "GeometriesType not fully captured"); + } + return out; +} +std::ostream &operator<<(std::ostream &out, api::RouteParameters::OverviewType overview) +{ + switch (overview) + { + case api::RouteParameters::OverviewType::False: + out << "False"; + break; + case api::RouteParameters::OverviewType::Full: + out << "Full"; + break; + case api::RouteParameters::OverviewType::Simplified: + out << "Simplified"; + break; + default: + BOOST_ASSERT_MSG(false, "OverviewType not fully captured"); + } + return out; +} +std::ostream &operator<<(std::ostream &out, api::RouteParameters::Bearing bearing) +{ + out << bearing.bearing << "," << bearing.range; + return out; +} +} +} +} + +#include +#include +#include + +#define CHECK_EQUAL_RANGE(R1, R2) \ + BOOST_CHECK_EQUAL_COLLECTIONS(R1.begin(), R1.end(), R2.begin(), R2.end()); + +BOOST_AUTO_TEST_SUITE(api_route_parameters_parser) + +using namespace osrm; +using namespace osrm::server; + +// returns distance to front +std::size_t testInvalidOptions(std::string options) +{ + auto iter = options.begin(); + auto result = api::parseRouteParameters(iter, options.end()); + BOOST_CHECK(!result); + return std::distance(options.begin(), iter); +} + +BOOST_AUTO_TEST_CASE(invalid_urls) +{ + BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&bla=foo"), 14UL); + BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&bearings=foo"), 24UL); + BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&uturns=foo"), 22UL); + BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&radiuses=foo"), 24UL); + BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&hints=foo"), 14UL); + BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&geometries=foo"), 14UL); + BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&overview=foo"), 14UL); + BOOST_CHECK_EQUAL(testInvalidOptions("overview=false&alternative=foo"), 14UL); +} + +BOOST_AUTO_TEST_CASE(valid_urls) +{ + engine::api::RouteParameters reference_1{}; + auto result_1 = api::parseRouteParameters(""); + BOOST_CHECK(result_1); + BOOST_CHECK_EQUAL(reference_1.steps, result_1->steps); + BOOST_CHECK_EQUAL(reference_1.alternative, result_1->alternative); + BOOST_CHECK_EQUAL(reference_1.geometries, result_1->geometries); + BOOST_CHECK_EQUAL(reference_1.overview, result_1->overview); + CHECK_EQUAL_RANGE(reference_1.uturns, result_1->uturns); + CHECK_EQUAL_RANGE(reference_1.bearings, result_1->bearings); + CHECK_EQUAL_RANGE(reference_1.radiuses, result_1->radiuses); + CHECK_EQUAL_RANGE(reference_1.coordinates, result_1->coordinates); + + // bool steps = true; + // bool alternative = true; + // GeometriesType geometries = GeometriesType::Polyline; + // OverviewType overview = OverviewType::False; + // std::vector> uturns; + + engine::api::RouteParameters reference_2{}; + auto result_2 = api::parseRouteParameters( + "steps=true&alternative=true&geometries=polyline&overview=simplified"); + BOOST_CHECK(result_2); + BOOST_CHECK_EQUAL(reference_2.steps, result_2->steps); + BOOST_CHECK_EQUAL(reference_2.alternative, result_2->alternative); + BOOST_CHECK_EQUAL(reference_2.geometries, result_2->geometries); + BOOST_CHECK_EQUAL(reference_2.overview, result_2->overview); + CHECK_EQUAL_RANGE(reference_2.uturns, result_2->uturns); + CHECK_EQUAL_RANGE(reference_2.bearings, result_2->bearings); + CHECK_EQUAL_RANGE(reference_2.radiuses, result_2->radiuses); + CHECK_EQUAL_RANGE(reference_2.coordinates, result_2->coordinates); + + std::vector> uturns_3 = {true, false, boost::none}; + engine::api::RouteParameters reference_3{ + false, false, engine::api::RouteParameters::GeometriesType::GeoJSON, + engine::api::RouteParameters::OverviewType::False, uturns_3}; + auto result_3 = api::parseRouteParameters( + "steps=false&alternative=false&geometries=geojson&overview=false&uturns=true;false;"); + BOOST_CHECK(result_3); + BOOST_CHECK_EQUAL(reference_3.steps, result_3->steps); + BOOST_CHECK_EQUAL(reference_3.alternative, result_3->alternative); + BOOST_CHECK_EQUAL(reference_3.geometries, result_3->geometries); + BOOST_CHECK_EQUAL(reference_3.overview, result_3->overview); + CHECK_EQUAL_RANGE(reference_3.uturns, result_3->uturns); + CHECK_EQUAL_RANGE(reference_3.bearings, result_3->bearings); + CHECK_EQUAL_RANGE(reference_3.radiuses, result_3->radiuses); + CHECK_EQUAL_RANGE(reference_3.coordinates, result_3->coordinates); + + std::vector> hints_4 = { + engine::Hint::FromBase64( + "rVghAzxMzABMAwAA5h4CAKMIAAAQAAAAGAAAAAYAAAAAAAAAch8BAJ4AAACpWCED_0vMAAEAAQGLSzmR"), + engine::Hint::FromBase64( + "_4ghA4JuzAD_IAAAo28BAOYAAAAzAAAAAgAAAEwAAAAAAAAAdIwAAJ4AAAAXiSEDfm7MAAEAAQGLSzmR"), + engine::Hint::FromBase64( + "03AhA0vnzAA_SAAA_____3wEAAAYAAAAQAAAAB4AAABAAAAAoUYBAJ4AAADlcCEDSefMAAMAAQGLSzmR")}; + engine::api::RouteParameters reference_4{false, + true, + engine::api::RouteParameters::GeometriesType::Polyline, + engine::api::RouteParameters::OverviewType::Simplified, + std::vector>{}, + std::vector{}, + hints_4, + std::vector>{}, + std::vector>{} + }; + auto result_4 = api::parseRouteParameters( + "steps=false&hints=rVghAzxMzABMAwAA5h4CAKMIAAAQAAAAGAAAAAYAAAAAAAAAch8BAJ4AAACpWCED_" + "0vMAAEAAQGLSzmR;_4ghA4JuzAD_" + "IAAAo28BAOYAAAAzAAAAAgAAAEwAAAAAAAAAdIwAAJ4AAAAXiSEDfm7MAAEAAQGLSzmR;03AhA0vnzAA_SAAA_____" + "3wEAAAYAAAAQAAAAB4AAABAAAAAoUYBAJ4AAADlcCEDSefMAAMAAQGLSzmR"); + BOOST_CHECK(result_3); + BOOST_CHECK_EQUAL(reference_4.steps, result_4->steps); + BOOST_CHECK_EQUAL(reference_4.alternative, result_4->alternative); + BOOST_CHECK_EQUAL(reference_4.geometries, result_4->geometries); + BOOST_CHECK_EQUAL(reference_4.overview, result_4->overview); + CHECK_EQUAL_RANGE(reference_4.uturns, result_4->uturns); + CHECK_EQUAL_RANGE(reference_4.bearings, result_4->bearings); + CHECK_EQUAL_RANGE(reference_4.radiuses, result_4->radiuses); + CHECK_EQUAL_RANGE(reference_4.coordinates, result_4->coordinates); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/server/url_parser.cpp b/unit_tests/server/url_parser.cpp new file mode 100644 index 000000000..613ba9f33 --- /dev/null +++ b/unit_tests/server/url_parser.cpp @@ -0,0 +1,110 @@ +#include "server/api/url_parser.hpp" + +#include + +// needed for BOOST_CHECK_EQUAL +namespace osrm +{ +namespace server +{ +namespace api +{ +std::ostream& operator<<(std::ostream& out, const osrm::server::api::ParsedURL& url) +{ + out << url.service << ", " << url.version << ", " << url.profile << ", "; + for (auto c : url.coordinates) + { + out << c << " "; + } + out << ", " << url.options; + + return out; +} +} +} +} + + +#include +#include + +#define CHECK_EQUAL_RANGE(R1, R2) \ + BOOST_CHECK_EQUAL_COLLECTIONS(R1.begin(), R1.end(), R2.begin(), R2.end()); + +BOOST_AUTO_TEST_SUITE(api_url_parser) + +using namespace osrm; +using namespace osrm::server; + +// returns distance to front +std::size_t testInvalidURL(std::string url) +{ + auto iter = url.begin(); + auto result = api::parseURL(iter, url.end()); + BOOST_CHECK(!result); + return std::distance(url.begin(), iter); +} + +BOOST_AUTO_TEST_CASE(invalid_urls) +{ + BOOST_CHECK_EQUAL(testInvalidURL("/route/"), 0UL); + BOOST_CHECK_EQUAL(testInvalidURL("/route/bla"), 0UL); + BOOST_CHECK_EQUAL(testInvalidURL("/route/1/1,2;3;4"), 0UL); + BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/pro_file/1,2;3,4"), 0UL); + BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/profile"), 0UL); + BOOST_CHECK_EQUAL(testInvalidURL("/route/v1/profile/"), 0UL); +} + +BOOST_AUTO_TEST_CASE(valid_urls) +{ + std::vector coords_1 = { + // lat,lon + util::FixedPointCoordinate(1 * COORDINATE_PRECISION, 0 * COORDINATE_PRECISION), + util::FixedPointCoordinate(3 * COORDINATE_PRECISION, 2 * COORDINATE_PRECISION), + util::FixedPointCoordinate(5 * COORDINATE_PRECISION, 4 * COORDINATE_PRECISION), + }; + api::ParsedURL reference_1{"route", 1, "profile", coords_1, "options=value&foo=bar"}; + auto result_1 = api::parseURL("/route/v1/profile/0,1;2,3;4,5?options=value&foo=bar"); + BOOST_CHECK(result_1); + BOOST_CHECK_EQUAL(reference_1.service, result_1->service); + BOOST_CHECK_EQUAL(reference_1.version, result_1->version); + BOOST_CHECK_EQUAL(reference_1.profile, result_1->profile); + CHECK_EQUAL_RANGE(reference_1.coordinates, result_1->coordinates); + BOOST_CHECK_EQUAL(reference_1.options, result_1->options); + + // no options + api::ParsedURL reference_2{"route", 1, "profile", coords_1, ""}; + auto result_2 = api::parseURL("/route/v1/profile/0,1;2,3;4,5"); + BOOST_CHECK(result_2); + BOOST_CHECK_EQUAL(reference_2.service, result_2->service); + BOOST_CHECK_EQUAL(reference_2.version, result_2->version); + BOOST_CHECK_EQUAL(reference_2.profile, result_2->profile); + CHECK_EQUAL_RANGE(reference_2.coordinates, result_2->coordinates); + BOOST_CHECK_EQUAL(reference_2.options, result_2->options); + + // one coordinate + std::vector coords_3 = { + // lat,lon + util::FixedPointCoordinate(1 * COORDINATE_PRECISION, 0 * COORDINATE_PRECISION), + }; + api::ParsedURL reference_3{"route", 1, "profile", coords_3, ""}; + auto result_3 = api::parseURL("/route/v1/profile/0,1"); + BOOST_CHECK(result_3); + BOOST_CHECK_EQUAL(reference_3.service, result_3->service); + BOOST_CHECK_EQUAL(reference_3.version, result_3->version); + BOOST_CHECK_EQUAL(reference_3.profile, result_3->profile); + CHECK_EQUAL_RANGE(reference_3.coordinates, result_3->coordinates); + BOOST_CHECK_EQUAL(reference_3.options, result_3->options); + + // polyline + api::ParsedURL reference_5{"route", 1, "profile", coords_1, ""}; + auto result_5 = api::parseURL("/route/v1/profile/polyline(_ibE?_seK_seK_seK_seK)?"); + BOOST_CHECK(result_5); + BOOST_CHECK_EQUAL(reference_5.service, result_5->service); + BOOST_CHECK_EQUAL(reference_5.version, result_5->version); + BOOST_CHECK_EQUAL(reference_5.profile, result_5->profile); + CHECK_EQUAL_RANGE(reference_5.coordinates, result_5->coordinates); + BOOST_CHECK_EQUAL(reference_5.options, result_5->options); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/server_tests.cpp b/unit_tests/server_tests.cpp new file mode 100644 index 000000000..c1edf0647 --- /dev/null +++ b/unit_tests/server_tests.cpp @@ -0,0 +1,7 @@ +#define BOOST_TEST_MODULE server tests + +#include + +/* + * This file will contain an automatically generated main function. + */ diff --git a/unit_tests/util/tiles.cpp b/unit_tests/util/tiles.cpp new file mode 100644 index 000000000..816009121 --- /dev/null +++ b/unit_tests/util/tiles.cpp @@ -0,0 +1,74 @@ +#include "util/tiles.hpp" + +using namespace osrm::util; + +#include +#include +#include + +#include + +BOOST_AUTO_TEST_SUITE(tiles_test) + +using namespace osrm; +using namespace osrm::util; + +BOOST_AUTO_TEST_CASE(point_to_tile_test) +{ + tiles::Tile tile_1_reference{2306375680,1409941503,32}; + tiles::Tile tile_2_reference{2308259840,1407668224,32}; + tiles::Tile tile_3_reference{616562688,2805989376,32}; + tiles::Tile tile_4_reference{1417674752,2084569088,32}; + tiles::Tile tile_5_reference{616562688,2805989376,32}; + tiles::Tile tile_6_reference{712654285,2671662374,32}; + + auto tile_1 = tiles::pointToTile(13.31817626953125,52.449314140869696); + auto tile_2 = tiles::pointToTile(13.476104736328125,52.56529070208021); + auto tile_3 = tiles::pointToTile(-128.32031249999997,-48.224672649565186); + auto tile_4 = tiles::pointToTile(-61.17187499999999,5.266007882805498); + auto tile_5 = tiles::pointToTile(-128.32031249999997,-48.224672649565186); + auto tile_6 = tiles::pointToTile(-120.266007882805532,-40.17187499999999); + BOOST_CHECK_EQUAL(tile_1.x, tile_1_reference.x); + BOOST_CHECK_EQUAL(tile_1.y, tile_1_reference.y); + BOOST_CHECK_EQUAL(tile_1.z, tile_1_reference.z); + BOOST_CHECK_EQUAL(tile_2.x, tile_2_reference.x); + BOOST_CHECK_EQUAL(tile_2.y, tile_2_reference.y); + BOOST_CHECK_EQUAL(tile_2.z, tile_2_reference.z); + BOOST_CHECK_EQUAL(tile_3.x, tile_3_reference.x); + BOOST_CHECK_EQUAL(tile_3.y, tile_3_reference.y); + BOOST_CHECK_EQUAL(tile_3.z, tile_3_reference.z); + BOOST_CHECK_EQUAL(tile_4.x, tile_4_reference.x); + BOOST_CHECK_EQUAL(tile_4.y, tile_4_reference.y); + BOOST_CHECK_EQUAL(tile_4.z, tile_4_reference.z); + BOOST_CHECK_EQUAL(tile_5.x, tile_5_reference.x); + BOOST_CHECK_EQUAL(tile_5.y, tile_5_reference.y); + BOOST_CHECK_EQUAL(tile_5.z, tile_5_reference.z); + BOOST_CHECK_EQUAL(tile_6.x, tile_6_reference.x); + BOOST_CHECK_EQUAL(tile_6.y, tile_6_reference.y); + BOOST_CHECK_EQUAL(tile_6.z, tile_6_reference.z); +} + +// Verify that the bearing-bounds checking function behaves as expected +BOOST_AUTO_TEST_CASE(bounding_box_to_tile_test) +{ + tiles::Tile tile_1_reference{17, 10, 5}; + tiles::Tile tile_2_reference{0, 0, 0}; + tiles::Tile tile_3_reference{0, 2, 2}; + auto tile_1 = tiles::getBBMaxZoomTile(13.31817626953125, 52.449314140869696, + 13.476104736328125, 52.56529070208021); + auto tile_2 = tiles::getBBMaxZoomTile(-128.32031249999997, -48.224672649565186, + -61.17187499999999, 5.266007882805498); + auto tile_3 = tiles::getBBMaxZoomTile(-128.32031249999997, -48.224672649565186, + -120.2660078828055, -40.17187499999999); + BOOST_CHECK_EQUAL(tile_1.x, tile_1_reference.x); + BOOST_CHECK_EQUAL(tile_1.y, tile_1_reference.y); + BOOST_CHECK_EQUAL(tile_1.z, tile_1_reference.z); + BOOST_CHECK_EQUAL(tile_2.x, tile_2_reference.x); + BOOST_CHECK_EQUAL(tile_2.y, tile_2_reference.y); + BOOST_CHECK_EQUAL(tile_2.z, tile_2_reference.z); + BOOST_CHECK_EQUAL(tile_3.x, tile_3_reference.x); + BOOST_CHECK_EQUAL(tile_3.y, tile_3_reference.y); + BOOST_CHECK_EQUAL(tile_3.z, tile_3_reference.z); +} + +BOOST_AUTO_TEST_SUITE_END()