diff --git a/features/car/startpoint.feature b/features/car/startpoint.feature index 965f57d43..e1bd95419 100644 --- a/features/car/startpoint.feature +++ b/features/car/startpoint.feature @@ -35,3 +35,23 @@ Feature: Car - Allowed start/end modes | from | to | route | modes | | 1 | 2 | ab,ab | driving,driving | | 2 | 1 | ab,ab | driving,driving | + + Scenario: Car - URL override of non-startpoints + Given the node map + """ + a 1 b c 2 d + """ + + Given the query options + | snapping | any | + + And the ways + | nodes | highway | access | + | ab | service | private | + | bc | primary | | + | cd | service | private | + + When I route I should get + | from | to | route | + | 1 | 2 | ab,bc,cd | + | 2 | 1 | cd,bc,ab | diff --git a/include/engine/api/route_parameters.hpp b/include/engine/api/route_parameters.hpp index ba200050d..41f63b833 100644 --- a/include/engine/api/route_parameters.hpp +++ b/include/engine/api/route_parameters.hpp @@ -78,6 +78,11 @@ struct RouteParameters : public BaseParameters Speed = 0x20, All = Duration | Nodes | Distance | Weight | Datasources | Speed }; + enum class SnappingType + { + Default, + Any + }; RouteParameters() = default; @@ -147,12 +152,13 @@ struct RouteParameters : public BaseParameters const OverviewType overview_, const boost::optional continue_straight_, std::vector waypoints_, + const SnappingType snapping_, const Args... args_) : BaseParameters{std::forward(args_)...}, steps{steps_}, alternatives{alternatives_}, number_of_alternatives{alternatives_ ? 1u : 0u}, annotations{annotations_}, annotations_type{annotations_ ? AnnotationsType::All : AnnotationsType::None}, geometries{geometries_}, overview{overview_}, continue_straight{continue_straight_}, - waypoints{waypoints_} + waypoints{waypoints_}, snapping{snapping_} { } @@ -165,12 +171,13 @@ struct RouteParameters : public BaseParameters const OverviewType overview_, const boost::optional continue_straight_, std::vector waypoints_, + const SnappingType snapping_, Args... args_) : BaseParameters{std::forward(args_)...}, steps{steps_}, alternatives{alternatives_}, number_of_alternatives{alternatives_ ? 1u : 0u}, annotations{annotations_ == AnnotationsType::None ? false : true}, annotations_type{annotations_}, geometries{geometries_}, overview{overview_}, - continue_straight{continue_straight_}, waypoints{waypoints_} + continue_straight{continue_straight_}, waypoints{waypoints_}, snapping{snapping_} { } @@ -184,6 +191,7 @@ struct RouteParameters : public BaseParameters OverviewType overview = OverviewType::Simplified; boost::optional continue_straight; std::vector waypoints; + SnappingType snapping = SnappingType::Default; bool IsValid() const { diff --git a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp index 899fb86d6..63d4dbc3a 100644 --- a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp +++ b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp @@ -389,23 +389,25 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade std::pair NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const Approach approach) const override final + const Approach approach, + const bool use_all_edges) const override final { BOOST_ASSERT(m_geospatial_query.get()); return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( - input_coordinate, approach); + input_coordinate, approach, use_all_edges); } std::pair NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, const double max_distance, - const Approach approach) const override final + const Approach approach, + const bool use_all_edges) const override final { BOOST_ASSERT(m_geospatial_query.get()); return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( - input_coordinate, max_distance, approach); + input_coordinate, max_distance, approach, use_all_edges); } std::pair @@ -413,24 +415,26 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade const double max_distance, const int bearing, const int bearing_range, - const Approach approach) const override final + const Approach approach, + const bool use_all_edges) const override final { BOOST_ASSERT(m_geospatial_query.get()); return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( - input_coordinate, max_distance, bearing, bearing_range, approach); + input_coordinate, max_distance, bearing, bearing_range, approach, use_all_edges); } std::pair NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, const int bearing, const int bearing_range, - const Approach approach) const override final + const Approach approach, + const bool use_all_edges) const override final { BOOST_ASSERT(m_geospatial_query.get()); return m_geospatial_query->NearestPhantomNodeWithAlternativeFromBigComponent( - input_coordinate, bearing, bearing_range, approach); + input_coordinate, bearing, bearing_range, approach, use_all_edges); } std::uint32_t GetCheckSum() const override final { return m_check_sum; } diff --git a/include/engine/datafacade/datafacade_base.hpp b/include/engine/datafacade/datafacade_base.hpp index 157dbb1ca..77dab643f 100644 --- a/include/engine/datafacade/datafacade_base.hpp +++ b/include/engine/datafacade/datafacade_base.hpp @@ -161,22 +161,26 @@ class BaseDataFacade virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const Approach approach) const = 0; + const Approach approach, + const bool use_all_edges) const = 0; virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, const double max_distance, - const Approach approach) const = 0; + const Approach approach, + const bool use_all_edges) const = 0; virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, const double max_distance, const int bearing, const int bearing_range, - const Approach approach) const = 0; + const Approach approach, + const bool use_all_edges) const = 0; virtual std::pair NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, const int bearing, const int bearing_range, - const Approach approach) const = 0; + const Approach approach, + const bool use_all_edges = false) const = 0; virtual bool HasLaneData(const EdgeID id) const = 0; virtual util::guidance::LaneTupleIdPair GetLaneData(const EdgeID id) const = 0; diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp index dd52d8dc7..bac91a566 100644 --- a/include/engine/geospatial_query.hpp +++ b/include/engine/geospatial_query.hpp @@ -205,18 +205,19 @@ template class GeospatialQuery std::pair NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, const double max_distance, - const Approach approach) const + const Approach approach, + const bool use_all_edges) const { bool has_small_component = false; bool has_big_component = false; auto results = rtree.Nearest( input_coordinate, - [this, approach, &input_coordinate, &has_big_component, &has_small_component]( + [this, approach, &input_coordinate, &has_big_component, &has_small_component, &use_all_edges]( const CandidateSegment &segment) { auto use_segment = (!has_small_component || (!has_big_component && !IsTinyComponent(segment))); auto use_directions = std::make_pair(use_segment, use_segment); - const auto valid_edges = HasValidEdge(segment); + const auto valid_edges = HasValidEdge(segment, use_all_edges); const auto admissible_segments = CheckSegmentExclude(segment); use_directions = boolPairAnd(use_directions, admissible_segments); use_directions = boolPairAnd(use_directions, valid_edges); @@ -251,19 +252,20 @@ template class GeospatialQuery // a second phantom node is return that is the nearest coordinate in a big component. std::pair NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, - const Approach approach) const + const Approach approach, + const bool use_all_edges) const { bool has_small_component = false; bool has_big_component = false; auto results = rtree.Nearest( input_coordinate, - [this, approach, &input_coordinate, &has_big_component, &has_small_component]( + [this, approach, &input_coordinate, &has_big_component, &has_small_component, &use_all_edges]( const CandidateSegment &segment) { auto use_segment = (!has_small_component || (!has_big_component && !IsTinyComponent(segment))); auto use_directions = std::make_pair(use_segment, use_segment); - const auto valid_edges = HasValidEdge(segment); + const auto valid_edges = HasValidEdge(segment, use_all_edges); const auto admissible_segments = CheckSegmentExclude(segment); use_directions = boolPairAnd(use_directions, admissible_segments); use_directions = boolPairAnd(use_directions, valid_edges); @@ -298,7 +300,8 @@ template class GeospatialQuery NearestPhantomNodeWithAlternativeFromBigComponent(const util::Coordinate input_coordinate, const int bearing, const int bearing_range, - const Approach approach) const + const Approach approach, + const bool use_all_edges) const { bool has_small_component = false; bool has_big_component = false; @@ -310,12 +313,13 @@ template class GeospatialQuery bearing, bearing_range, &has_big_component, - &has_small_component](const CandidateSegment &segment) { + &has_small_component, + &use_all_edges](const CandidateSegment &segment) { auto use_segment = (!has_small_component || (!has_big_component && !IsTinyComponent(segment))); auto use_directions = std::make_pair(use_segment, use_segment); const auto admissible_segments = CheckSegmentExclude(segment); - use_directions = boolPairAnd(use_directions, HasValidEdge(segment)); + use_directions = boolPairAnd(use_directions, HasValidEdge(segment, use_all_edges)); if (use_segment) { @@ -356,7 +360,8 @@ template class GeospatialQuery const double max_distance, const int bearing, const int bearing_range, - const Approach approach) const + const Approach approach, + const bool use_all_edges) const { bool has_small_component = false; bool has_big_component = false; @@ -368,12 +373,13 @@ template class GeospatialQuery bearing, bearing_range, &has_big_component, - &has_small_component](const CandidateSegment &segment) { + &has_small_component, + &use_all_edges](const CandidateSegment &segment) { auto use_segment = (!has_small_component || (!has_big_component && !IsTinyComponent(segment))); auto use_directions = std::make_pair(use_segment, use_segment); const auto admissible_segments = CheckSegmentExclude(segment); - use_directions = boolPairAnd(use_directions, HasValidEdge(segment)); + use_directions = boolPairAnd(use_directions, HasValidEdge(segment, use_all_edges)); if (use_segment) { diff --git a/include/engine/plugins/plugin_base.hpp b/include/engine/plugins/plugin_base.hpp index 7412627de..f8b2560cc 100644 --- a/include/engine/plugins/plugin_base.hpp +++ b/include/engine/plugins/plugin_base.hpp @@ -262,7 +262,8 @@ class BasePlugin } std::vector GetPhantomNodes(const datafacade::BaseDataFacade &facade, - const api::BaseParameters ¶meters) const + const api::BaseParameters ¶meters, + const bool use_all_edges = false) const { std::vector phantom_node_pairs(parameters.coordinates.size()); @@ -296,7 +297,8 @@ class BasePlugin *parameters.radiuses[i], parameters.bearings[i]->bearing, parameters.bearings[i]->range, - approach); + approach, + use_all_edges); } else { @@ -305,7 +307,8 @@ class BasePlugin parameters.coordinates[i], parameters.bearings[i]->bearing, parameters.bearings[i]->range, - approach); + approach, + use_all_edges); } } else @@ -314,13 +317,16 @@ class BasePlugin { phantom_node_pairs[i] = facade.NearestPhantomNodeWithAlternativeFromBigComponent( - parameters.coordinates[i], *parameters.radiuses[i], approach); + parameters.coordinates[i], + *parameters.radiuses[i], + approach, + use_all_edges); } else { phantom_node_pairs[i] = facade.NearestPhantomNodeWithAlternativeFromBigComponent( - parameters.coordinates[i], approach); + parameters.coordinates[i], approach, use_all_edges); } } diff --git a/include/nodejs/node_osrm_support.hpp b/include/nodejs/node_osrm_support.hpp index a76b5ccf5..d99c43e92 100644 --- a/include/nodejs/node_osrm_support.hpp +++ b/include/nodejs/node_osrm_support.hpp @@ -1010,6 +1010,36 @@ argumentsToRouteParameter(const Nan::FunctionCallbackInfo &args, } } + if (obj->Has(Nan::New("snapping").ToLocalChecked())) + { + v8::Local snapping = obj->Get(Nan::New("snapping").ToLocalChecked()); + if (snapping.IsEmpty()) + return route_parameters_ptr(); + + if (!snapping->IsString()) + { + Nan::ThrowError("Snapping must be a string: [default, any]"); + return route_parameters_ptr(); + } + const Nan::Utf8String snapping_utf8str(snapping); + std::string snapping_str{*snapping_utf8str, + *snapping_utf8str + snapping_utf8str.length()}; + + if (snapping_str == "default") + { + params->snapping = osrm::RouteParameters::SnappingType::Default; + } + else if (snapping_str == "any") + { + params->snapping = osrm::RouteParameters::SnappingType::Any; + } + else + { + Nan::ThrowError("'snapping' param must be one of [default, any]"); + return route_parameters_ptr(); + } + } + bool parsedSuccessfully = parseCommonParameters(obj, params); if (!parsedSuccessfully) { diff --git a/include/server/api/route_parameters_grammar.hpp b/include/server/api/route_parameters_grammar.hpp index 21463f6ee..dd26a5884 100644 --- a/include/server/api/route_parameters_grammar.hpp +++ b/include/server/api/route_parameters_grammar.hpp @@ -82,6 +82,9 @@ struct RouteParametersGrammar : public BaseParametersGrammar geometries_type; qi::symbols overview_type; qi::symbols annotations_type; + qi::symbols snapping_type; }; } } diff --git a/src/engine/plugins/viaroute.cpp b/src/engine/plugins/viaroute.cpp index a4df2de86..992a62c0d 100644 --- a/src/engine/plugins/viaroute.cpp +++ b/src/engine/plugins/viaroute.cpp @@ -87,7 +87,7 @@ Status ViaRoutePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithm return Status::Error; const auto &facade = algorithms.GetFacade(); - auto phantom_node_pairs = GetPhantomNodes(facade, route_parameters); + auto phantom_node_pairs = GetPhantomNodes(facade, route_parameters, (route_parameters.snapping == api::RouteParameters::SnappingType::Any)); if (phantom_node_pairs.size() != route_parameters.coordinates.size()) { return Error("NoSegment", diff --git a/test/nodejs/route.js b/test/nodejs/route.js index 037a7be10..f23eaf3ee 100644 --- a/test/nodejs/route.js +++ b/test/nodejs/route.js @@ -689,4 +689,25 @@ test('route: throws on invalid waypoints values, waypoints must be an array of i }; assert.throws(function () { osrm.route(options, function (err, response) { console.error(`response: ${response}`); console.error(`error: ${err}`); }); }, /Waypoints must be supplied in increasing order/); +}); + +test('route: throws on invalid snapping values', function (assert) { + assert.plan(1); + var osrm = new OSRM(monaco_path); + var options = { + steps: true, + coordinates: three_test_coordinates.concat(three_test_coordinates), + snapping: "zing" + }; + assert.throws(function () { osrm.route(options, function (err, response) { console.error(`response: ${response}`); console.error(`error: ${err}`); }); }, + /'snapping' param must be one of \[default, any\]/); +}); + +test('route: snapping parameter passed through OK', function(assert) { + assert.plan(2); + var osrm = new OSRM(monaco_path); + osrm.route({snapping: "any", coordinates: [[7.448205209414596,43.754001097311544],[7.447122039202185,43.75306156811368]]}, function(err, route) { + assert.ifError(err); + assert.equal(Math.round(route.routes[0].distance * 10), 1314); // Round it to nearest 0.1m to eliminate floating point comparison error + }); }); \ No newline at end of file