#ifndef ENGINE_API_MATCH_HPP #define ENGINE_API_MATCH_HPP #include "engine/api/match_parameters.hpp" #include "engine/api/match_parameters_tidy.hpp" #include "engine/api/route_api.hpp" #include "engine/datafacade/datafacade_base.hpp" #include "engine/internal_route_result.hpp" #include "engine/map_matching/sub_matching.hpp" #include "util/integer_range.hpp" namespace osrm { namespace engine { namespace api { class MatchAPI final : public RouteAPI { public: MatchAPI(const datafacade::BaseDataFacade &facade_, const MatchParameters ¶meters_, const tidy::Result &tidy_result_) : RouteAPI(facade_, parameters_), parameters(parameters_), tidy_result(tidy_result_) { } void MakeResponse(const std::vector &sub_matchings, const std::vector &sub_routes, osrm::engine::api::ResultT &response) const { BOOST_ASSERT(sub_matchings.size() == sub_routes.size()); if (response.is()) { auto &fb_result = response.get(); MakeResponse(sub_matchings, sub_routes, fb_result); } else { auto &json_result = response.get(); MakeResponse(sub_matchings, sub_routes, json_result); } } void MakeResponse(const std::vector &sub_matchings, const std::vector &sub_routes, flatbuffers::FlatBufferBuilder &fb_result) const { auto data_timestamp = facade.GetTimestamp(); flatbuffers::Offset data_version_string; if (!data_timestamp.empty()) { data_version_string = fb_result.CreateString(data_timestamp); } auto response = MakeFBResponse(sub_routes, fb_result, [this, &fb_result, &sub_matchings]() { return MakeTracepoints(fb_result, sub_matchings); }); if (!data_timestamp.empty()) { response->add_data_version(data_version_string); } fb_result.Finish(response->Finish()); } void MakeResponse(const std::vector &sub_matchings, const std::vector &sub_routes, util::json::Object &response) const { auto number_of_routes = sub_matchings.size(); util::json::Array routes; routes.values.reserve(number_of_routes); for (auto index : util::irange(0UL, sub_matchings.size())) { auto route = MakeRoute(sub_routes[index].segment_end_coordinates, sub_routes[index].unpacked_path_segments, sub_routes[index].source_traversed_in_reverse, sub_routes[index].target_traversed_in_reverse); route.values["confidence"] = sub_matchings[index].confidence; routes.values.push_back(std::move(route)); } if (!parameters.skip_waypoints) { response.values["tracepoints"] = MakeTracepoints(sub_matchings); } response.values["matchings"] = std::move(routes); response.values["code"] = "Ok"; } protected: // FIXME this logic is a little backwards. We should change the output format of the // map_matching // routing algorithm to be easier to consume here. struct MatchingIndex { MatchingIndex() = default; MatchingIndex(unsigned sub_matching_index_, unsigned point_index_) : sub_matching_index(sub_matching_index_), point_index(point_index_) { } unsigned sub_matching_index = std::numeric_limits::max(); unsigned point_index = std::numeric_limits::max(); bool NotMatched() { return sub_matching_index == std::numeric_limits::max() && point_index == std::numeric_limits::max(); } }; flatbuffers::Offset>> MakeTracepoints(flatbuffers::FlatBufferBuilder &fb_result, const std::vector &sub_matchings) const { std::vector> waypoints; waypoints.reserve(parameters.coordinates.size()); auto trace_idx_to_matching_idx = MakeMatchingIndices(sub_matchings); BOOST_ASSERT(parameters.waypoints.empty() || sub_matchings.size() == 1); std::size_t was_waypoint_idx = 0; for (auto trace_index : util::irange(0UL, parameters.coordinates.size())) { if (tidy_result.can_be_removed[trace_index]) { waypoints.push_back(fbresult::WaypointBuilder(fb_result).Finish()); continue; } auto matching_index = trace_idx_to_matching_idx[trace_index]; if (matching_index.NotMatched()) { waypoints.push_back(fbresult::WaypointBuilder(fb_result).Finish()); continue; } const auto &phantom = sub_matchings[matching_index.sub_matching_index].nodes[matching_index.point_index]; auto waypoint = BaseAPI::MakeWaypoint(&fb_result, phantom); waypoint->add_matchings_index(matching_index.sub_matching_index); waypoint->add_alternatives_count(sub_matchings[matching_index.sub_matching_index] .alternatives_count[matching_index.point_index]); // waypoint indices need to be adjusted if route legs were collapsed // waypoint parameter assumes there is only one match object if (!parameters.waypoints.empty()) { if (tidy_result.was_waypoint[trace_index]) { waypoint->add_waypoint_index(was_waypoint_idx); was_waypoint_idx++; } else { waypoint->add_waypoint_index(0); } } else { waypoint->add_waypoint_index(matching_index.point_index); } waypoints.push_back(waypoint->Finish()); } return fb_result.CreateVector(waypoints); } util::json::Array MakeTracepoints(const std::vector &sub_matchings) const { util::json::Array waypoints; waypoints.values.reserve(parameters.coordinates.size()); auto trace_idx_to_matching_idx = MakeMatchingIndices(sub_matchings); BOOST_ASSERT(parameters.waypoints.empty() || sub_matchings.size() == 1); std::size_t was_waypoint_idx = 0; for (auto trace_index : util::irange(0UL, parameters.coordinates.size())) { if (tidy_result.can_be_removed[trace_index]) { waypoints.values.push_back(util::json::Null()); continue; } auto matching_index = trace_idx_to_matching_idx[trace_index]; if (matching_index.NotMatched()) { waypoints.values.push_back(util::json::Null()); continue; } const auto &phantom = sub_matchings[matching_index.sub_matching_index].nodes[matching_index.point_index]; auto waypoint = BaseAPI::MakeWaypoint(phantom); waypoint.values["matchings_index"] = matching_index.sub_matching_index; waypoint.values["waypoint_index"] = matching_index.point_index; waypoint.values["alternatives_count"] = sub_matchings[matching_index.sub_matching_index] .alternatives_count[matching_index.point_index]; // waypoint indices need to be adjusted if route legs were collapsed // waypoint parameter assumes there is only one match object if (!parameters.waypoints.empty()) { if (tidy_result.was_waypoint[trace_index]) { waypoint.values["waypoint_index"] = was_waypoint_idx; was_waypoint_idx++; } else { waypoint.values["waypoint_index"] = util::json::Null(); } } waypoints.values.push_back(std::move(waypoint)); } return waypoints; } std::vector MakeMatchingIndices(const std::vector &sub_matchings) const { std::vector trace_idx_to_matching_idx(parameters.coordinates.size()); for (auto sub_matching_index : util::irange(0u, static_cast(sub_matchings.size()))) { for (auto point_index : util::irange( 0u, static_cast(sub_matchings[sub_matching_index].indices.size()))) { // tidied_to_original: index of the input coordinate that a tidied coordinate // corresponds to. // sub_matching indices: index of the coordinate passed to map matching plugin that // a matched node corresponds to. trace_idx_to_matching_idx[tidy_result .tidied_to_original[sub_matchings[sub_matching_index] .indices[point_index]]] = MatchingIndex{sub_matching_index, point_index}; } } return trace_idx_to_matching_idx; } const MatchParameters ¶meters; const tidy::Result &tidy_result; }; } // ns api } // ns engine } // ns osrm #endif