Add trip plugin
This commit is contained in:
		
							parent
							
								
									cb82376083
								
							
						
					
					
						commit
						c53a448589
					
				
							
								
								
									
										110
									
								
								include/engine/api/trip_api.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								include/engine/api/trip_api.hpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,110 @@ | |||||||
|  | #ifndef ENGINE_API_TRIP_HPP | ||||||
|  | #define ENGINE_API_TRIP_HPP | ||||||
|  | 
 | ||||||
|  | #include "engine/api/route_api.hpp" | ||||||
|  | #include "engine/api/trip_parameters.hpp" | ||||||
|  | 
 | ||||||
|  | #include "engine/datafacade/datafacade_base.hpp" | ||||||
|  | 
 | ||||||
|  | #include "engine/internal_route_result.hpp" | ||||||
|  | 
 | ||||||
|  | #include "util/integer_range.hpp" | ||||||
|  | 
 | ||||||
|  | namespace osrm | ||||||
|  | { | ||||||
|  | namespace engine | ||||||
|  | { | ||||||
|  | namespace api | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | class TripAPI final : public RouteAPI | ||||||
|  | { | ||||||
|  |   public: | ||||||
|  |     TripAPI(const datafacade::BaseDataFacade &facade_, const TripParameters ¶meters_) | ||||||
|  |         : RouteAPI(facade_, parameters_), parameters(parameters_) | ||||||
|  |     { | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void MakeResponse(const std::vector<std::vector<NodeID>> &sub_trips, | ||||||
|  |                       const std::vector<InternalRouteResult> &sub_routes, | ||||||
|  |                       const std::vector<PhantomNode> &phantoms, | ||||||
|  |                       util::json::Object &response) const | ||||||
|  |     { | ||||||
|  |         auto number_of_routes = sub_trips.size(); | ||||||
|  |         util::json::Array routes; | ||||||
|  |         routes.values.reserve(number_of_routes); | ||||||
|  |         BOOST_ASSERT(sub_trips.size() == sub_routes.size()); | ||||||
|  |         for (auto index : util::irange<std::size_t>(0UL, sub_trips.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); | ||||||
|  |             routes.values.push_back(std::move(route)); | ||||||
|  |         } | ||||||
|  |         response.values["waypoints"] = MakeWaypoints(sub_trips, phantoms); | ||||||
|  |         response.values["routes"] = std::move(routes); | ||||||
|  |         response.values["code"] = "ok"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |   protected: | ||||||
|  |     // FIXME this logic is a little backwards. We should change the output format of the
 | ||||||
|  |     // trip plugin routing algorithm to be easier to consume here.
 | ||||||
|  |     util::json::Array MakeWaypoints(const std::vector<std::vector<NodeID>> &sub_trips, | ||||||
|  |                                     const std::vector<PhantomNode> &phantoms) const | ||||||
|  |     { | ||||||
|  |         util::json::Array waypoints; | ||||||
|  |         waypoints.values.reserve(parameters.coordinates.size()); | ||||||
|  | 
 | ||||||
|  |         struct TripIndex | ||||||
|  |         { | ||||||
|  |             TripIndex() = default; | ||||||
|  |             TripIndex(unsigned sub_trip_index_, unsigned point_index_) | ||||||
|  |                 : sub_trip_index(sub_trip_index_), point_index(point_index_) | ||||||
|  |             { | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             unsigned sub_trip_index = std::numeric_limits<unsigned>::max(); | ||||||
|  |             unsigned point_index = std::numeric_limits<unsigned>::max(); | ||||||
|  | 
 | ||||||
|  |             bool NotUsed() | ||||||
|  |             { | ||||||
|  |                 return sub_trip_index == std::numeric_limits<unsigned>::max() && | ||||||
|  |                        point_index == std::numeric_limits<unsigned>::max(); | ||||||
|  |             } | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         std::vector<TripIndex> input_idx_to_trip_idx(parameters.coordinates.size()); | ||||||
|  |         for (auto sub_trip_index : util::irange(0u, static_cast<unsigned>(sub_trips.size()))) | ||||||
|  |         { | ||||||
|  |             for (auto point_index : | ||||||
|  |                  util::irange(0u, static_cast<unsigned>(sub_trips[sub_trip_index].size()))) | ||||||
|  |             { | ||||||
|  |                 input_idx_to_trip_idx[sub_trips[sub_trip_index][point_index]] = | ||||||
|  |                     TripIndex{sub_trip_index, point_index}; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         for (auto input_index : util::irange(0UL, parameters.coordinates.size())) | ||||||
|  |         { | ||||||
|  |             auto trip_index = input_idx_to_trip_idx[input_index]; | ||||||
|  |             BOOST_ASSERT(!trip_index.NotUsed()); | ||||||
|  | 
 | ||||||
|  |             auto waypoint = | ||||||
|  |                 BaseAPI::MakeWaypoint(parameters.coordinates[input_index], phantoms[input_index]); | ||||||
|  |             waypoint.values["trips_index"] = trip_index.sub_trip_index; | ||||||
|  |             waypoint.values["waypoint_index"] = trip_index.point_index; | ||||||
|  |             waypoints.values.push_back(std::move(waypoint)); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return waypoints; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const TripParameters ¶meters; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } // ns api
 | ||||||
|  | } // ns engine
 | ||||||
|  | } // ns osrm
 | ||||||
|  | 
 | ||||||
|  | #endif | ||||||
| @ -14,7 +14,7 @@ namespace api | |||||||
| 
 | 
 | ||||||
| struct TripParameters : public RouteParameters | struct TripParameters : public RouteParameters | ||||||
| { | { | ||||||
|     bool IsValid() const; |     //bool IsValid() const; Falls back to base class
 | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| } | } | ||||||
|  | |||||||
| @ -73,7 +73,7 @@ class Engine final | |||||||
|     std::unique_ptr<plugins::ViaRoutePlugin> route_plugin; |     std::unique_ptr<plugins::ViaRoutePlugin> route_plugin; | ||||||
|     std::unique_ptr<plugins::TablePlugin> table_plugin; |     std::unique_ptr<plugins::TablePlugin> table_plugin; | ||||||
|     std::unique_ptr<plugins::NearestPlugin> nearest_plugin; |     std::unique_ptr<plugins::NearestPlugin> nearest_plugin; | ||||||
|     // std::unique_ptr<plugins::TripPlugin> trip_plugin;
 |     std::unique_ptr<plugins::TripPlugin> trip_plugin; | ||||||
|     std::unique_ptr<plugins::MatchPlugin> match_plugin; |     std::unique_ptr<plugins::MatchPlugin> match_plugin; | ||||||
| 
 | 
 | ||||||
|     std::unique_ptr<datafacade::BaseDataFacade> query_data_facade; |     std::unique_ptr<datafacade::BaseDataFacade> query_data_facade; | ||||||
|  | |||||||
| @ -3,16 +3,10 @@ | |||||||
| 
 | 
 | ||||||
| #include "engine/plugins/plugin_base.hpp" | #include "engine/plugins/plugin_base.hpp" | ||||||
| 
 | 
 | ||||||
| #include "engine/object_encoder.hpp" | #include "engine/api/trip_parameters.hpp" | ||||||
| #include "extractor/tarjan_scc.hpp" | #include "engine/routing_algorithms/shortest_path.hpp" | ||||||
| #include "engine/trip/trip_nearest_neighbour.hpp" | #include "engine/routing_algorithms/many_to_many.hpp" | ||||||
| #include "engine/trip/trip_farthest_insertion.hpp" | 
 | ||||||
| #include "engine/trip/trip_brute_force.hpp" |  | ||||||
| #include "engine/search_engine.hpp" |  | ||||||
| #include "util/matrix_graph_wrapper.hpp" // wrapper to use tarjan scc on dist table
 |  | ||||||
| #include "engine/api_response_generator.hpp" |  | ||||||
| #include "util/make_unique.hpp" |  | ||||||
| #include "util/dist_table_wrapper.hpp" // to access the dist table more easily
 |  | ||||||
| #include "osrm/json_container.hpp" | #include "osrm/json_container.hpp" | ||||||
| 
 | 
 | ||||||
| #include <boost/assert.hpp> | #include <boost/assert.hpp> | ||||||
| @ -32,335 +26,28 @@ namespace engine | |||||||
| namespace plugins | namespace plugins | ||||||
| { | { | ||||||
| 
 | 
 | ||||||
| template <class DataFacadeT> class RoundTripPlugin final : public BasePlugin | class TripPlugin final : public BasePlugin | ||||||
| { | { | ||||||
|   private: |   private: | ||||||
|     std::string descriptor_string; |     SearchEngineData heaps; | ||||||
|     DataFacadeT *facade; |     routing_algorithms::ShortestPathRouting<datafacade::BaseDataFacade> shortest_path; | ||||||
|     std::unique_ptr<SearchEngine<DataFacadeT>> search_engine_ptr; |     routing_algorithms::ManyToManyRouting<datafacade::BaseDataFacade> duration_table; | ||||||
|     int max_locations_trip; |     int max_locations_trip; | ||||||
| 
 | 
 | ||||||
|   public: |  | ||||||
|     explicit RoundTripPlugin(DataFacadeT *facade, int max_locations_trip) |  | ||||||
|         : descriptor_string("trip"), facade(facade), max_locations_trip(max_locations_trip) |  | ||||||
|     { |  | ||||||
|         search_engine_ptr = util::make_unique<SearchEngine<DataFacadeT>>(facade); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const std::string GetDescriptor() const override final { return descriptor_string; } |  | ||||||
| 
 |  | ||||||
|     std::vector<PhantomNode> GetPhantomNodes(const RouteParameters &route_parameters) |  | ||||||
|     { |  | ||||||
|         const bool checksum_OK = (route_parameters.check_sum == facade->GetCheckSum()); |  | ||||||
|         const auto &input_bearings = route_parameters.bearings; |  | ||||||
| 
 |  | ||||||
|         std::vector<PhantomNode> phantom_node_list; |  | ||||||
|         phantom_node_list.reserve(route_parameters.coordinates.size()); |  | ||||||
| 
 |  | ||||||
|         // find phantom nodes for all input coords
 |  | ||||||
|         for (const auto i : util::irange<std::size_t>(0, route_parameters.coordinates.size())) |  | ||||||
|         { |  | ||||||
|             // if client hints are helpful, encode hints
 |  | ||||||
|             if (checksum_OK && i < route_parameters.hints.size() && |  | ||||||
|                 !route_parameters.hints[i].empty()) |  | ||||||
|             { |  | ||||||
|                 auto current_phantom_node = decodeBase64<PhantomNode>(route_parameters.hints[i]); |  | ||||||
|                 if (current_phantom_node.IsValid(facade->GetNumberOfNodes())) |  | ||||||
|                 { |  | ||||||
|                     phantom_node_list.push_back(std::move(current_phantom_node)); |  | ||||||
|                     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; |  | ||||||
|             auto results = |  | ||||||
|                 facade->NearestPhantomNodes(route_parameters.coordinates[i], 1, bearing, range); |  | ||||||
|             if (results.empty()) |  | ||||||
|             { |  | ||||||
|                 break; |  | ||||||
|             } |  | ||||||
|             phantom_node_list.push_back(std::move(results.front().phantom_node)); |  | ||||||
|             BOOST_ASSERT(phantom_node_list.back().IsValid(facade->GetNumberOfNodes())); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return phantom_node_list; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // Object to hold all strongly connected components (scc) of a graph
 |  | ||||||
|     // to access all graphs with component ID i, get the iterators by:
 |  | ||||||
|     // auto start = std::begin(scc_component.component) + scc_component.range[i];
 |  | ||||||
|     // auto end = std::begin(scc_component.component) + scc_component.range[i+1];
 |  | ||||||
|     struct SCC_Component |  | ||||||
|     { |  | ||||||
|         // in_component: all NodeIDs sorted by component ID
 |  | ||||||
|         // in_range: index where a new component starts
 |  | ||||||
|         //
 |  | ||||||
|         // example: NodeID 0, 1, 2, 4, 5 are in component 0
 |  | ||||||
|         //          NodeID 3, 6, 7, 8    are in component 1
 |  | ||||||
|         //          => in_component = [0, 1, 2, 4, 5, 3, 6, 7, 8]
 |  | ||||||
|         //          => in_range = [0, 5]
 |  | ||||||
|         SCC_Component(std::vector<NodeID> in_component_nodes, std::vector<size_t> in_range) |  | ||||||
|             : component(std::move(in_component_nodes)), range(std::move(in_range)) |  | ||||||
|         { |  | ||||||
|             BOOST_ASSERT_MSG(component.size() > 0, "there's no scc component"); |  | ||||||
|             BOOST_ASSERT_MSG(*std::max_element(range.begin(), range.end()) == component.size(), |  | ||||||
|                              "scc component ranges are out of bound"); |  | ||||||
|             BOOST_ASSERT_MSG(*std::min_element(range.begin(), range.end()) == 0, |  | ||||||
|                              "invalid scc component range"); |  | ||||||
|             BOOST_ASSERT_MSG(std::is_sorted(std::begin(range), std::end(range)), |  | ||||||
|                              "invalid component ranges"); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         std::size_t GetNumberOfComponents() const |  | ||||||
|         { |  | ||||||
|             BOOST_ASSERT_MSG(range.size() > 0, "there's no range"); |  | ||||||
|             return range.size() - 1; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const std::vector<NodeID> component; |  | ||||||
|         std::vector<std::size_t> range; |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     // takes the number of locations and its distance matrix,
 |  | ||||||
|     // identifies and splits the graph in its strongly connected components (scc)
 |  | ||||||
|     // and returns an SCC_Component
 |  | ||||||
|     SCC_Component SplitUnaccessibleLocations(const std::size_t number_of_locations, |  | ||||||
|                                              const util::DistTableWrapper<EdgeWeight> &result_table) |  | ||||||
|     { |  | ||||||
| 
 |  | ||||||
|         if (std::find(std::begin(result_table), std::end(result_table), INVALID_EDGE_WEIGHT) == |  | ||||||
|             std::end(result_table)) |  | ||||||
|         { |  | ||||||
|             // whole graph is one scc
 |  | ||||||
|             std::vector<NodeID> location_ids(number_of_locations); |  | ||||||
|             std::iota(std::begin(location_ids), std::end(location_ids), 0); |  | ||||||
|             std::vector<size_t> range = {0, location_ids.size()}; |  | ||||||
|             return SCC_Component(std::move(location_ids), std::move(range)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // Run TarjanSCC
 |  | ||||||
|         auto wrapper = std::make_shared<util::MatrixGraphWrapper<EdgeWeight>>( |  | ||||||
|             result_table.GetTable(), number_of_locations); |  | ||||||
|         auto scc = extractor::TarjanSCC<util::MatrixGraphWrapper<EdgeWeight>>(wrapper); |  | ||||||
|         scc.run(); |  | ||||||
| 
 |  | ||||||
|         const auto number_of_components = scc.get_number_of_components(); |  | ||||||
| 
 |  | ||||||
|         std::vector<std::size_t> range_insertion; |  | ||||||
|         std::vector<std::size_t> range; |  | ||||||
|         range_insertion.reserve(number_of_components); |  | ||||||
|         range.reserve(number_of_components); |  | ||||||
| 
 |  | ||||||
|         std::vector<NodeID> components(number_of_locations, 0); |  | ||||||
| 
 |  | ||||||
|         std::size_t prefix = 0; |  | ||||||
|         for (std::size_t j = 0; j < number_of_components; ++j) |  | ||||||
|         { |  | ||||||
|             range_insertion.push_back(prefix); |  | ||||||
|             range.push_back(prefix); |  | ||||||
|             prefix += scc.get_component_size(j); |  | ||||||
|         } |  | ||||||
|         // senitel
 |  | ||||||
|         range.push_back(components.size()); |  | ||||||
| 
 |  | ||||||
|         for (std::size_t i = 0; i < number_of_locations; ++i) |  | ||||||
|         { |  | ||||||
|             components[range_insertion[scc.get_component_id(i)]] = i; |  | ||||||
|             ++range_insertion[scc.get_component_id(i)]; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return SCC_Component(std::move(components), std::move(range)); |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void SetLocPermutationOutput(const std::vector<NodeID> &permutation, |  | ||||||
|                                  util::json::Object &json_result) |  | ||||||
|     { |  | ||||||
|         util::json::Array json_permutation; |  | ||||||
|         json_permutation.values.insert(std::end(json_permutation.values), std::begin(permutation), |  | ||||||
|                                        std::end(permutation)); |  | ||||||
|         json_result.values["permutation"] = json_permutation; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     InternalRouteResult ComputeRoute(const std::vector<PhantomNode> &phantom_node_list, |     InternalRouteResult ComputeRoute(const std::vector<PhantomNode> &phantom_node_list, | ||||||
|                                      const RouteParameters &route_parameters, |                                      const api::TripParameters ¶meters, | ||||||
|                                      const std::vector<NodeID> &trip) |                                      const std::vector<NodeID> &trip); | ||||||
|  | 
 | ||||||
|  |   public: | ||||||
|  |     explicit TripPlugin(datafacade::BaseDataFacade &facade_, const int max_locations_trip_) | ||||||
|  |         : BasePlugin(facade_), shortest_path(&facade_, heaps), duration_table(&facade_, heaps), | ||||||
|  |           max_locations_trip(max_locations_trip_) | ||||||
|     { |     { | ||||||
|         InternalRouteResult min_route; |  | ||||||
|         // given he final trip, compute total distance and return the route and location permutation
 |  | ||||||
|         PhantomNodes viapoint; |  | ||||||
|         const auto start = std::begin(trip); |  | ||||||
|         const auto end = std::end(trip); |  | ||||||
|         // computes a roundtrip from the nodes in trip
 |  | ||||||
|         for (auto it = start; it != end; ++it) |  | ||||||
|         { |  | ||||||
|             const auto from_node = *it; |  | ||||||
|             // if from_node is the last node, compute the route from the last to the first location
 |  | ||||||
|             const auto to_node = std::next(it) != end ? *std::next(it) : *start; |  | ||||||
| 
 |  | ||||||
|             viapoint = PhantomNodes{phantom_node_list[from_node], phantom_node_list[to_node]}; |  | ||||||
|             min_route.segment_end_coordinates.emplace_back(viapoint); |  | ||||||
|         } |  | ||||||
|         BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size()); |  | ||||||
| 
 |  | ||||||
|         std::vector<bool> uturns(trip.size() + 1); |  | ||||||
|         BOOST_ASSERT(route_parameters.uturns.size() > 0); |  | ||||||
|         std::transform(trip.begin(), trip.end(), uturns.begin(), |  | ||||||
|                        [&route_parameters](const NodeID idx) |  | ||||||
|                        { |  | ||||||
|                            return route_parameters.uturns[idx]; |  | ||||||
|                        }); |  | ||||||
|         BOOST_ASSERT(uturns.size() > 0); |  | ||||||
|         uturns.back() = route_parameters.uturns[trip.front()]; |  | ||||||
| 
 |  | ||||||
|         search_engine_ptr->shortest_path(min_route.segment_end_coordinates, uturns, min_route); |  | ||||||
| 
 |  | ||||||
|         BOOST_ASSERT_MSG(min_route.shortest_path_length < INVALID_EDGE_WEIGHT, "unroutable route"); |  | ||||||
|         return min_route; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     Status HandleRequest(const RouteParameters &route_parameters, |     Status HandleRequest(const api::TripParameters ¶meters, util::json::Object &json_result); | ||||||
|                          util::json::Object &json_result) override final |  | ||||||
|     { |  | ||||||
|         if (max_locations_trip > 0 && |  | ||||||
|             (static_cast<int>(route_parameters.coordinates.size()) > max_locations_trip)) |  | ||||||
|         { |  | ||||||
|             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_trip) + ")"; |  | ||||||
|             return Status::Error; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // check if all inputs are coordinates
 |  | ||||||
|         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 coordinates"; |  | ||||||
|             return Status::Error; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // get phantom nodes
 |  | ||||||
|         auto phantom_node_list = GetPhantomNodes(route_parameters); |  | ||||||
|         if (phantom_node_list.size() != route_parameters.coordinates.size()) |  | ||||||
|         { |  | ||||||
|             BOOST_ASSERT(phantom_node_list.size() < route_parameters.coordinates.size()); |  | ||||||
|             json_result.values["status_message"] = |  | ||||||
|                 std::string("Could not find a matching segment for coordinate ") + |  | ||||||
|                 std::to_string(phantom_node_list.size()); |  | ||||||
|             return Status::NoSegment; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const auto number_of_locations = phantom_node_list.size(); |  | ||||||
| 
 |  | ||||||
|         // compute the distance table of all phantom nodes
 |  | ||||||
|         const auto result_table = util::DistTableWrapper<EdgeWeight>( |  | ||||||
|             *search_engine_ptr->distance_table(phantom_node_list, phantom_node_list), |  | ||||||
|             number_of_locations); |  | ||||||
| 
 |  | ||||||
|         if (result_table.size() == 0) |  | ||||||
|         { |  | ||||||
|             return Status::Error; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         const constexpr std::size_t BF_MAX_FEASABLE = 10; |  | ||||||
|         BOOST_ASSERT_MSG(result_table.size() == number_of_locations * number_of_locations, |  | ||||||
|                          "Distance Table has wrong size"); |  | ||||||
| 
 |  | ||||||
|         // get scc components
 |  | ||||||
|         SCC_Component scc = SplitUnaccessibleLocations(number_of_locations, result_table); |  | ||||||
| 
 |  | ||||||
|         using NodeIDIterator = typename std::vector<NodeID>::const_iterator; |  | ||||||
| 
 |  | ||||||
|         std::vector<std::vector<NodeID>> route_result; |  | ||||||
|         route_result.reserve(scc.GetNumberOfComponents()); |  | ||||||
|         // run Trip computation for every SCC
 |  | ||||||
|         for (std::size_t k = 0; k < scc.GetNumberOfComponents(); ++k) |  | ||||||
|         { |  | ||||||
|             const auto component_size = scc.range[k + 1] - scc.range[k]; |  | ||||||
| 
 |  | ||||||
|             BOOST_ASSERT_MSG(component_size > 0, "invalid component size"); |  | ||||||
| 
 |  | ||||||
|             std::vector<NodeID> scc_route; |  | ||||||
|             NodeIDIterator start = std::begin(scc.component) + scc.range[k]; |  | ||||||
|             NodeIDIterator end = std::begin(scc.component) + scc.range[k + 1]; |  | ||||||
| 
 |  | ||||||
|             if (component_size > 1) |  | ||||||
|             { |  | ||||||
| 
 |  | ||||||
|                 if (component_size < BF_MAX_FEASABLE) |  | ||||||
|                 { |  | ||||||
|                     scc_route = trip::BruteForceTrip(start, end, number_of_locations, result_table); |  | ||||||
|                 } |  | ||||||
|                 else |  | ||||||
|                 { |  | ||||||
|                     scc_route = |  | ||||||
|                         trip::FarthestInsertionTrip(start, end, number_of_locations, result_table); |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 // use this output if debugging of route is needed:
 |  | ||||||
|                 // util::SimpleLogger().Write() << "Route #" << k << ": " << [&scc_route]()
 |  | ||||||
|                 // {
 |  | ||||||
|                 //     std::string s = "";
 |  | ||||||
|                 //     for (auto x : scc_route)
 |  | ||||||
|                 //     {
 |  | ||||||
|                 //         s += std::to_string(x) + " ";
 |  | ||||||
|                 //     }
 |  | ||||||
|                 //     return s;
 |  | ||||||
|                 // }();
 |  | ||||||
|             } |  | ||||||
|             else |  | ||||||
|             { |  | ||||||
|                 scc_route = std::vector<NodeID>(start, end); |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             route_result.push_back(std::move(scc_route)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // compute all round trip routes
 |  | ||||||
|         std::vector<InternalRouteResult> comp_route; |  | ||||||
|         comp_route.reserve(route_result.size()); |  | ||||||
|         for (auto &elem : route_result) |  | ||||||
|         { |  | ||||||
|             comp_route.push_back(ComputeRoute(phantom_node_list, route_parameters, elem)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         // prepare JSON output
 |  | ||||||
|         // create a json object for every trip
 |  | ||||||
|         util::json::Array trip; |  | ||||||
|         for (std::size_t i = 0; i < route_result.size(); ++i) |  | ||||||
|         { |  | ||||||
|             util::json::Object scc_trip; |  | ||||||
| 
 |  | ||||||
|             // annotate comp_route[i] as a json trip
 |  | ||||||
|             auto generator = MakeApiResponseGenerator(facade); |  | ||||||
|             generator.DescribeRoute(route_parameters, comp_route[i], scc_trip); |  | ||||||
| 
 |  | ||||||
|             // set permutation output
 |  | ||||||
|             SetLocPermutationOutput(route_result[i], scc_trip); |  | ||||||
|             // set viaroute output
 |  | ||||||
|             trip.values.push_back(std::move(scc_trip)); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         if (trip.values.empty()) |  | ||||||
|         { |  | ||||||
|             json_result.values["status_message"] = "Cannot find trips"; |  | ||||||
|             return Status::EmptyResult; |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         json_result.values["trips"] = std::move(trip); |  | ||||||
|         json_result.values["status_message"] = "Found trips"; |  | ||||||
|         return Status::Ok; |  | ||||||
|     } |  | ||||||
| }; | }; | ||||||
|  | 
 | ||||||
| } | } | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #ifndef TRIP_BRUTE_FORCE_HPP | #ifndef TRIP_BRUTE_FORCE_HPP | ||||||
| #define TRIP_BRUTE_FORCE_HPP | #define TRIP_BRUTE_FORCE_HPP | ||||||
| 
 | 
 | ||||||
| #include "engine/search_engine.hpp" | #include "util/typedefs.hpp" | ||||||
| #include "util/dist_table_wrapper.hpp" | #include "util/dist_table_wrapper.hpp" | ||||||
| #include "util/simple_logger.hpp" | #include "util/simple_logger.hpp" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #ifndef TRIP_FARTHEST_INSERTION_HPP | #ifndef TRIP_FARTHEST_INSERTION_HPP | ||||||
| #define TRIP_FARTHEST_INSERTION_HPP | #define TRIP_FARTHEST_INSERTION_HPP | ||||||
| 
 | 
 | ||||||
| #include "engine/search_engine.hpp" | #include "util/typedefs.hpp" | ||||||
| #include "util/dist_table_wrapper.hpp" | #include "util/dist_table_wrapper.hpp" | ||||||
| 
 | 
 | ||||||
| #include "osrm/json_container.hpp" | #include "osrm/json_container.hpp" | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| #ifndef TRIP_NEAREST_NEIGHBOUR_HPP | #ifndef TRIP_NEAREST_NEIGHBOUR_HPP | ||||||
| #define TRIP_NEAREST_NEIGHBOUR_HPP | #define TRIP_NEAREST_NEIGHBOUR_HPP | ||||||
| 
 | 
 | ||||||
| #include "engine/search_engine.hpp" | #include "util/typedefs.hpp" | ||||||
| #include "util/simple_logger.hpp" | #include "util/simple_logger.hpp" | ||||||
| #include "util/dist_table_wrapper.hpp" | #include "util/dist_table_wrapper.hpp" | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ | |||||||
| 
 | 
 | ||||||
| #include <boost/spirit/include/qi_lit.hpp> | #include <boost/spirit/include/qi_lit.hpp> | ||||||
| #include <boost/spirit/include/qi_uint.hpp> | #include <boost/spirit/include/qi_uint.hpp> | ||||||
|  | #include <boost/spirit/include/qi_bool.hpp> | ||||||
| #include <boost/spirit/include/qi_grammar.hpp> | #include <boost/spirit/include/qi_grammar.hpp> | ||||||
| #include <boost/spirit/include/qi_action.hpp> | #include <boost/spirit/include/qi_action.hpp> | ||||||
| #include <boost/spirit/include/qi_optional.hpp> | #include <boost/spirit/include/qi_optional.hpp> | ||||||
| @ -23,16 +24,53 @@ namespace qi = boost::spirit::qi; | |||||||
| struct TripParametersGrammar final : public BaseParametersGrammar | struct TripParametersGrammar final : public BaseParametersGrammar | ||||||
| { | { | ||||||
|     using Iterator = std::string::iterator; |     using Iterator = std::string::iterator; | ||||||
|  |     using StepsT = bool; | ||||||
|  |     using GeometriesT = engine::api::RouteParameters::GeometriesType; | ||||||
|  |     using OverviewT = engine::api::RouteParameters::OverviewType; | ||||||
| 
 | 
 | ||||||
|     TripParametersGrammar() : BaseParametersGrammar(root_rule, parameters) |     TripParametersGrammar() : BaseParametersGrammar(root_rule, parameters) | ||||||
|     { |     { | ||||||
|         root_rule = "TODO(daniel-j-h)"; |         const auto set_geojson_type = [this]() | ||||||
|  |         { | ||||||
|  |             parameters.geometries = engine::api::RouteParameters::GeometriesType::GeoJSON; | ||||||
|  |         }; | ||||||
|  |         const auto set_polyline_type = [this]() | ||||||
|  |         { | ||||||
|  |             parameters.geometries = engine::api::RouteParameters::GeometriesType::Polyline; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         const auto set_simplified_type = [this]() | ||||||
|  |         { | ||||||
|  |             parameters.overview = engine::api::RouteParameters::OverviewType::Simplified; | ||||||
|  |         }; | ||||||
|  |         const auto set_full_type = [this]() | ||||||
|  |         { | ||||||
|  |             parameters.overview = engine::api::RouteParameters::OverviewType::Full; | ||||||
|  |         }; | ||||||
|  |         const auto set_false_type = [this]() | ||||||
|  |         { | ||||||
|  |             parameters.overview = engine::api::RouteParameters::OverviewType::False; | ||||||
|  |         }; | ||||||
|  |         const auto set_steps = [this](const StepsT steps) | ||||||
|  |         { | ||||||
|  |             parameters.steps = steps; | ||||||
|  |         }; | ||||||
|  | 
 | ||||||
|  |         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]; | ||||||
|  |         trip_rule = steps_rule[set_steps] | geometries_rule | overview_rule; | ||||||
|  |         root_rule = -((base_rule | trip_rule) % '&'); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     engine::api::TripParameters parameters; |     engine::api::TripParameters parameters; | ||||||
| 
 | 
 | ||||||
|   private: |   private: | ||||||
|     qi::rule<Iterator> root_rule, trip_rule; |     qi::rule<Iterator> root_rule, trip_rule, geometries_rule, overview_rule; | ||||||
|  |     qi::rule<Iterator, StepsT()> steps_rule; | ||||||
| }; | }; | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
| @ -7,7 +7,7 @@ | |||||||
| //#include "engine/plugins/hello_world.hpp"
 | //#include "engine/plugins/hello_world.hpp"
 | ||||||
| #include "engine/plugins/nearest.hpp" | #include "engine/plugins/nearest.hpp" | ||||||
| //#include "engine/plugins/timestamp.hpp"
 | //#include "engine/plugins/timestamp.hpp"
 | ||||||
| //#include "engine/plugins/trip.hpp"
 | #include "engine/plugins/trip.hpp" | ||||||
| #include "engine/plugins/viaroute.hpp" | #include "engine/plugins/viaroute.hpp" | ||||||
| //#include "engine/plugins/tile.hpp"
 | //#include "engine/plugins/tile.hpp"
 | ||||||
| #include "engine/plugins/match.hpp" | #include "engine/plugins/match.hpp" | ||||||
| @ -146,7 +146,7 @@ Engine::Engine(EngineConfig &config) | |||||||
|     route_plugin = create<ViaRoutePlugin>(*query_data_facade, config.max_locations_viaroute); |     route_plugin = create<ViaRoutePlugin>(*query_data_facade, config.max_locations_viaroute); | ||||||
|     table_plugin = create<TablePlugin>(*query_data_facade, config.max_locations_distance_table); |     table_plugin = create<TablePlugin>(*query_data_facade, config.max_locations_distance_table); | ||||||
|     nearest_plugin = create<NearestPlugin>(*query_data_facade); |     nearest_plugin = create<NearestPlugin>(*query_data_facade); | ||||||
|     // trip_plugin = ceate<TripPlugin>(*query_data_facade, config.max_locations_trip);
 |     trip_plugin = create<TripPlugin>(*query_data_facade, config.max_locations_trip); | ||||||
|     match_plugin = create<MatchPlugin>(*query_data_facade, config.max_locations_map_matching); |     match_plugin = create<MatchPlugin>(*query_data_facade, config.max_locations_map_matching); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -172,8 +172,7 @@ Status Engine::Nearest(const api::NearestParameters ¶ms, util::json::Object | |||||||
| 
 | 
 | ||||||
| Status Engine::Trip(const api::TripParameters ¶ms, util::json::Object &result) | Status Engine::Trip(const api::TripParameters ¶ms, util::json::Object &result) | ||||||
| { | { | ||||||
|     // return RunQuery(lock, *query_data_facade, params, *trip_plugin, result);
 |     return RunQuery(lock, *query_data_facade, params, *trip_plugin, result); | ||||||
|     return Status::Error; |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| Status Engine::Match(const api::MatchParameters ¶ms, util::json::Object &result) | Status Engine::Match(const api::MatchParameters ¶ms, util::json::Object &result) | ||||||
|  | |||||||
| @ -163,7 +163,7 @@ Status MatchPlugin::HandleRequest(const api::MatchParameters ¶meters, | |||||||
|     std::vector<InternalRouteResult> sub_routes(sub_matchings.size()); |     std::vector<InternalRouteResult> sub_routes(sub_matchings.size()); | ||||||
|     for (auto index : util::irange(0UL, sub_matchings.size())) |     for (auto index : util::irange(0UL, sub_matchings.size())) | ||||||
|     { |     { | ||||||
|         BOOST_ASSERT(sub.nodes.size() > 1); |         BOOST_ASSERT(sub_matchings[index].nodes.size() > 1); | ||||||
| 
 | 
 | ||||||
|         // FIXME we only run this to obtain the geometry
 |         // FIXME we only run this to obtain the geometry
 | ||||||
|         // The clean way would be to get this directly from the map matching plugin
 |         // The clean way would be to get this directly from the map matching plugin
 | ||||||
| @ -177,7 +177,7 @@ Status MatchPlugin::HandleRequest(const api::MatchParameters ¶meters, | |||||||
|             sub_routes[index].segment_end_coordinates.emplace_back(current_phantom_node_pair); |             sub_routes[index].segment_end_coordinates.emplace_back(current_phantom_node_pair); | ||||||
|         } |         } | ||||||
|         shortest_path(sub_routes[index].segment_end_coordinates, {}, sub_routes[index]); |         shortest_path(sub_routes[index].segment_end_coordinates, {}, sub_routes[index]); | ||||||
|         BOOST_ASSERT(raw_route.shortest_path_length != INVALID_EDGE_WEIGHT); |         BOOST_ASSERT(sub_routes[index].shortest_path_length != INVALID_EDGE_WEIGHT); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     api::MatchAPI match_api{BasePlugin::facade, parameters}; |     api::MatchAPI match_api{BasePlugin::facade, parameters}; | ||||||
|  | |||||||
							
								
								
									
										258
									
								
								src/engine/plugins/trip.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										258
									
								
								src/engine/plugins/trip.cpp
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,258 @@ | |||||||
|  | #include "engine/plugins/trip.hpp" | ||||||
|  | 
 | ||||||
|  | #include "extractor/tarjan_scc.hpp" | ||||||
|  | 
 | ||||||
|  | #include "engine/api/trip_api.hpp" | ||||||
|  | #include "engine/api/trip_parameters.hpp" | ||||||
|  | #include "engine/trip/trip_nearest_neighbour.hpp" | ||||||
|  | #include "engine/trip/trip_farthest_insertion.hpp" | ||||||
|  | #include "engine/trip/trip_brute_force.hpp" | ||||||
|  | #include "util/dist_table_wrapper.hpp"   // to access the dist table more easily
 | ||||||
|  | #include "util/matrix_graph_wrapper.hpp" // wrapper to use tarjan scc on dist table
 | ||||||
|  | #include "util/json_container.hpp" | ||||||
|  | 
 | ||||||
|  | #include <boost/assert.hpp> | ||||||
|  | 
 | ||||||
|  | #include <cstdlib> | ||||||
|  | #include <algorithm> | ||||||
|  | #include <memory> | ||||||
|  | #include <string> | ||||||
|  | #include <utility> | ||||||
|  | #include <vector> | ||||||
|  | #include <iterator> | ||||||
|  | 
 | ||||||
|  | namespace osrm | ||||||
|  | { | ||||||
|  | namespace engine | ||||||
|  | { | ||||||
|  | namespace plugins | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  | // Object to hold all strongly connected components (scc) of a graph
 | ||||||
|  | // to access all graphs with component ID i, get the iterators by:
 | ||||||
|  | // auto start = std::begin(scc_component.component) + scc_component.range[i];
 | ||||||
|  | // auto end = std::begin(scc_component.component) + scc_component.range[i+1];
 | ||||||
|  | struct SCC_Component | ||||||
|  | { | ||||||
|  |     // in_component: all NodeIDs sorted by component ID
 | ||||||
|  |     // in_range: index where a new component starts
 | ||||||
|  |     //
 | ||||||
|  |     // example: NodeID 0, 1, 2, 4, 5 are in component 0
 | ||||||
|  |     //          NodeID 3, 6, 7, 8    are in component 1
 | ||||||
|  |     //          => in_component = [0, 1, 2, 4, 5, 3, 6, 7, 8]
 | ||||||
|  |     //          => in_range = [0, 5]
 | ||||||
|  |     SCC_Component(std::vector<NodeID> in_component_nodes, std::vector<size_t> in_range) | ||||||
|  |         : component(std::move(in_component_nodes)), range(std::move(in_range)) | ||||||
|  |     { | ||||||
|  |         BOOST_ASSERT_MSG(component.size() > 0, "there's no scc component"); | ||||||
|  |         BOOST_ASSERT_MSG(*std::max_element(range.begin(), range.end()) == component.size(), | ||||||
|  |                          "scc component ranges are out of bound"); | ||||||
|  |         BOOST_ASSERT_MSG(*std::min_element(range.begin(), range.end()) == 0, | ||||||
|  |                          "invalid scc component range"); | ||||||
|  |         BOOST_ASSERT_MSG(std::is_sorted(std::begin(range), std::end(range)), | ||||||
|  |                          "invalid component ranges"); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     std::size_t GetNumberOfComponents() const | ||||||
|  |     { | ||||||
|  |         BOOST_ASSERT_MSG(range.size() > 0, "there's no range"); | ||||||
|  |         return range.size() - 1; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const std::vector<NodeID> component; | ||||||
|  |     std::vector<std::size_t> range; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // takes the number of locations and its duration matrix,
 | ||||||
|  | // identifies and splits the graph in its strongly connected components (scc)
 | ||||||
|  | // and returns an SCC_Component
 | ||||||
|  | SCC_Component SplitUnaccessibleLocations(const std::size_t number_of_locations, | ||||||
|  |                                          const util::DistTableWrapper<EdgeWeight> &result_table) | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     if (std::find(std::begin(result_table), std::end(result_table), INVALID_EDGE_WEIGHT) == | ||||||
|  |         std::end(result_table)) | ||||||
|  |     { | ||||||
|  |         // whole graph is one scc
 | ||||||
|  |         std::vector<NodeID> location_ids(number_of_locations); | ||||||
|  |         std::iota(std::begin(location_ids), std::end(location_ids), 0); | ||||||
|  |         std::vector<size_t> range = {0, location_ids.size()}; | ||||||
|  |         return SCC_Component(std::move(location_ids), std::move(range)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Run TarjanSCC
 | ||||||
|  |     auto wrapper = std::make_shared<util::MatrixGraphWrapper<EdgeWeight>>(result_table.GetTable(), | ||||||
|  |                                                                           number_of_locations); | ||||||
|  |     auto scc = extractor::TarjanSCC<util::MatrixGraphWrapper<EdgeWeight>>(wrapper); | ||||||
|  |     scc.run(); | ||||||
|  | 
 | ||||||
|  |     const auto number_of_components = scc.get_number_of_components(); | ||||||
|  | 
 | ||||||
|  |     std::vector<std::size_t> range_insertion; | ||||||
|  |     std::vector<std::size_t> range; | ||||||
|  |     range_insertion.reserve(number_of_components); | ||||||
|  |     range.reserve(number_of_components); | ||||||
|  | 
 | ||||||
|  |     std::vector<NodeID> components(number_of_locations, 0); | ||||||
|  | 
 | ||||||
|  |     std::size_t prefix = 0; | ||||||
|  |     for (std::size_t j = 0; j < number_of_components; ++j) | ||||||
|  |     { | ||||||
|  |         range_insertion.push_back(prefix); | ||||||
|  |         range.push_back(prefix); | ||||||
|  |         prefix += scc.get_component_size(j); | ||||||
|  |     } | ||||||
|  |     // senitel
 | ||||||
|  |     range.push_back(components.size()); | ||||||
|  | 
 | ||||||
|  |     for (std::size_t i = 0; i < number_of_locations; ++i) | ||||||
|  |     { | ||||||
|  |         components[range_insertion[scc.get_component_id(i)]] = i; | ||||||
|  |         ++range_insertion[scc.get_component_id(i)]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return SCC_Component(std::move(components), std::move(range)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | InternalRouteResult TripPlugin::ComputeRoute(const std::vector<PhantomNode> &snapped_phantoms, | ||||||
|  |                                              const api::TripParameters ¶meters, | ||||||
|  |                                              const std::vector<NodeID> &trip) | ||||||
|  | { | ||||||
|  |     InternalRouteResult min_route; | ||||||
|  |     // given he final trip, compute total duration and return the route and location permutation
 | ||||||
|  |     PhantomNodes viapoint; | ||||||
|  |     const auto start = std::begin(trip); | ||||||
|  |     const auto end = std::end(trip); | ||||||
|  |     // computes a roundtrip from the nodes in trip
 | ||||||
|  |     for (auto it = start; it != end; ++it) | ||||||
|  |     { | ||||||
|  |         const auto from_node = *it; | ||||||
|  |         // if from_node is the last node, compute the route from the last to the first location
 | ||||||
|  |         const auto to_node = std::next(it) != end ? *std::next(it) : *start; | ||||||
|  | 
 | ||||||
|  |         viapoint = PhantomNodes{snapped_phantoms[from_node], snapped_phantoms[to_node]}; | ||||||
|  |         min_route.segment_end_coordinates.emplace_back(viapoint); | ||||||
|  |     } | ||||||
|  |     BOOST_ASSERT(min_route.segment_end_coordinates.size() == trip.size()); | ||||||
|  | 
 | ||||||
|  |     std::vector<boost::optional<bool>> uturns; | ||||||
|  |     if (parameters.uturns.size() > 0) | ||||||
|  |     { | ||||||
|  |         uturns.resize(trip.size() + 1); | ||||||
|  |         std::transform(trip.begin(), trip.end(), uturns.begin(), [¶meters](const NodeID idx) | ||||||
|  |                 { | ||||||
|  |                 return parameters.uturns[idx]; | ||||||
|  |                 }); | ||||||
|  |         BOOST_ASSERT(uturns.size() > 0); | ||||||
|  |         uturns.back() = parameters.uturns[trip.front()]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     shortest_path(min_route.segment_end_coordinates, uturns, min_route); | ||||||
|  | 
 | ||||||
|  |     BOOST_ASSERT_MSG(min_route.shortest_path_length < INVALID_EDGE_WEIGHT, "unroutable route"); | ||||||
|  |     return min_route; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | Status TripPlugin::HandleRequest(const api::TripParameters ¶meters, | ||||||
|  |                                  util::json::Object &json_result) | ||||||
|  | { | ||||||
|  |     BOOST_ASSERT(parameters.IsValid()); | ||||||
|  | 
 | ||||||
|  |     // enforce maximum number of locations for performance reasons
 | ||||||
|  |     if (max_locations_trip > 0 && | ||||||
|  |         static_cast<int>(parameters.coordinates.size()) > max_locations_trip) | ||||||
|  |     { | ||||||
|  |         return Error("TooBig", "Too many trip coordinates", json_result); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (!CheckAllCoordinates(parameters.coordinates)) | ||||||
|  |     { | ||||||
|  |         return Error("InvalidValue", "Invalid coordinate value.", json_result); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     auto phantom_node_pairs = GetPhantomNodes(parameters); | ||||||
|  |     if (phantom_node_pairs.size() != 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() == parameters.coordinates.size()); | ||||||
|  | 
 | ||||||
|  |     auto snapped_phantoms = SnapPhantomNodes(phantom_node_pairs); | ||||||
|  | 
 | ||||||
|  |     const auto number_of_locations = snapped_phantoms.size(); | ||||||
|  | 
 | ||||||
|  |     // compute the duration table of all phantom nodes
 | ||||||
|  |     const auto result_table = util::DistTableWrapper<EdgeWeight>( | ||||||
|  |         duration_table(snapped_phantoms), number_of_locations); | ||||||
|  | 
 | ||||||
|  |     if (result_table.size() == 0) | ||||||
|  |     { | ||||||
|  |         return Status::Error; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const constexpr std::size_t BF_MAX_FEASABLE = 10; | ||||||
|  |     BOOST_ASSERT_MSG(result_table.size() == number_of_locations * number_of_locations, | ||||||
|  |                      "Distance Table has wrong size"); | ||||||
|  | 
 | ||||||
|  |     // get scc components
 | ||||||
|  |     SCC_Component scc = SplitUnaccessibleLocations(number_of_locations, result_table); | ||||||
|  | 
 | ||||||
|  |     using NodeIDIterator = typename std::vector<NodeID>::const_iterator; | ||||||
|  | 
 | ||||||
|  |     std::vector<std::vector<NodeID>> trips; | ||||||
|  |     trips.reserve(scc.GetNumberOfComponents()); | ||||||
|  |     // run Trip computation for every SCC
 | ||||||
|  |     for (std::size_t k = 0; k < scc.GetNumberOfComponents(); ++k) | ||||||
|  |     { | ||||||
|  |         const auto component_size = scc.range[k + 1] - scc.range[k]; | ||||||
|  | 
 | ||||||
|  |         BOOST_ASSERT_MSG(component_size > 0, "invalid component size"); | ||||||
|  | 
 | ||||||
|  |         std::vector<NodeID> scc_route; | ||||||
|  |         NodeIDIterator start = std::begin(scc.component) + scc.range[k]; | ||||||
|  |         NodeIDIterator end = std::begin(scc.component) + scc.range[k + 1]; | ||||||
|  | 
 | ||||||
|  |         if (component_size > 1) | ||||||
|  |         { | ||||||
|  | 
 | ||||||
|  |             if (component_size < BF_MAX_FEASABLE) | ||||||
|  |             { | ||||||
|  |                 scc_route = trip::BruteForceTrip(start, end, number_of_locations, result_table); | ||||||
|  |             } | ||||||
|  |             else | ||||||
|  |             { | ||||||
|  |                 scc_route = | ||||||
|  |                     trip::FarthestInsertionTrip(start, end, number_of_locations, result_table); | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         else | ||||||
|  |         { | ||||||
|  |             scc_route = std::vector<NodeID>(start, end); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         trips.push_back(std::move(scc_route)); | ||||||
|  |     } | ||||||
|  |     if (trips.empty()) | ||||||
|  |     { | ||||||
|  |         return Error("NoTrips", "Cannot find trips", json_result); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // compute all round trip routes
 | ||||||
|  |     std::vector<InternalRouteResult> routes; | ||||||
|  |     routes.reserve(trips.size()); | ||||||
|  |     for (auto &trip : trips) | ||||||
|  |     { | ||||||
|  |         routes.push_back(ComputeRoute(snapped_phantoms, parameters, trip)); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     api::TripAPI trip_api{BasePlugin::facade, parameters}; | ||||||
|  |     trip_api.MakeResponse(trips, routes, snapped_phantoms, json_result); | ||||||
|  | 
 | ||||||
|  |     return Status::Ok; | ||||||
|  | } | ||||||
|  | } | ||||||
|  | } | ||||||
|  | } | ||||||
| @ -1,4 +1,5 @@ | |||||||
| #include "server/service/trip_service.hpp" | #include "server/service/trip_service.hpp" | ||||||
|  | #include "server/service/utils.hpp" | ||||||
| 
 | 
 | ||||||
| #include "engine/api/trip_parameters.hpp" | #include "engine/api/trip_parameters.hpp" | ||||||
| #include "server/api/parameters_parser.hpp" | #include "server/api/parameters_parser.hpp" | ||||||
| @ -13,14 +14,60 @@ namespace server | |||||||
| { | { | ||||||
| namespace service | namespace service | ||||||
| { | { | ||||||
|  | namespace | ||||||
|  | { | ||||||
|  | std::string getWrongOptionHelp(const engine::api::TripParameters ¶meters) | ||||||
|  | { | ||||||
|  |     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); | ||||||
|  | 
 | ||||||
|  |     if (!param_size_mismatch && parameters.coordinates.size() < 2) | ||||||
|  |     { | ||||||
|  |         help = "Number of coordinates needs to be at least two."; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return help; | ||||||
|  | } | ||||||
|  | } // anon. ns
 | ||||||
| 
 | 
 | ||||||
| engine::Status TripService::RunQuery(std::vector<util::FixedPointCoordinate> coordinates, | engine::Status TripService::RunQuery(std::vector<util::FixedPointCoordinate> coordinates, | ||||||
|                                      std::string &options, |                                       std::string &options, | ||||||
|                                      util::json::Object &result) |                                       util::json::Object &result) | ||||||
| { | { | ||||||
|     // TODO(daniel-j-h)
 |     auto options_iterator = options.begin(); | ||||||
|     return Status::Error; |     auto parameters = | ||||||
| } |         api::parseParameters<engine::api::TripParameters>(options_iterator, options.end()); | ||||||
|  |     if (!parameters || options_iterator != options.end()) | ||||||
|  |     { | ||||||
|  |         const auto position = std::distance(options.begin(), options_iterator); | ||||||
|  |         result.values["code"] = "invalid-options"; | ||||||
|  |         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()) | ||||||
|  |     { | ||||||
|  |         result.values["code"] = "invalid-options"; | ||||||
|  |         result.values["message"] = getWrongOptionHelp(*parameters); | ||||||
|  |         return engine::Status::Error; | ||||||
|  |     } | ||||||
|  |     BOOST_ASSERT(parameters->IsValid()); | ||||||
|  | 
 | ||||||
|  |     return BaseService::routing_machine.Trip(*parameters, result); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| } | } | ||||||
| } | } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user