osrm-backend/include/engine/api_response_generator.hpp

300 lines
11 KiB
C++

#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 <boost/assert.hpp>
#include <cstdint>
#include <cstddef>
#include <cmath>
#include <limits>
#include <string>
#include <utility>
#include <vector>
namespace osrm
{
namespace engine
{
namespace detail
{
struct Segment
{
uint32_t name_id;
int32_t length;
std::size_t position;
};
} // namespace detail
template <typename DataFacadeT> class ApiResponseGenerator
{
public:
using DataFacade = DataFacadeT;
using Segments = guidance::SegmentList<DataFacade>;
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<Segment> 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 <typename DataFacadeT>
ApiResponseGenerator<DataFacadeT>::ApiResponseGenerator(DataFacadeT *facade_)
: facade(facade_)
{
}
template <typename DataFacadeT>
void ApiResponseGenerator<DataFacadeT>::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<detail::Segment> 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 <typename DataFacadeT>
util::json::Object
ApiResponseGenerator<DataFacadeT>::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 <typename DataFacadeT>
util::json::Array
ApiResponseGenerator<DataFacadeT>::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 <typename DataFacadeT>
util::json::Array
ApiResponseGenerator<DataFacadeT>::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 <typename DataFacadeT>
util::json::Value ApiResponseGenerator<DataFacadeT>::GetGeometry(const bool return_encoded,
const Segments &segments) const
{
if (return_encoded)
return polylineEncodeAsJSON(segments.Get());
else
return polylineUnencodedAsJSON(segments.Get());
}
template <typename DataFacadeT>
std::vector<detail::Segment>
ApiResponseGenerator<DataFacadeT>::BuildRouteSegments(const Segments &segment_list) const
{
std::vector<detail::Segment> 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<int32_t>(segment.length),
static_cast<std::size_t>(result.size())};
result.emplace_back(std::move(seg));
}
}
return result;
}
template <typename DataFacadeT>
util::json::Object
ApiResponseGenerator<DataFacadeT>::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<std::size_t>(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 <typename DataFacadeT>
ApiResponseGenerator<DataFacadeT> MakeApiResponseGenerator(DataFacadeT *facade)
{
return ApiResponseGenerator<DataFacadeT>(facade);
}
} // namespace engine
} // namespace osrm
#endif // ENGINE_GUIDANCE_API_RESPONSE_GENERATOR_HPP_