#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/douglas_peucker.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; using RouteNameExtractor = ExtractRouteNames; 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; RouteNameExtractor generate_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 = generate_route_names(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 = generate_route_names(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 PolylineFormatter().printEncodedString(segments.Get()); else return PolylineFormatter().printUnencodedString(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::TurnInstructionsClass::TurnIsNecessary(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())) { ObjectEncoder::EncodeToBase64(raw_route.segment_end_coordinates[i].source_phantom, hint); json_location_hint_array.values.push_back(hint); } ObjectEncoder::EncodeToBase64(raw_route.segment_end_coordinates.back().target_phantom, hint); 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_