diff --git a/CHANGELOG.md b/CHANGELOG.md index f524becbfd1..85d75a3014f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ - ADDED: new API parameter - `snapping=any|default` to allow snapping to previously unsnappable edges [#5361](https://github.com/Project-OSRM/osrm-backend/pull/5361) - ADDED: keepalive support to the osrm-routed HTTP server [#5518](https://github.com/Project-OSRM/osrm-backend/pull/5518) - ADDED: flatbuffers output format support [#5513](https://github.com/Project-OSRM/osrm-backend/pull/5513) + - ADDED: Global 'skip_waypoints' option [#5556](https://github.com/Project-OSRM/osrm-backend/pull/5556) - Routing: - CHANGED: allow routing past `barrier=arch` [#5352](https://github.com/Project-OSRM/osrm-backend/pull/5352) - CHANGED: default car weight was reduced to 2000 kg. [#5371](https://github.com/Project-OSRM/osrm-backend/pull/5371) diff --git a/docs/http.md b/docs/http.md index 4052f1eac75..346e6e2eca8 100644 --- a/docs/http.md +++ b/docs/http.md @@ -29,15 +29,16 @@ To pass parameters to each location some options support an array like encoding: **Request options** -| Option | Values | Description | -|----------------|--------------------------------------------------------|-------------------------------------------------------------------------------------------------------| -|bearings |`{bearing};{bearing}[;{bearing} ...]` |Limits the search to segments with given bearing in degrees towards true north in clockwise direction. | -|radiuses |`{radius};{radius}[;{radius} ...]` |Limits the search to given radius in meters. | -|generate\_hints |`true` (default), `false` |Adds a Hint to the response which can be used in subsequent requests, see `hints` parameter. | -|hints |`{hint};{hint}[;{hint} ...]` |Hint from previous request to derive position in street network. | -|approaches |`{approach};{approach}[;{approach} ...]` |Keep waypoints on curb side. | -|exclude |`{class}[,{class}]` |Additive list of classes to avoid, order does not matter. | -|snapping |`default` (default), `any` |Default snapping avoids is_startpoint (see profile) edges, `any` will snap to any edge in the graph | +| Option | Values | Description | +|----------------|--------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +|bearings |`{bearing};{bearing}[;{bearing} ...]` |Limits the search to segments with given bearing in degrees towards true north in clockwise direction. | +|radiuses |`{radius};{radius}[;{radius} ...]` |Limits the search to given radius in meters. | +|generate\_hints |`true` (default), `false` |Adds a Hint to the response which can be used in subsequent requests, see `hints` parameter. | +|hints |`{hint};{hint}[;{hint} ...]` |Hint from previous request to derive position in street network. | +|approaches |`{approach};{approach}[;{approach} ...]` |Keep waypoints on curb side. | +|exclude |`{class}[,{class}]` |Additive list of classes to avoid, order does not matter. | +|snapping |`default` (default), `any` |Default snapping avoids is_startpoint (see profile) edges, `any` will snap to any edge in the graph | +|skip_waypoints |`true`, `false` (default) |Removes waypoints from the response. Waypoints are still calculated, but not serialized. Could be useful in case you are interested in some other part of response and do not want to transfer waste data. | Where the elements follow the following format: @@ -128,6 +129,9 @@ In addition to the [general options](#general-options) the following options are |------------|------------------------------|----------------------------------------------------| |number |`integer >= 1` (default `1`) |Number of nearest segments that should be returned. | +As `waypoints` is a single thing, returned byt that service, using it with option `skip_waypoints` set to `true` is quite useless, but still +possible. In that case only `code` field will be returned. + **Response** - `code` if the request was successful `Ok` otherwise see the service dependent and general status codes. @@ -207,8 +211,8 @@ In addition to the [general options](#general-options) the following options are |annotations |`true`, `false` (default), `nodes`, `distance`, `duration`, `datasources`, `weight`, `speed` |Returns additional metadata for each coordinate along the route geometry. | |geometries |`polyline` (default), `polyline6`, `geojson` |Returned route geometry format (influences overview and per step) | |overview |`simplified` (default), `full`, `false` |Add overview geometry either full, simplified according to highest zoom level it could be display on, or not at all.| -|continue\_straight |`default` (default), `true`, `false` |Forces the route to keep going straight at waypoints constraining uturns there even if it would be faster. Default value depends on the profile. | -|waypoints | `{index};{index};{index}...` |Treats input coordinates indicated by given indices as waypoints in returned Match object. Default is to treat all input coordinates as waypoints. | +|continue\_straight |`default` (default), `true`, `false` |Forces the route to keep going straight at waypoints constraining uturns there even if it would be faster. Default value depends on the profile. | +|waypoints | `{index};{index};{index}...` |Treats input coordinates indicated by given indices as waypoints in returned Match object. Default is to treat all input coordinates as waypoints. | \* Please note that even if alternative routes are requested, a result cannot be guaranteed. @@ -257,6 +261,8 @@ In addition to the [general options](#general-options) the following options are Unlike other array encoded options, the length of `sources` and `destinations` can be **smaller or equal** to number of input locations; +With `skip_waypoints` set to `true`, both `sources` and `destinations` arrays will be skipped. + **Example:** ``` @@ -967,7 +973,7 @@ Root object is the only object, available from a 'raw' `flatbuffers` buffer. It - `error`: `bool` Marks response as erroneous. Erroneus response should include `code` field set, all the other field may not present. - `code`: `Error` Error description object, only present, when `error` is `true` -- `waypoints`: `[Waypoint]` Array of `Waypoint` objects. Should present for every service call. Table service will put `sources` array here. +- `waypoints`: `[Waypoint]` Array of `Waypoint` objects. Should present for every service call, unless `skip_waypoints` is set to `true`. Table service will put `sources` array here. - `routes`: `[RouteObject]` Array of `RouteObject` objects. May be empty or absent. Should present for Route/Trip/Match services call. - `table`: `Table` Table object, may absent. Should be present in case of Table service call. @@ -1112,6 +1118,7 @@ Almost same as `json` Table object. The main difference is that 'sources' field used instead. All the other differences follow: - `durations`: `[float]` Flat representation of a durations matrix. Element at row;col can be adressed as [row * cols + col] -- `destinations`: `[float]` Flat representation of a destinations matrix. Element at row;col can be adressed as [row * cols + col] +- `distances`: `[float]` Flat representation of a destinations matrix. Element at row;col can be adressed as [row * cols + col] +- `destinations`: `[Waypoint]` Array of `Waypoint` objects. Will be `null` if `skip_waypoints` will be set to `true` - `rows`: `ushort` Number of rows in durations/destinations matrices. - `cols`: `ushort` Number of cols in durations/destinations matrices. diff --git a/features/step_definitions/distance_matrix.js b/features/step_definitions/distance_matrix.js index 594268d50fa..37de07a269d 100644 --- a/features/step_definitions/distance_matrix.js +++ b/features/step_definitions/distance_matrix.js @@ -1,27 +1,38 @@ var util = require('util'); +var flatbuffers = require('../support/flatbuffers').flatbuffers; +var FBResult = require('../support/fbresult_generated').osrm.engine.api.fbresult.FBResult; + module.exports = function () { const durationsRegex = new RegExp(/^I request a travel time matrix I should get$/); const distancesRegex = new RegExp(/^I request a travel distance matrix I should get$/); const estimatesRegex = new RegExp(/^I request a travel time matrix I should get estimates for$/); + const durationsRegexFb = new RegExp(/^I request a travel time matrix with flatbuffers I should get$/); + const distancesRegexFb = new RegExp(/^I request a travel distance matrix with flatbuffers I should get$/); const DURATIONS_NO_ROUTE = 2147483647; // MAX_INT const DISTANCES_NO_ROUTE = 3.40282e+38; // MAX_FLOAT - this.When(durationsRegex, function(table, callback) {tableParse.call(this, table, DURATIONS_NO_ROUTE, 'durations', callback);}.bind(this)); - this.When(distancesRegex, function(table, callback) {tableParse.call(this, table, DISTANCES_NO_ROUTE, 'distances', callback);}.bind(this)); - this.When(estimatesRegex, function(table, callback) {tableParse.call(this, table, DISTANCES_NO_ROUTE, 'fallback_speed_cells', callback);}.bind(this)); + const FORMAT_JSON = 'json'; + const FORMAT_FB = 'flatbuffers'; + + this.When(durationsRegex, function(table, callback) {tableParse.call(this, table, DURATIONS_NO_ROUTE, 'durations', FORMAT_JSON, callback);}.bind(this)); + this.When(distancesRegex, function(table, callback) {tableParse.call(this, table, DISTANCES_NO_ROUTE, 'distances', FORMAT_JSON, callback);}.bind(this)); + this.When(estimatesRegex, function(table, callback) {tableParse.call(this, table, DISTANCES_NO_ROUTE, 'fallback_speed_cells', FORMAT_JSON, callback);}.bind(this)); + this.When(durationsRegexFb, function(table, callback) {tableParse.call(this, table, DURATIONS_NO_ROUTE, 'durations', FORMAT_FB, callback);}.bind(this)); + this.When(distancesRegexFb, function(table, callback) {tableParse.call(this, table, DISTANCES_NO_ROUTE, 'distances', FORMAT_FB, callback);}.bind(this)); }; const durationsParse = function(v) { return isNaN(parseInt(v)); }; const distancesParse = function(v) { return isNaN(parseFloat(v)); }; const estimatesParse = function(v) { return isNaN(parseFloat(v)); }; -function tableParse(table, noRoute, annotation, callback) { +function tableParse(table, noRoute, annotation, format, callback) { const parse = annotation == 'distances' ? distancesParse : (annotation == 'durations' ? durationsParse : estimatesParse); const params = this.queryParams; params.annotations = ['durations','fallback_speed_cells'].indexOf(annotation) !== -1 ? 'duration' : 'distance'; + params.output = format; var tableRows = table.raw(); @@ -62,27 +73,53 @@ function tableParse(table, noRoute, annotation, callback) { if (err) return callback(err); if (!response.body.length) return callback(new Error('Invalid response body')); - var json = JSON.parse(response.body); - - var result = {}; - if (annotation === 'fallback_speed_cells') { - result = table.raw().map(row => row.map(() => '')); - json[annotation].forEach(pair => { - result[pair[0]+1][pair[1]+1] = 'Y'; - }); - result = result.slice(1).map(row => { - var hashes = {}; - row.slice(1).forEach((v,i) => { - hashes[tableRows[0][i+1]] = v; + var result = []; + if (format === 'json') { + var json = JSON.parse(response.body); + + if (annotation === 'fallback_speed_cells') { + result = table.raw().map(row => row.map(() => '')); + json[annotation].forEach(pair => { + result[pair[0]+1][pair[1]+1] = 'Y'; }); - return hashes; - }); - } else { - result = json[annotation].map(row => { - var hashes = {}; - row.forEach((v, i) => { hashes[tableRows[0][i+1]] = parse(v) ? '' : v; }); - return hashes; - }); + result = result.slice(1).map(row => { + var hashes = {}; + row.slice(1).forEach((v,i) => { + hashes[tableRows[0][i+1]] = v; + }); + return hashes; + }); + } else { + result = json[annotation].map(row => { + var hashes = {}; + row.forEach((v, i) => { hashes[tableRows[0][i+1]] = parse(v) ? '' : v; }); + return hashes; + }); + } + } else { //flatbuffers + var body = response.body; + var bytes = new Uint8Array(body.length); + for (var indx = 0; indx < body.length; ++indx) { + bytes[indx] = body.charCodeAt(indx); + } + var buf = new flatbuffers.ByteBuffer(bytes); + var fb = FBResult.getRootAsFBResult(buf); + + var matrix; + if (annotation === 'durations') { + matrix = fb.table().durationsArray(); + } + if (annotation === 'distances') { + matrix = fb.table().distancesArray(); + } + var cols = fb.table().cols(); + var rows = fb.table().rows(); + for (let r = 0; r < rows; ++r) { + result[r]={}; + for(let c=0; c < cols; ++c) { + result[r][tableRows[0][c+1]] = matrix[r*cols + c]; + } + } } var testRow = (row, ri, cb) => { diff --git a/features/step_definitions/routing.js b/features/step_definitions/routing.js index c3e679aa5b3..136add5ef29 100644 --- a/features/step_definitions/routing.js +++ b/features/step_definitions/routing.js @@ -12,4 +12,9 @@ module.exports = function () { q.awaitAll(callback); }); + + this.Given(/^skip waypoints$/, (callback) => { + this.queryParams['skip_waypoints'] = true; + callback(); + }); }; diff --git a/features/support/shared_steps.js b/features/support/shared_steps.js index 20fc0cf71a2..0eeda27902c 100644 --- a/features/support/shared_steps.js +++ b/features/support/shared_steps.js @@ -155,6 +155,13 @@ module.exports = function () { if (headers.has('locations')){ got.locations = (locations || '').trim(); } + if (headers.has('waypoints_count')) { + if ('waypoints' in json) { + got.waypoints_count = json.waypoints.length; + } else{ + got.waypoints_count = 0; + } + } /* if (headers.has('approaches')){ got.approaches = (approaches || '').trim(); diff --git a/features/testbot/basic.feature b/features/testbot/basic.feature index 0041a133eb3..9224cd46911 100644 --- a/features/testbot/basic.feature +++ b/features/testbot/basic.feature @@ -17,9 +17,9 @@ Feature: Basic Routing | ab | When I route I should get - | from | to | route | data_version | - | a | b | ab,ab | | - | b | a | ab,ab | | + | from | to | route | data_version | waypoints_count | + | a | b | ab,ab | | 2 | + | b | a | ab,ab | | 2 | Scenario: Data_version test Given the node map @@ -38,6 +38,23 @@ Feature: Basic Routing | a | b | ab,ab | cucumber_data_version | | b | a | ab,ab | cucumber_data_version | + Scenario: Skip_waypoints test + Given the node map + """ + a b + """ + + And skip waypoints + + And the ways + | nodes | + | ab | + + When I route I should get + | from | to | route | waypoints_count | + | a | b | ab,ab | 0 | + | b | a | ab,ab | 0 | + Scenario: Routing in between two nodes of way Given the node map """ diff --git a/features/testbot/duration_matrix.feature b/features/testbot/duration_matrix.feature index dce1b177c08..94e0a64144c 100644 --- a/features/testbot/duration_matrix.feature +++ b/features/testbot/duration_matrix.feature @@ -21,6 +21,21 @@ Feature: Basic Duration Matrix | a | 0 | 10 | | b | 10 | 0 | + Scenario: Testbot - Travel time matrix of minimal network requested with flatbuffer format + Given the node map + """ + a b + """ + + And the ways + | nodes | + | ab | + + When I request a travel time matrix with flatbuffers I should get + | | a | b | + | a | 0 | 10 | + | b | 10 | 0 | + @ch Scenario: Testbot - Travel time matrix of minimal network with toll exclude Given the query options diff --git a/include/engine/api/base_parameters.hpp b/include/engine/api/base_parameters.hpp index 4e3628459f5..e7a49378df1 100644 --- a/include/engine/api/base_parameters.hpp +++ b/include/engine/api/base_parameters.hpp @@ -87,6 +87,9 @@ struct BaseParameters // Adds hints to response which can be included in subsequent requests, see `hints` above. bool generate_hints = true; + // Remove waypoints array from the response. + bool skip_waypoints = false; + SnappingType snapping = SnappingType::Default; BaseParameters(const std::vector coordinates_ = {}, diff --git a/include/engine/api/match_api.hpp b/include/engine/api/match_api.hpp index 3fb95523b01..d005c3a321e 100644 --- a/include/engine/api/match_api.hpp +++ b/include/engine/api/match_api.hpp @@ -83,7 +83,10 @@ class MatchAPI final : public RouteAPI route.values["confidence"] = sub_matchings[index].confidence; routes.values.push_back(std::move(route)); } - response.values["tracepoints"] = MakeTracepoints(sub_matchings); + if (!parameters.skip_waypoints) + { + response.values["tracepoints"] = MakeTracepoints(sub_matchings); + } response.values["matchings"] = std::move(routes); response.values["code"] = "Ok"; } diff --git a/include/engine/api/nearest_api.hpp b/include/engine/api/nearest_api.hpp index a10d424b23b..6e0788ef427 100644 --- a/include/engine/api/nearest_api.hpp +++ b/include/engine/api/nearest_api.hpp @@ -2,6 +2,7 @@ #define ENGINE_API_NEAREST_API_HPP #include "engine/api/base_api.hpp" +#include "engine/api/base_result.hpp" #include "engine/api/nearest_parameters.hpp" #include "engine/api/json_factory.hpp" @@ -54,25 +55,33 @@ class NearestAPI final : public BaseAPI data_version_string = fb_result.CreateString(data_timestamp); } - std::vector> waypoints; - waypoints.resize(phantom_nodes.front().size()); - std::transform(phantom_nodes.front().begin(), - phantom_nodes.front().end(), - waypoints.begin(), - [this, &fb_result](const PhantomNodeWithDistance &phantom_with_distance) { - auto &phantom_node = phantom_with_distance.phantom_node; + flatbuffers::Offset>> + waypoints_vector; + if (!parameters.skip_waypoints) + { + std::vector> waypoints; + waypoints.resize(phantom_nodes.front().size()); + std::transform( + phantom_nodes.front().begin(), + phantom_nodes.front().end(), + waypoints.begin(), + [this, &fb_result](const PhantomNodeWithDistance &phantom_with_distance) { + auto &phantom_node = phantom_with_distance.phantom_node; + + auto node_values = MakeNodes(phantom_node); + fbresult::Uint64Pair nodes{node_values.first, node_values.second}; - auto node_values = MakeNodes(phantom_node); - fbresult::Uint64Pair nodes{node_values.first, node_values.second}; + auto waypoint = MakeWaypoint(fb_result, phantom_node); + waypoint.add_nodes(&nodes); - auto waypoint = MakeWaypoint(fb_result, phantom_node); - waypoint.add_nodes(&nodes); + return waypoint.Finish(); + }); - return waypoint.Finish(); - }); + waypoints_vector = fb_result.CreateVector(waypoints); + } - auto waypoints_vector = fb_result.CreateVector(waypoints); fbresult::FBResultBuilder response(fb_result); + response.add_waypoints(waypoints_vector); if (data_version_string) { @@ -83,28 +92,31 @@ class NearestAPI final : public BaseAPI void MakeResponse(const std::vector> &phantom_nodes, util::json::Object &response) const { - util::json::Array waypoints; - waypoints.values.resize(phantom_nodes.front().size()); - std::transform(phantom_nodes.front().begin(), - phantom_nodes.front().end(), - waypoints.values.begin(), - [this](const PhantomNodeWithDistance &phantom_with_distance) { - auto &phantom_node = phantom_with_distance.phantom_node; - auto waypoint = MakeWaypoint(phantom_node); - - util::json::Array nodes; - - auto node_values = MakeNodes(phantom_node); - - nodes.values.push_back(node_values.first); - nodes.values.push_back(node_values.second); - waypoint.values["nodes"] = std::move(nodes); - - return waypoint; - }); + if (!parameters.skip_waypoints) + { + util::json::Array waypoints; + waypoints.values.resize(phantom_nodes.front().size()); + std::transform(phantom_nodes.front().begin(), + phantom_nodes.front().end(), + waypoints.values.begin(), + [this](const PhantomNodeWithDistance &phantom_with_distance) { + auto &phantom_node = phantom_with_distance.phantom_node; + auto waypoint = MakeWaypoint(phantom_node); + + util::json::Array nodes; + + auto node_values = MakeNodes(phantom_node); + + nodes.values.push_back(node_values.first); + nodes.values.push_back(node_values.second); + waypoint.values["nodes"] = std::move(nodes); + + return waypoint; + }); + response.values["waypoints"] = std::move(waypoints); + } response.values["code"] = "Ok"; - response.values["waypoints"] = std::move(waypoints); } const NearestParameters ¶meters; diff --git a/include/engine/api/route_api.hpp b/include/engine/api/route_api.hpp index 6450e6b56e3..8adac88efac 100644 --- a/include/engine/api/route_api.hpp +++ b/include/engine/api/route_api.hpp @@ -3,6 +3,7 @@ #include "extractor/maneuver_override.hpp" #include "engine/api/base_api.hpp" +#include "engine/api/base_result.hpp" #include "engine/api/json_factory.hpp" #include "engine/api/route_parameters.hpp" @@ -109,7 +110,10 @@ class RouteAPI : public BaseAPI route.target_traversed_in_reverse)); } - response.values["waypoints"] = BaseAPI::MakeWaypoints(all_start_end_points); + if (!parameters.skip_waypoints) + { + response.values["waypoints"] = BaseAPI::MakeWaypoints(all_start_end_points); + } response.values["routes"] = std::move(jsRoutes); response.values["code"] = "Ok"; auto data_timestamp = facade.GetTimestamp(); @@ -140,7 +144,12 @@ class RouteAPI : public BaseAPI } auto routes_vector = fb_result.CreateVector(routes); - auto waypoints_vector = getWaypoints(); + flatbuffers::Offset>> + waypoints_vector; + if (!parameters.skip_waypoints) + { + waypoints_vector = getWaypoints(); + } fbresult::FBResultBuilder response(fb_result); response.add_routes(routes_vector); diff --git a/include/engine/api/table_api.hpp b/include/engine/api/table_api.hpp index 7b6cd1c48e1..4fe21b66583 100644 --- a/include/engine/api/table_api.hpp +++ b/include/engine/api/table_api.hpp @@ -2,6 +2,7 @@ #define ENGINE_API_TABLE_HPP #include "engine/api/base_api.hpp" +#include "engine/api/base_result.hpp" #include "engine/api/json_factory.hpp" #include "engine/api/table_parameters.hpp" @@ -83,24 +84,36 @@ class TableAPI final : public BaseAPI flatbuffers::Offset>> sources; if (parameters.sources.empty()) { - sources = MakeWaypoints(fb_result, phantoms); + if (!parameters.skip_waypoints) + { + sources = MakeWaypoints(fb_result, phantoms); + } number_of_sources = phantoms.size(); } else { - sources = MakeWaypoints(fb_result, phantoms, parameters.sources); + if (!parameters.skip_waypoints) + { + sources = MakeWaypoints(fb_result, phantoms, parameters.sources); + } } flatbuffers::Offset>> destinations; if (parameters.destinations.empty()) { - destinations = MakeWaypoints(fb_result, phantoms); + if (!parameters.skip_waypoints) + { + destinations = MakeWaypoints(fb_result, phantoms); + } number_of_destinations = phantoms.size(); } else { - destinations = MakeWaypoints(fb_result, phantoms, parameters.destinations); + if (!parameters.skip_waypoints) + { + destinations = MakeWaypoints(fb_result, phantoms, parameters.destinations); + } } boost::optional>> durations = boost::none; @@ -162,22 +175,34 @@ class TableAPI final : public BaseAPI // symmetric case if (parameters.sources.empty()) { - response.values["sources"] = MakeWaypoints(phantoms); + if (!parameters.skip_waypoints) + { + response.values["sources"] = MakeWaypoints(phantoms); + } number_of_sources = phantoms.size(); } else { - response.values["sources"] = MakeWaypoints(phantoms, parameters.sources); + if (!parameters.skip_waypoints) + { + response.values["sources"] = MakeWaypoints(phantoms, parameters.sources); + } } if (parameters.destinations.empty()) { - response.values["destinations"] = MakeWaypoints(phantoms); + if (!parameters.skip_waypoints) + { + response.values["destinations"] = MakeWaypoints(phantoms); + } number_of_destinations = phantoms.size(); } else { - response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations); + if (!parameters.skip_waypoints) + { + response.values["destinations"] = MakeWaypoints(phantoms, parameters.destinations); + } } if (parameters.annotations & TableParameters::AnnotationsType::Duration) diff --git a/include/engine/api/trip_api.hpp b/include/engine/api/trip_api.hpp index 518d0ec7d6f..a38275f543c 100644 --- a/include/engine/api/trip_api.hpp +++ b/include/engine/api/trip_api.hpp @@ -81,7 +81,10 @@ class TripAPI final : public RouteAPI sub_routes[index].target_traversed_in_reverse); routes.values.push_back(std::move(route)); } - response.values["waypoints"] = MakeWaypoints(sub_trips, phantoms); + if (!parameters.skip_waypoints) + { + response.values["waypoints"] = MakeWaypoints(sub_trips, phantoms); + } response.values["trips"] = std::move(routes); response.values["code"] = "Ok"; } diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp index dc196ae2b0a..31bc4a71752 100644 --- a/include/server/api/base_parameters_grammar.hpp +++ b/include/server/api/base_parameters_grammar.hpp @@ -152,6 +152,10 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar qi::lit("generate_hints=") > qi::bool_[ph::bind(&engine::api::BaseParameters::generate_hints, qi::_r1) = qi::_1]; + skip_waypoints_rule = + qi::lit("skip_waypoints=") > + qi::bool_[ph::bind(&engine::api::BaseParameters::skip_waypoints, qi::_r1) = qi::_1]; + bearings_rule = qi::lit("bearings=") > (-(qi::short_ > ',' > qi::short_))[ph::bind(add_bearing, qi::_r1, qi::_1)] % ';'; @@ -183,6 +187,7 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar | hints_rule(qi::_r1) // | bearings_rule(qi::_r1) // | generate_hints_rule(qi::_r1) // + | skip_waypoints_rule(qi::_r1) // | approach_rule(qi::_r1) // | exclude_rule(qi::_r1) // | snapping_rule(qi::_r1); @@ -203,6 +208,7 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar qi::rule hints_rule; qi::rule generate_hints_rule; + qi::rule skip_waypoints_rule; qi::rule approach_rule; qi::rule exclude_rule; diff --git a/unit_tests/library/match.cpp b/unit_tests/library/match.cpp index c7c3b1eae7f..cce5138cd7e 100644 --- a/unit_tests/library/match.cpp +++ b/unit_tests/library/match.cpp @@ -65,6 +65,30 @@ BOOST_AUTO_TEST_CASE(test_match) } } +BOOST_AUTO_TEST_CASE(test_match_skip_waypoints) +{ + using namespace osrm; + + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + + MatchParameters params; + params.skip_waypoints = true; + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + + engine::api::ResultT result = json::Object(); + + const auto rc = osrm.Match(params, result); + + auto &json_result = result.get(); + BOOST_CHECK(rc == Status::Ok || rc == Status::Error); + const auto code = json_result.values.at("code").get().value; + BOOST_CHECK_EQUAL(code, "Ok"); + + BOOST_CHECK(json_result.values.find("tracepoints") == json_result.values.end()); +} + BOOST_AUTO_TEST_CASE(test_match_split) { using namespace osrm; @@ -118,4 +142,70 @@ BOOST_AUTO_TEST_CASE(test_match_split) } } +BOOST_AUTO_TEST_CASE(test_match_fb_serialization) +{ + using namespace osrm; + + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + + MatchParameters params; + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + + engine::api::ResultT result = flatbuffers::FlatBufferBuilder(); + + const auto rc = osrm.Match(params, result); + BOOST_CHECK(rc == Status::Ok); + + auto &fb_result = result.get(); + auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); + + BOOST_CHECK(!fb->error()); + + BOOST_CHECK(fb->waypoints() != nullptr); + const auto waypoints = fb->waypoints(); + BOOST_CHECK(waypoints->size() == params.coordinates.size()); + + BOOST_CHECK(fb->routes() != nullptr); + const auto matchings = fb->routes(); + const auto &number_of_matchings = matchings->size(); + + for (const auto &waypoint : *waypoints) + { + BOOST_CHECK(waypoint_check(waypoint)); + const auto matchings_index = waypoint->matchings_index(); + const auto waypoint_index = waypoint->waypoint_index(); + const auto &route_legs = matchings->operator[](matchings_index)->legs(); + + BOOST_CHECK_LT(waypoint_index, route_legs->size() + 1); + BOOST_CHECK_LT(matchings_index, number_of_matchings); + } +} + +BOOST_AUTO_TEST_CASE(test_match_fb_serialization_skip_waypoints) +{ + using namespace osrm; + + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + + MatchParameters params; + params.skip_waypoints = true; + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + + engine::api::ResultT result = flatbuffers::FlatBufferBuilder(); + + const auto rc = osrm.Match(params, result); + BOOST_CHECK(rc == Status::Ok); + + auto &fb_result = result.get(); + auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); + + BOOST_CHECK(!fb->error()); + + BOOST_CHECK(fb->waypoints() == nullptr); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/library/nearest.cpp b/unit_tests/library/nearest.cpp index 9119c104283..5f4568dc88d 100644 --- a/unit_tests/library/nearest.cpp +++ b/unit_tests/library/nearest.cpp @@ -43,6 +43,27 @@ BOOST_AUTO_TEST_CASE(test_nearest_response) } } +BOOST_AUTO_TEST_CASE(test_nearest_response_skip_waypoints) +{ + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + + using namespace osrm; + + NearestParameters params; + params.skip_waypoints = true; + params.coordinates.push_back(get_dummy_location()); + + engine::api::ResultT result = json::Object(); + const auto rc = osrm.Nearest(params, result); + BOOST_REQUIRE(rc == Status::Ok); + + auto &json_result = result.get(); + const auto code = json_result.values.at("code").get().value; + BOOST_CHECK_EQUAL(code, "Ok"); + + BOOST_CHECK(json_result.values.find("waypoints") == json_result.values.end()); +} + BOOST_AUTO_TEST_CASE(test_nearest_response_no_coordinates) { auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); @@ -118,7 +139,7 @@ BOOST_AUTO_TEST_CASE(test_nearest_response_for_location_in_small_component) } } -BOOST_AUTO_TEST_CASE(test_nearest_fb_serilization) +BOOST_AUTO_TEST_CASE(test_nearest_fb_serialization) { auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); @@ -147,6 +168,27 @@ BOOST_AUTO_TEST_CASE(test_nearest_fb_serilization) } } +BOOST_AUTO_TEST_CASE(test_nearest_fb_serialization_skip_waypoints) +{ + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + + using namespace osrm; + + NearestParameters params; + params.skip_waypoints = true; + params.coordinates.push_back(get_dummy_location()); + + engine::api::ResultT result = flatbuffers::FlatBufferBuilder(); + const auto rc = osrm.Nearest(params, result); + BOOST_REQUIRE(rc == Status::Ok); + + auto &fb_result = result.get(); + auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); + BOOST_CHECK(!fb->error()); + + BOOST_CHECK(fb->waypoints() == nullptr); +} + BOOST_AUTO_TEST_CASE(test_nearest_fb_error) { auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); diff --git a/unit_tests/library/route.cpp b/unit_tests/library/route.cpp index b00c782be04..a2984530bae 100644 --- a/unit_tests/library/route.cpp +++ b/unit_tests/library/route.cpp @@ -270,6 +270,53 @@ BOOST_AUTO_TEST_CASE(test_route_same_coordinates) } } +BOOST_AUTO_TEST_CASE(test_route_same_coordinates_no_waypoints) +{ + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + + using namespace osrm; + + RouteParameters params; + params.skip_waypoints = true; + params.steps = true; + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + + engine::api::ResultT result = json::Object(); + const auto rc = osrm.Route(params, result); + BOOST_CHECK(rc == Status::Ok); + + auto &json_result = result.get(); + const auto code = json_result.values.at("code").get().value; + BOOST_CHECK_EQUAL(code, "Ok"); + + BOOST_CHECK(json_result.values.find("waypoints") == json_result.values.end()); + + const auto &routes = json_result.values.at("routes").get().values; + BOOST_REQUIRE_GT(routes.size(), 0); + + for (const auto &route : routes) + { + const auto &route_object = route.get(); + + const auto distance = route_object.values.at("distance").get().value; + BOOST_CHECK_EQUAL(distance, 0); + + const auto duration = route_object.values.at("duration").get().value; + BOOST_CHECK_EQUAL(duration, 0); + + // geometries=polyline by default + const auto geometry = route_object.values.at("geometry").get().value; + BOOST_CHECK(!geometry.empty()); + + const auto &legs = route_object.values.at("legs").get().values; + BOOST_CHECK(!legs.empty()); + + // The rest of legs contents is verified by test_route_same_coordinates + } +} + BOOST_AUTO_TEST_CASE(test_route_response_for_locations_in_small_component) { auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); @@ -575,4 +622,43 @@ BOOST_AUTO_TEST_CASE(test_route_serialize_fb) } } +BOOST_AUTO_TEST_CASE(test_route_serialize_fb_skip_waypoints) +{ + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + + using namespace osrm; + + RouteParameters params; + params.skip_waypoints = true; + params.steps = true; + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + + engine::api::ResultT result = flatbuffers::FlatBufferBuilder(); + const auto rc = osrm.Route(params, result); + BOOST_CHECK(rc == Status::Ok); + + auto &fb_result = result.get(); + auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); + BOOST_CHECK(!fb->error()); + + BOOST_CHECK(fb->waypoints() == nullptr); + + BOOST_CHECK(fb->routes() != nullptr); + const auto routes = fb->routes(); + BOOST_REQUIRE_GT(routes->size(), 0); + + for (const auto &route : *routes) + { + BOOST_CHECK_EQUAL(route->distance(), 0); + BOOST_CHECK_EQUAL(route->duration(), 0); + + const auto &legs = route->legs(); + BOOST_CHECK(legs->size() > 0); + + // Rest of the content is verified by test_route_serialize_fb + } +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/library/table.cpp b/unit_tests/library/table.cpp index 11186455845..33d57b0fb52 100644 --- a/unit_tests/library/table.cpp +++ b/unit_tests/library/table.cpp @@ -77,6 +77,57 @@ BOOST_AUTO_TEST_CASE(test_table_three_coords_one_source_one_dest_matrix) } } +BOOST_AUTO_TEST_CASE(test_table_three_coords_one_source_one_dest_matrix_no_waypoints) +{ + using namespace osrm; + + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + + TableParameters params; + params.skip_waypoints = true; + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + params.sources.push_back(0); + params.destinations.push_back(2); + params.annotations = TableParameters::AnnotationsType::All; + + engine::api::ResultT result = json::Object(); + + const auto rc = osrm.Table(params, result); + + auto &json_result = result.get(); + BOOST_CHECK(rc == Status::Ok || rc == Status::Error); + const auto code = json_result.values.at("code").get().value; + BOOST_CHECK_EQUAL(code, "Ok"); + + // check that returned durations error is expected size and proportions + // this test expects a 1x1 matrix + const auto &durations_array = json_result.values.at("durations").get().values; + BOOST_CHECK_EQUAL(durations_array.size(), params.sources.size()); + for (unsigned int i = 0; i < durations_array.size(); i++) + { + const auto durations_matrix = durations_array[i].get().values; + BOOST_CHECK_EQUAL(durations_matrix.size(), + params.sources.size() * params.destinations.size()); + } + + // check that returned distances error is expected size and proportions + // this test expects a 1x1 matrix + const auto &distances_array = json_result.values.at("distances").get().values; + BOOST_CHECK_EQUAL(distances_array.size(), params.sources.size()); + for (unsigned int i = 0; i < distances_array.size(); i++) + { + const auto distances_matrix = distances_array[i].get().values; + BOOST_CHECK_EQUAL(distances_matrix.size(), + params.sources.size() * params.destinations.size()); + } + + // waypoint arrays should be missing + BOOST_CHECK(json_result.values.find("destinations") == json_result.values.end()); + BOOST_CHECK(json_result.values.find("sources") == json_result.values.end()); +} + BOOST_AUTO_TEST_CASE(test_table_three_coords_one_source_matrix) { using namespace osrm; @@ -247,4 +298,46 @@ BOOST_AUTO_TEST_CASE(test_table_serialiaze_fb) } } +BOOST_AUTO_TEST_CASE(test_table_serialiaze_fb_no_waypoints) +{ + using namespace osrm; + + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + + TableParameters params; + params.skip_waypoints = true; + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + params.coordinates.push_back(get_dummy_location()); + params.sources.push_back(0); + params.destinations.push_back(2); + params.annotations = TableParameters::AnnotationsType::All; + + engine::api::ResultT result = flatbuffers::FlatBufferBuilder(); + + const auto rc = osrm.Table(params, result); + + BOOST_CHECK(rc == Status::Ok || rc == Status::Error); + + auto &fb_result = result.get(); + auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); + BOOST_CHECK(!fb->error()); + BOOST_CHECK(fb->table() != nullptr); + + // check that returned durations error is expected size and proportions + // this test expects a 1x1 matrix + BOOST_CHECK(fb->table()->durations() != nullptr); + auto durations_array = fb->table()->durations(); + BOOST_CHECK_EQUAL(durations_array->size(), params.sources.size() * params.destinations.size()); + + // check that returned distances error is expected size and proportions + // this test expects a 1x1 matrix + BOOST_CHECK(fb->table()->distances() != nullptr); + auto distances_array = fb->table()->distances(); + BOOST_CHECK_EQUAL(distances_array->size(), params.sources.size() * params.destinations.size()); + + BOOST_CHECK(fb->table()->destinations() == nullptr); + BOOST_CHECK(fb->waypoints() == nullptr); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/library/trip.cpp b/unit_tests/library/trip.cpp index bfe1108c508..e41e14aee6c 100644 --- a/unit_tests/library/trip.cpp +++ b/unit_tests/library/trip.cpp @@ -5,6 +5,7 @@ #include "fixture.hpp" #include "osrm/trip_parameters.hpp" +#include #include "osrm/coordinate.hpp" #include "osrm/engine_config.hpp" @@ -57,6 +58,30 @@ BOOST_AUTO_TEST_CASE(test_roundtrip_response_for_locations_in_small_component) } } +BOOST_AUTO_TEST_CASE(test_roundtrip_response_for_locations_in_small_component_skip_waypoints) +{ + using namespace osrm; + + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + const auto locations = get_locations_in_small_component(); + + TripParameters params; + params.skip_waypoints = true; + params.coordinates.push_back(locations.at(0)); + params.coordinates.push_back(locations.at(1)); + params.coordinates.push_back(locations.at(2)); + + engine::api::ResultT result = json::Object(); + const auto rc = osrm.Trip(params, result); + BOOST_CHECK(rc == Status::Ok); + + auto &json_result = result.get(); + const auto code = json_result.values.at("code").get().value; + BOOST_CHECK_EQUAL(code, "Ok"); + + BOOST_CHECK(json_result.values.find("waypoints") == json_result.values.end()); +} + BOOST_AUTO_TEST_CASE(test_roundtrip_response_for_locations_in_big_component) { using namespace osrm; @@ -422,4 +447,72 @@ BOOST_AUTO_TEST_CASE(test_tfse_legal_parameters) CheckOk(osrm, params); } +BOOST_AUTO_TEST_CASE(test_roundtrip_response_fb_serialization) +{ + using namespace osrm; + + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + const auto locations = get_locations_in_small_component(); + + TripParameters params; + params.coordinates.push_back(locations.at(0)); + params.coordinates.push_back(locations.at(1)); + params.coordinates.push_back(locations.at(2)); + + engine::api::ResultT result = flatbuffers::FlatBufferBuilder(); + const auto rc = osrm.Trip(params, result); + BOOST_CHECK(rc == Status::Ok); + + auto &fb_result = result.get(); + auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); + + BOOST_CHECK(!fb->error()); + + BOOST_CHECK(fb->waypoints() != nullptr); + const auto waypoints = fb->waypoints(); + BOOST_CHECK(waypoints->size() == params.coordinates.size()); + + BOOST_CHECK(fb->routes() != nullptr); + const auto trips = fb->routes(); + BOOST_CHECK_EQUAL(trips->size(), 1); + + for (const auto &waypoint : *waypoints) + { + const auto longitude = waypoint->location()->longitude(); + const auto latitude = waypoint->location()->latitude(); + BOOST_CHECK(longitude >= -180. && longitude <= 180.); + BOOST_CHECK(latitude >= -90. && latitude <= 90.); + + const auto trip = waypoint->trips_index(); + const auto pos = waypoint->waypoint_index(); + BOOST_CHECK(trip < trips->size()); + BOOST_CHECK(pos < waypoints->size()); + } +} + +BOOST_AUTO_TEST_CASE(test_roundtrip_response_fb_serialization_skip_waypoints) +{ + using namespace osrm; + + auto osrm = getOSRM(OSRM_TEST_DATA_DIR "/ch/monaco.osrm"); + const auto locations = get_locations_in_small_component(); + + TripParameters params; + params.skip_waypoints = true; + params.coordinates.push_back(locations.at(0)); + params.coordinates.push_back(locations.at(1)); + params.coordinates.push_back(locations.at(2)); + + engine::api::ResultT result = flatbuffers::FlatBufferBuilder(); + const auto rc = osrm.Trip(params, result); + BOOST_CHECK(rc == Status::Ok); + + auto &fb_result = result.get(); + auto fb = engine::api::fbresult::GetFBResult(fb_result.GetBufferPointer()); + + BOOST_CHECK(!fb->error()); + + BOOST_CHECK(fb->waypoints() == nullptr); +} + BOOST_AUTO_TEST_SUITE_END()