diff --git a/data_structures/route_parameters.cpp b/data_structures/route_parameters.cpp index 7e2117da7..51ac9f23f 100644 --- a/data_structures/route_parameters.cpp +++ b/data_structures/route_parameters.cpp @@ -148,8 +148,18 @@ void RouteParameters::addCoordinate( static_cast(COORDINATE_PRECISION * boost::fusion::at_c<1>(received_coordinates))); } +void RouteParameters::addSource( + const boost::fusion::vector &received_coordinates) +{ + sources.emplace_back( + static_cast(COORDINATE_PRECISION * boost::fusion::at_c<0>(received_coordinates)), + static_cast(COORDINATE_PRECISION * boost::fusion::at_c<1>(received_coordinates))); +} + void RouteParameters::getCoordinatesFromGeometry(const std::string &geometry_string) { PolylineCompressor pc; coordinates = pc.decode_string(geometry_string); } + +void RouteParameters::setMappedPointsFlag(const bool flag) { mapped_points = flag; } diff --git a/features/step_definitions/distance_matrix.rb b/features/step_definitions/distance_matrix.rb index 8c4e927de..f51fec238 100644 --- a/features/step_definitions/distance_matrix.rb +++ b/features/step_definitions/distance_matrix.rb @@ -1,5 +1,4 @@ When /^I request a travel time matrix I should get$/ do |table| - no_route = 2147483647 # MAX_INT raise "*** Top-left cell of matrix table must be empty" unless table.headers[0]=="" @@ -7,7 +6,7 @@ When /^I request a travel time matrix I should get$/ do |table| nodes = [] column_headers = table.headers[1..-1] row_headers = table.rows.map { |h| h.first } - unless column_headers==row_headers + unless column_headers==row_headers || @query_params['src'] raise "*** Column and row headers must match in matrix table, got #{column_headers.inspect} and #{row_headers.inspect}" end column_headers.each do |node_name| @@ -23,6 +22,10 @@ When /^I request a travel time matrix I should get$/ do |table| # compute matrix params = @query_params + if params['src'] + node = find_node_by_name(params['src']) + params['src'] = node.lat + ',' + node.lon + end response = request_table nodes, params if response.body.empty? == false json = JSON.parse response.body diff --git a/features/testbot/distance_matrix.feature b/features/testbot/distance_matrix.feature index 390ae9847..0a41530d4 100644 --- a/features/testbot/distance_matrix.feature +++ b/features/testbot/distance_matrix.feature @@ -100,3 +100,24 @@ Feature: Basic Distance Matrix | y | 500 | 0 | 300 | 200 | | d | 200 | 300 | 0 | 300 | | e | 300 | 400 | 100 | 0 | + + Scenario: Testbot - Travel time matrix and mapped coordinates with only one source + Given the node map + | a | b | c | + | d | e | f | + + And the ways + | nodes | + | abc | + | def | + | ad | + | be | + | cf | + + And the query options + | mapped_points | true | + | src | a | + + When I request a travel time matrix I should get + | | a | b | e | f | + | a | 0 | 100 | 200 | 300 | diff --git a/include/osrm/route_parameters.hpp b/include/osrm/route_parameters.hpp index 1b2a698ea..092a2e9be 100644 --- a/include/osrm/route_parameters.hpp +++ b/include/osrm/route_parameters.hpp @@ -82,8 +82,12 @@ struct RouteParameters void addCoordinate(const boost::fusion::vector &received_coordinates); + void addSource(const boost::fusion::vector &received_coordinates); + void getCoordinatesFromGeometry(const std::string &geometry_string); + void setMappedPointsFlag(const bool flag); + short zoom_level; bool print_instructions; bool alternate_route; @@ -92,6 +96,7 @@ struct RouteParameters bool deprecatedAPI; bool uturn_default; bool classify; + bool mapped_points; double matching_beta; double gps_precision; unsigned check_sum; @@ -105,6 +110,7 @@ struct RouteParameters std::vector>> bearings; std::vector uturns; std::vector coordinates; + std::vector sources; }; #endif // ROUTE_PARAMETERS_HPP diff --git a/plugins/distance_table.hpp b/plugins/distance_table.hpp index df4de54c5..6eed9f6c6 100644 --- a/plugins/distance_table.hpp +++ b/plugins/distance_table.hpp @@ -70,13 +70,14 @@ template class DistanceTablePlugin final : public BasePlugin int HandleRequest(const RouteParameters &route_parameters, osrm::json::Object &json_result) override final { - if (!check_all_coordinates(route_parameters.coordinates)) + if (!check_all_coordinates(route_parameters.coordinates) || + (route_parameters.sources.size() && !check_all_coordinates(route_parameters.sources, 1))) { return 400; } const auto &input_bearings = route_parameters.bearings; - if (input_bearings.size() > 0 && route_parameters.coordinates.size() != input_bearings.size()) + if (input_bearings.size() > 0 && route_parameters.coordinates.size() + route_parameters.sources.size() != input_bearings.size()) { json_result.values["status"] = "Number of bearings does not match number of coordinates ."; return 400; @@ -87,7 +88,7 @@ template class DistanceTablePlugin final : public BasePlugin std::min(static_cast(max_locations_distance_table), static_cast(route_parameters.coordinates.size())); - PhantomNodeArray phantom_node_vector(max_locations); + PhantomNodeArray phantom_node_target_vector(max_locations); for (const auto i : osrm::irange(0u, max_locations)) { if (checksum_OK && i < route_parameters.hints.size() && @@ -97,21 +98,49 @@ template class DistanceTablePlugin final : public BasePlugin ObjectEncoder::DecodeFromBase64(route_parameters.hints[i], current_phantom_node); if (current_phantom_node.is_valid(facade->GetNumberOfNodes())) { - phantom_node_vector[i].emplace_back(std::move(current_phantom_node)); + phantom_node_target_vector[i].emplace_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; facade->IncrementalFindPhantomNodeForCoordinate(route_parameters.coordinates[i], - phantom_node_vector[i], 1, bearing, range); + phantom_node_target_vector[i], 1, bearing, range); - BOOST_ASSERT(phantom_node_vector[i].front().is_valid(facade->GetNumberOfNodes())); + BOOST_ASSERT(phantom_node_target_vector[i].front().is_valid(facade->GetNumberOfNodes())); + } + unsigned nb_coordinates = route_parameters.coordinates.size(); + max_locations = 0; + if (route_parameters.sources.size()) + { + max_locations = std::min(static_cast(max_locations_distance_table), + static_cast(route_parameters.sources.size())); + } + PhantomNodeArray phantom_node_source_vector(max_locations); + for (const auto i : osrm::irange(0u, max_locations)) + { + if (checksum_OK && i < route_parameters.hints.size() - route_parameters.coordinates.size() && + !route_parameters.hints[i+route_parameters.coordinates.size()].empty()) + { + PhantomNode current_phantom_node; + ObjectEncoder::DecodeFromBase64(route_parameters.hints[i+route_parameters.coordinates.size()], current_phantom_node); + if (current_phantom_node.is_valid(facade->GetNumberOfNodes())) + { + phantom_node_source_vector[i].emplace_back(std::move(current_phantom_node)); + continue; + } + } + const int bearing = input_bearings.size() > 0 ? input_bearings[nb_coordinates + i].first : 0; + const int range = input_bearings.size() > 0 ? (input_bearings[nb_coordinates + i].second?*input_bearings[nb_coordinates + i].second:10) : 180; + facade->IncrementalFindPhantomNodeForCoordinate(route_parameters.sources[i], + phantom_node_source_vector[i], 1, bearing, range); + + BOOST_ASSERT(phantom_node_source_vector[i].front().is_valid(facade->GetNumberOfNodes())); } // TIMER_START(distance_table); std::shared_ptr> result_table = - search_engine_ptr->distance_table(phantom_node_vector); + search_engine_ptr->distance_table(phantom_node_target_vector, phantom_node_source_vector); // TIMER_STOP(distance_table); if (!result_table) @@ -119,17 +148,41 @@ template class DistanceTablePlugin final : public BasePlugin return 400; } - osrm::json::Array json_array; - const auto number_of_locations = phantom_node_vector.size(); - for (const auto row : osrm::irange(0, number_of_locations)) + osrm::json::Array matrix_json_array; + const auto number_of_targets = phantom_node_target_vector.size(); + const auto number_of_sources = (phantom_node_source_vector.size()) ? phantom_node_source_vector.size() : number_of_targets; + for (const auto row : osrm::irange(0, number_of_sources)) { osrm::json::Array json_row; - auto row_begin_iterator = result_table->begin() + (row * number_of_locations); - auto row_end_iterator = result_table->begin() + ((row + 1) * number_of_locations); + auto row_begin_iterator = result_table->begin() + (row * number_of_targets); + auto row_end_iterator = result_table->begin() + ((row + 1) * number_of_targets); json_row.values.insert(json_row.values.end(), row_begin_iterator, row_end_iterator); - json_array.values.push_back(json_row); + matrix_json_array.values.push_back(json_row); + } + json_result.values["distance_table"] = matrix_json_array; + if (route_parameters.mapped_points) { + osrm::json::Array target_coord_json_array; + for (const std::vector &phantom_node_vector : phantom_node_target_vector) + { + osrm::json::Array json_coord; + FixedPointCoordinate coord = phantom_node_vector[0].location; + json_coord.values.push_back(coord.lat / COORDINATE_PRECISION); + json_coord.values.push_back(coord.lon / COORDINATE_PRECISION); + target_coord_json_array.values.push_back(json_coord); + } + json_result.values["target_mapped_coordinates"] = target_coord_json_array; + osrm::json::Array source_coord_json_array; + for (const std::vector &phantom_node_vector : phantom_node_source_vector) + { + osrm::json::Array json_coord; + FixedPointCoordinate coord = phantom_node_vector[0].location; + json_coord.values.push_back(coord.lat / COORDINATE_PRECISION); + json_coord.values.push_back(coord.lon / COORDINATE_PRECISION); + source_coord_json_array.values.push_back(json_coord); + } + if (source_coord_json_array.values.size()) + json_result.values["source_mapped_coordinates"] = source_coord_json_array; } - json_result.values["distance_table"] = json_array; // osrm::json::render(reply.content, json_object); return 200; } diff --git a/plugins/plugin_base.hpp b/plugins/plugin_base.hpp index acc820432..5ae53632c 100644 --- a/plugins/plugin_base.hpp +++ b/plugins/plugin_base.hpp @@ -45,9 +45,9 @@ class BasePlugin virtual const std::string GetDescriptor() const = 0; virtual int HandleRequest(const RouteParameters &, osrm::json::Object &) = 0; virtual bool - check_all_coordinates(const std::vector &coordinates) const final + check_all_coordinates(const std::vector &coordinates, const unsigned min = 2) const final { - if (2 > coordinates.size() || std::any_of(std::begin(coordinates), std::end(coordinates), + if (min > coordinates.size() || std::any_of(std::begin(coordinates), std::end(coordinates), [](const FixedPointCoordinate &coordinate) { return !coordinate.is_valid(); diff --git a/routing_algorithms/many_to_many.hpp b/routing_algorithms/many_to_many.hpp index 238880471..a2c4f8a10 100644 --- a/routing_algorithms/many_to_many.hpp +++ b/routing_algorithms/many_to_many.hpp @@ -67,11 +67,12 @@ class ManyToManyRouting final ~ManyToManyRouting() {} std::shared_ptr> - operator()(const PhantomNodeArray &phantom_nodes_array) const + operator()(const PhantomNodeArray &phantom_targets_array, const PhantomNodeArray &phantom_sources_array = PhantomNodeArray(0)) const { - const auto number_of_locations = phantom_nodes_array.size(); + const auto number_of_targets = phantom_targets_array.size(); + const auto number_of_sources = (phantom_sources_array.size()) ? phantom_sources_array.size() : number_of_targets; std::shared_ptr> result_table = - std::make_shared>(number_of_locations * number_of_locations, + std::make_shared>(number_of_targets * number_of_sources, std::numeric_limits::max()); engine_working_data.InitializeOrClearFirstThreadLocalStorage( @@ -82,7 +83,7 @@ class ManyToManyRouting final SearchSpaceWithBuckets search_space_with_buckets; unsigned target_id = 0; - for (const std::vector &phantom_node_vector : phantom_nodes_array) + for (const std::vector &phantom_node_vector : phantom_targets_array) { query_heap.Clear(); // insert target(s) at distance 0 @@ -113,7 +114,7 @@ class ManyToManyRouting final // for each source do forward search unsigned source_id = 0; - for (const std::vector &phantom_node_vector : phantom_nodes_array) + for (const std::vector &phantom_node_vector : (phantom_sources_array.size() ? phantom_sources_array : phantom_targets_array)) { query_heap.Clear(); for (const PhantomNode &phantom_node : phantom_node_vector) @@ -136,13 +137,13 @@ class ManyToManyRouting final // explore search space while (!query_heap.Empty()) { - ForwardRoutingStep(source_id, number_of_locations, query_heap, + ForwardRoutingStep(source_id, number_of_targets, query_heap, search_space_with_buckets, result_table); } ++source_id; } - BOOST_ASSERT(source_id == target_id); + // BOOST_ASSERT(source_id == target_id); return result_table; } diff --git a/server/api_grammar.hpp b/server/api_grammar.hpp index 7fd9986f1..2006cda84 100644 --- a/server/api_grammar.hpp +++ b/server/api_grammar.hpp @@ -40,9 +40,9 @@ template struct APIGrammar : qi::grammar> string[boost::bind(&HandlerT::setService, handler, ::_1)] >> *(query) >> -(uturns); - query = ('?') >> (+(zoom | output | jsonp | checksum | location | hint | timestamp | bearing | u | cmp | + query = ('?') >> (+(zoom | output | jsonp | checksum | location | source | hint | timestamp | bearing | u | cmp | language | instruction | geometry | alt_route | old_API | num_results | - matching_beta | gps_precision | classify | locs)); + matching_beta | gps_precision | classify | locs | mapped_points)); zoom = (-qi::lit('&')) >> qi::lit('z') >> '=' >> qi::short_[boost::bind(&HandlerT::setZoomLevel, handler, ::_1)]; @@ -61,6 +61,9 @@ template struct APIGrammar : qi::grammar> qi::lit("loc") >> '=' >> (qi::double_ >> qi::lit(',') >> qi::double_)[boost::bind(&HandlerT::addCoordinate, handler, ::_1)]; + source = (-qi::lit('&')) >> qi::lit("src") >> '=' >> + (qi::double_ >> qi::lit(',') >> + qi::double_)[boost::bind(&HandlerT::addSource, handler, ::_1)]; hint = (-qi::lit('&')) >> qi::lit("hint") >> '=' >> stringwithDot[boost::bind(&HandlerT::addHint, handler, ::_1)]; timestamp = (-qi::lit('&')) >> qi::lit("t") >> '=' >> @@ -87,6 +90,8 @@ template struct APIGrammar : qi::grammar> qi::lit("locs") >> '=' >> stringforPolyline[boost::bind(&HandlerT::getCoordinatesFromGeometry, handler, ::_1)]; + mapped_points = (-qi::lit('&')) >> qi::lit("mapped_points") >> '=' >> + qi::bool_[boost::bind(&HandlerT::setMappedPointsFlag, handler, ::_1)]; string = +(qi::char_("a-zA-Z")); stringwithDot = +(qi::char_("a-zA-Z0-9_.-")); @@ -96,9 +101,9 @@ template struct APIGrammar : qi::grammar api_call, query; - qi::rule service, zoom, output, string, jsonp, checksum, location, + qi::rule service, zoom, output, string, jsonp, checksum, location, source, hint, timestamp, bearing, stringwithDot, stringwithPercent, language, instruction, geometry, cmp, alt_route, u, - uturns, old_API, num_results, matching_beta, gps_precision, classify, locs, stringforPolyline; + uturns, old_API, num_results, matching_beta, gps_precision, classify, locs, mapped_points, stringforPolyline; HandlerT *handler; };