diff --git a/features/testbot/stoppage.feature b/features/testbot/stoppage.feature new file mode 100644 index 000000000..332085a23 --- /dev/null +++ b/features/testbot/stoppage.feature @@ -0,0 +1,168 @@ +@routing @maxspeed @testbot +Feature: Testbot - Acceleration profiles + + Background: Use specific speeds + Given the profile "testbot" + + Scenario: Testbot - No stoppage penalties + Given a grid size of 10 meters + Given the node map + """ + a 1 2 3 4 5 b + """ + + And the ways + | nodes | highway | maxspeed:forward | maxspeed:backward | + | ab | trunk | 60 | 45 | + + When I route I should get + | from | to | route | time | distance | + | a | b | ab,ab | 3.6s | 59.9m | + | a | 1 | ab,ab | 0.6s | 10m | + | a | 2 | ab,ab | 1.2s | 20m | + | a | 3 | ab,ab | 1.8s | 30m | + | a | 4 | ab,ab | 2.4s | 40m | + | a | 5 | ab,ab | 3s | 50m | + | 5 | b | ab,ab | 0.6s | 9.9m | + | 4 | b | ab,ab | 1.2s | 19.9m | + | 3 | b | ab,ab | 1.8s | 29.9m | + | 2 | b | ab,ab | 2.4s | 39.9m | + | 1 | b | ab,ab | 3s | 49.9m | + | 1 | 2 | ab,ab | 0.6s | 10m | + | 1 | 3 | ab,ab | 1.2s | 20m | + | 1 | 4 | ab,ab | 1.8s | 30m | + | 1 | 5 | ab,ab | 2.4s | 40m | + + When I request a travel time matrix I should get + | | a | 1 | 2 | 3 | 4 | 5 | b | + | a | 0 | 0.6 | 1.2 | 1.8 | 2.4 | 3 | 3.6 | + | 1 | 0.8 | 0 | 0.6 | 1.2 | 1.8 | 2.4 | 3 | + | 2 | 1.6 | 0.8 | 0 | 0.6 | 1.2 | 1.8 | 2.4 | + | 3 | 2.4 | 1.6 | 0.8 | 0 | 0.6 | 1.2 | 1.8 | + | 4 | 3.2 | 2.4 | 1.6 | 0.8 | 0 | 0.6 | 1.2 | + | 5 | 4 | 3.2 | 2.4 | 1.6 | 0.8 | 0 | 0.6 | + | b | 4.8 | 4 | 3.2 | 2.4 | 1.6 | 0.8 | 0 | + + Scenario: Testbot - No stoppage points, tiny grid size + Given a grid size of 1 meters + Given the node map + """ + a 1 2 3 4 5 6 7 8 9 b + """ + + And the ways + | nodes | highway | maxspeed:forward | maxspeed:backward | + | ab | trunk | 60 | 45 | + + When I request a travel time matrix I should get + | | a | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | b | + | a | 0 | 0 | 0.1 | 0.1 | 0.2 | 0.3 | 0.3 | 0.4 | 0.4 | 0.5 | 0.6 | + | 1 | 0 | 0 | 0.1 | 0.1 | 0.2 | 0.3 | 0.3 | 0.4 | 0.4 | 0.5 | 0.6 | + | 2 | 0.1 | 0.1 | 0 | 0 | 0.1 | 0.2 | 0.2 | 0.3 | 0.3 | 0.4 | 0.5 | + | 3 | 0.2 | 0.2 | 0.1 | 0 | 0.1 | 0.2 | 0.2 | 0.3 | 0.3 | 0.4 | 0.5 | + | 4 | 0.3 | 0.3 | 0.2 | 0.1 | 0 | 0.1 | 0.1 | 0.2 | 0.2 | 0.3 | 0.4 | + | 5 | 0.4 | 0.4 | 0.3 | 0.2 | 0.1 | 0 | 0 | 0.1 | 0.1 | 0.2 | 0.3 | + | 6 | 0.4 | 0.4 | 0.3 | 0.2 | 0.1 | 0 | 0 | 0.1 | 0.1 | 0.2 | 0.3 | + | 7 | 0.5 | 0.5 | 0.4 | 0.3 | 0.2 | 0.1 | 0.1 | 0 | 0 | 0.1 | 0.2 | + | 8 | 0.6 | 0.6 | 0.5 | 0.4 | 0.3 | 0.2 | 0.2 | 0.1 | 0 | 0.1 | 0.2 | + | 9 | 0.7 | 0.7 | 0.6 | 0.5 | 0.4 | 0.3 | 0.3 | 0.2 | 0.1 | 0 | 0.1 | + | b | 0.8 | 0.8 | 0.7 | 0.6 | 0.5 | 0.4 | 0.4 | 0.3 | 0.2 | 0.1 | 0 | + + Scenario: Testbot - With stoppage points, tiny grid size + Given a grid size of 1 meters + Given the node map + """ + a 1 2 3 4 5 6 7 8 9 b + """ + + And the ways + | nodes | highway | maxspeed:forward | maxspeed:backward | + | ab | trunk | 60 | 45 | + + And the query options + | acceleration_profile | car | + + When I request a travel time matrix I should get + | | a | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | b | + | a | 0 | 1.1 | 1.7 | 2.1 | 2.4 | 2.6 | 2.9 | 3.1 | 3.4 | 3.5 | 3.7 | + | 1 | 1.1 | 0 | 1.1 | 1.7 | 2 | 2.3 | 2.6 | 2.8 | 3.1 | 3.3 | 3.5 | + | 2 | 1.7 | 1.1 | 0 | 1.1 | 1.7 | 2 | 2.4 | 2.6 | 2.9 | 3.1 | 3.3 | + | 3 | 2 | 1.5 | 1.1 | 0 | 1.1 | 1.5 | 2 | 2.3 | 2.6 | 2.8 | 3 | + | 4 | 2.3 | 1.9 | 1.5 | 1.1 | 0 | 1.1 | 1.7 | 2 | 2.4 | 2.6 | 2.8 | + | 5 | 2.5 | 2.2 | 1.9 | 1.5 | 1.1 | 0 | 1.1 | 1.7 | 2.1 | 2.4 | 2.6 | + | 6 | 2.8 | 2.5 | 2.3 | 2 | 1.7 | 1.1 | 0 | 1.1 | 1.7 | 2 | 2.3 | + | 7 | 3 | 2.8 | 2.5 | 2.3 | 2 | 1.7 | 1.1 | 0 | 1.1 | 1.7 | 2 | + | 8 | 3.2 | 3 | 2.8 | 2.5 | 2.3 | 2 | 1.5 | 1.1 | 0 | 1.1 | 1.5 | + | 9 | 3.4 | 3.2 | 3 | 2.8 | 2.5 | 2.3 | 1.9 | 1.5 | 1.1 | 0 | 1.1 | + | b | 3.6 | 3.4 | 3.2 | 3 | 2.8 | 2.5 | 2.2 | 1.9 | 1.5 | 1.1 | 0 | + + + Scenario: Testbot - Use stoppage penalty at waypoints + Given a grid size of 10 meters + Given the node map + """ + a 1 2 3 4 5 b + """ + + And the ways + | nodes | highway | maxspeed:forward | maxspeed:backward | + | ab | trunk | 60 | 45 | + + And the query options + | acceleration_profile | car | + + When I request a travel time matrix I should get + | | a | 1 | 2 | 3 | 4 | 5 | b | + | a | 0 | 3.7 | 5.3 | 6.5 | 7.5 | 8.4 | 9.1 | + | 1 | 3.6 | 0 | 3.7 | 5.3 | 6.5 | 7.5 | 8.3 | + | 2 | 5.1 | 3.6 | 0 | 3.7 | 5.3 | 6.5 | 7.5 | + | 3 | 6.3 | 5.1 | 3.6 | 0 | 3.7 | 5.3 | 6.4 | + | 4 | 7.2 | 6.3 | 5.1 | 3.6 | 0 | 3.7 | 5.2 | + | 5 | 8.1 | 7.2 | 6.3 | 5.1 | 3.6 | 0 | 3.7 | + | b | 8.9 | 8.1 | 7.2 | 6.2 | 5.1 | 3.6 | 0 | + + + Scenario: Long distance grid with no penalty + Given a grid size of 1000 meters + Given the node map + """ + a 1 2 3 4 5 b + """ + + And the ways + | nodes | highway | maxspeed:forward | maxspeed:backward | + | ab | trunk | 60 | 45 | + + When I request a travel time matrix I should get + | | a | 1 | 2 | 3 | 4 | 5 | b | + | a | 0 | 59.9 | 119.9 | 179.9 | 239.9 | 299.9 | 359.9 | + | 1 | 79.9 | 0 | 60 | 120 | 180 | 240 | 300 | + | 2 | 159.9 | 80 | 0 | 60 | 120 | 180 | 240 | + | 3 | 239.9 | 160 | 80 | 0 | 60 | 120 | 180 | + | 4 | 319.9 | 240 | 160 | 80 | 0 | 60 | 120 | + | 5 | 399.9 | 320 | 240 | 160 | 80 | 0 | 60 | + | b | 479.9 | 400 | 320 | 240 | 160 | 80 | 0 | + + Scenario: Long distance grid + Given a grid size of 1000 meters + Given the node map + """ + a 1 2 3 4 5 b + """ + + And the ways + | nodes | highway | maxspeed:forward | maxspeed:backward | + | ab | trunk | 60 | 45 | + + And the query options + | acceleration_profile | car | + + When I request a travel time matrix I should get + | | a | 1 | 2 | 3 | 4 | 5 | b | + | a | 0 | 65.1 | 125.1 | 185.1 | 245.1 | 305.1 | 365.1 | + | 1 | 83.7 | 0 | 65.2 | 125.2 | 185.2 | 245.2 | 305.2 | + | 2 | 163.7 | 83.8 | 0 | 65.2 | 125.2 | 185.2 | 245.2 | + | 3 | 243.7 | 163.8 | 83.8 | 0 | 65.2 | 125.2 | 185.2 | + | 4 | 323.7 | 243.8 | 163.8 | 83.8 | 0 | 65.2 | 125.2 | + | 5 | 403.7 | 323.8 | 243.8 | 163.8 | 83.8 | 0 | 65.2 | + | b | 483.7 | 403.8 | 323.8 | 243.8 | 163.8 | 83.8 | 0 | \ No newline at end of file diff --git a/include/engine/api/base_parameters.hpp b/include/engine/api/base_parameters.hpp index 91ac937b4..c4bbe7edf 100644 --- a/include/engine/api/base_parameters.hpp +++ b/include/engine/api/base_parameters.hpp @@ -81,6 +81,8 @@ struct BaseParameters bool generate_hints = true; SnappingType snapping = SnappingType::Default; + // Whether or not to add acceleration/decelleration penalties at waypoints + double waypoint_acceleration_factor = 0.; BaseParameters(const std::vector coordinates_ = {}, const std::vector> hints_ = {}, @@ -89,10 +91,11 @@ struct BaseParameters std::vector> approaches_ = {}, bool generate_hints_ = true, std::vector exclude = {}, - const SnappingType snapping_ = SnappingType::Default) + const SnappingType snapping_ = SnappingType::Default, + bool waypoint_acceleration_factor_ = 0.) : coordinates(coordinates_), hints(hints_), radiuses(radiuses_), bearings(bearings_), approaches(approaches_), exclude(std::move(exclude)), generate_hints(generate_hints_), - snapping(snapping_) + snapping(snapping_), waypoint_acceleration_factor(waypoint_acceleration_factor_) { } diff --git a/include/nodejs/node_osrm_support.hpp b/include/nodejs/node_osrm_support.hpp index a4d291d15..867da139d 100644 --- a/include/nodejs/node_osrm_support.hpp +++ b/include/nodejs/node_osrm_support.hpp @@ -721,6 +721,48 @@ inline bool argumentsToParameter(const Nan::FunctionCallbackInfo &arg } } + if (obj->Has(Nan::New("acceleration_profile").ToLocalChecked())) + { + v8::Local acceleration_profile = + obj->Get(Nan::New("acceleration_profile").ToLocalChecked()); + if (acceleration_profile.IsEmpty()) + return false; + + if (!acceleration_profile->IsNumber() || !acceleration_profile->IsString()) + { + Nan::ThrowError("acceleration_profile must be a decimal number or one of 'car', 'fast_car', 'slow_car', 'truck', or 'tractor_trailer'"); + return false; + } + + if (acceleration_profile->IsString()) { + std::string ssaf = *v8::String::Utf8Value(acceleration_profile); + // If they say 'yes', they get the default + if (ssaf == "car") { + params->waypoint_acceleration_factor = ACCELERATION_ALPHA_CAR; + } else if (ssaf == "fast_car") { + params->waypoint_acceleration_factor = ACCELERATION_ALPHA_FAST_CAR; + } else if (ssaf == "slow_car") { + params->waypoint_acceleration_factor = ACCELERATION_ALPHA_SLOW_CAR; + } else if (ssaf == "truck") { + params->waypoint_acceleration_factor = ACCELERATION_ALPHA_TRUCK; + } else if (ssaf == "tractor_trailer") { + params->waypoint_acceleration_factor = ACCELERATION_ALPHA_TRACTOR_TRAILER; + } else { + Nan::ThrowError("acceleration_profile must be a decimal number or one of 'car', 'fast_car', 'slow_car', 'truck', or 'tractor_trailer'"); + return false; + } + return true; + } + + const auto value = acceleration_profile->NumberValue(); + if (value < 0) { + Nan::ThrowError("acceleration_profile cannot be negative"); + return false; + } + + params->waypoint_acceleration_factor = value; + } + return true; } diff --git a/include/server/api/base_parameters_grammar.hpp b/include/server/api/base_parameters_grammar.hpp index 5615ee53f..995451a34 100644 --- a/include/server/api/base_parameters_grammar.hpp +++ b/include/server/api/base_parameters_grammar.hpp @@ -135,6 +135,19 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar }, qi::_1)]; + acceleration_alpha_defaults_rule = + qi::lit("car")[qi::_val = ACCELERATION_ALPHA_CAR] | + qi::lit("fast_car")[qi::_val = ACCELERATION_ALPHA_FAST_CAR] | + qi::lit("slow_car")[qi::_val = ACCELERATION_ALPHA_SLOW_CAR] | + qi::lit("truck")[qi::_val = ACCELERATION_ALPHA_TRUCK] | + qi::lit("tractor_trailer")[qi::_val = ACCELERATION_ALPHA_TRACTOR_TRAILER]; + + acceleration_profile_rule = + qi::lit("acceleration_profile=") > + (qi::double_ | acceleration_alpha_defaults_rule) + [ph::bind(&engine::api::BaseParameters::waypoint_acceleration_factor, qi::_r1) = + qi::_1]; + query_rule = ((location_rule % ';') | polyline_rule | polyline6_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1]; @@ -179,7 +192,8 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar | generate_hints_rule(qi::_r1) // | approach_rule(qi::_r1) // | exclude_rule(qi::_r1) // - | snapping_rule(qi::_r1); + | snapping_rule(qi::_r1) // + | acceleration_profile_rule(qi::_r1);// } protected: @@ -196,6 +210,7 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar qi::rule generate_hints_rule; qi::rule approach_rule; qi::rule exclude_rule; + qi::rule acceleration_profile_rule; qi::rule bearing_rule; qi::rule location_rule; @@ -207,6 +222,8 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar qi::rule unlimited_rule; qi::rule snapping_rule; + qi::rule acceleration_alpha_defaults_rule; + qi::symbols approach_type; qi::symbols snapping_type; }; diff --git a/include/util/ieee754.hpp b/include/util/ieee754.hpp index 16f37e15d..3a6328dfd 100644 --- a/include/util/ieee754.hpp +++ b/include/util/ieee754.hpp @@ -488,8 +488,8 @@ inline void Prettify(char *buffer, int length, int k) inline void dtoa_milo(double value, char *buffer) { // Not handling NaN and inf - assert(!isnan(value)); - assert(!isinf(value)); + assert(!std::isnan(value)); + assert(!std::isinf(value)); if (value == 0) { diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp index d647a7db7..99ddeca60 100644 --- a/include/util/typedefs.hpp +++ b/include/util/typedefs.hpp @@ -118,6 +118,14 @@ static const TurnPenalty INVALID_TURN_PENALTY = std::numeric_limits static const EdgeDistance INVALID_EDGE_DISTANCE = std::numeric_limits::max(); static const EdgeDistance INVALID_FALLBACK_SPEED = std::numeric_limits::max(); +// Recommended value for passenger vehicles from +// https://fdotwww.blob.core.windows.net/sitefinity/docs/default-source/content/rail/publications/studies/safety/accelerationresearch.pdf?sfvrsn=716a4bb1_0 +static const double ACCELERATION_ALPHA_CAR = 6.0; +static const double ACCELERATION_ALPHA_FAST_CAR = 18; +static const double ACCELERATION_ALPHA_SLOW_CAR = 2; +static const double ACCELERATION_ALPHA_TRUCK = 1.5; +static const double ACCELERATION_ALPHA_TRACTOR_TRAILER = 0.5; + // FIXME the bitfields we use require a reduced maximal duration, this should be kept consistent // within the code base. For now we have to ensure that we don't case 30 bit to -1 and break any // min() / operator< checks due to the invalid truncation. In addition, using signed and unsigned diff --git a/src/engine/plugins/table.cpp b/src/engine/plugins/table.cpp index f78645c9d..14c5d07cf 100644 --- a/src/engine/plugins/table.cpp +++ b/src/engine/plugins/table.cpp @@ -86,8 +86,11 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, bool request_distance = params.annotations & api::TableParameters::AnnotationsType::Distance; bool request_duration = params.annotations & api::TableParameters::AnnotationsType::Duration; + // Because of the Short Trip ETA adjustments below, we need distances every time + const bool distances_required = request_distance || params.waypoint_acceleration_factor > 0.; + auto result_tables_pair = algorithms.ManyToManySearch( - snapped_phantoms, params.sources, params.destinations, request_distance); + snapped_phantoms, params.sources, params.destinations, distances_required); if ((request_duration && result_tables_pair.first.empty()) || (request_distance && result_tables_pair.second.empty())) @@ -97,8 +100,71 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, std::vector estimated_pairs; + // Adds some time to adjust for getting up to speed and slowing down to a stop + // Returns a new `duration` + auto adjust_for_startstop = [&](const double &acceleration_alpha, + const EdgeDuration &duration, + const EdgeDistance &distance) -> EdgeDuration { + + // Very short paths can end up with 0 duration. That'll lead to a divide + // by zero, so instead, we'll assume the travel speed is 10m/s (36km/h). + // Typically, the distance is also short, so we're quibbling at tiny numbers + // here, but tiny numbers is what this adjustment lambda is all about, + // so we do try to be reasonable. + const auto average_speed = + duration == 0 ? 10 : distance / + (duration / 10.); // duration is in deciseconds, we need m/sec + + // The following reference has a nice model (equations 9 through 12) + // for vehicle acceleration + // https://fdotwww.blob.core.windows.net/sitefinity/docs/default-source/content/rail/publications/studies/safety/accelerationresearch.pdf?sfvrsn=716a4bb1_0 + // We solve euqation 10 for time to figure out how long it'll take + // to get up to the desired speed + + // Because Equation 10 is asymptotic on v_des, we need to pick a target speed + // that's slighly less so the equation can actually get there. 1m/s less than + // target seems like a reasonable value to aim for + const auto target_speed = std::max(average_speed - 1, 0.1); + const auto initial_speed = 0.; + + // Equation 9 + const auto beta = acceleration_alpha / average_speed; + + // Equation 10 solved for time + const auto time_to_full_speed = std::log( (average_speed - initial_speed) / (average_speed - target_speed) ) / beta; + BOOST_ASSERT(time_to_full_speed >= 0); + + // Equation 11 + const auto distance_to_full_speed = + average_speed * time_to_full_speed - + average_speed * (1 - std::exp(-1 * beta * time_to_full_speed)) / beta; + + BOOST_ASSERT(distance_to_full_speed >= 0); + + if (distance_to_full_speed > distance / 2) + { + // Because equation 12 requires velocity at halfway, and + // solving equation 11 for t requires a Lambert W function, + // we'll approximate here by assuming constant acceleration + // over distance_to_full_speed using s = ut + 1/2at^2 + const auto average_acceleration = + 2 * distance_to_full_speed / (time_to_full_speed * time_to_full_speed); + const auto time_to_halfway = std::sqrt(distance / average_acceleration); + BOOST_ASSERT(time_to_halfway >= 0); + return (2 * time_to_halfway) * 10; // result is in deciseconds + } + else + { + const auto cruising_distance = distance - 2 * distance_to_full_speed; + const auto cruising_time = cruising_distance / average_speed; + BOOST_ASSERT(cruising_time >= 0); + return (cruising_time + 2 * time_to_full_speed) * 10; // result is in deciseconds + } + }; + // Scan table for null results - if any exist, replace with distance estimates - if (params.fallback_speed != INVALID_FALLBACK_SPEED || params.scale_factor != 1) + if (params.fallback_speed != INVALID_FALLBACK_SPEED || params.scale_factor != 1 || + params.waypoint_acceleration_factor != 0.) { for (std::size_t row = 0; row < num_sources; row++) { @@ -106,6 +172,16 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, { const auto &table_index = row * num_destinations + column; BOOST_ASSERT(table_index < result_tables_pair.first.size()); + // apply an accel/deceleration penalty to the duration + if (result_tables_pair.first[table_index] != MAXIMAL_EDGE_DURATION && + row != column && params.waypoint_acceleration_factor != 0.) + { + result_tables_pair.first[table_index] = + adjust_for_startstop(params.waypoint_acceleration_factor, + result_tables_pair.first[table_index], + result_tables_pair.second[table_index]); + } + // Estimate null results based on fallback_speed (if valid) and distance if (params.fallback_speed != INVALID_FALLBACK_SPEED && params.fallback_speed > 0 && result_tables_pair.first[table_index] == MAXIMAL_EDGE_DURATION) { @@ -132,6 +208,7 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, estimated_pairs.emplace_back(row, column); } + // Apply a scale factor to non-null result if requested if (params.scale_factor > 0 && params.scale_factor != 1 && result_tables_pair.first[table_index] != MAXIMAL_EDGE_DURATION && result_tables_pair.first[table_index] != 0) @@ -153,6 +230,14 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, } } + // If distances weren't requested, delete them from the result so they don't + // get rendered. + if (!request_distance) + { + std::vector empty; + result_tables_pair.second.swap(empty); + } + api::TableAPI table_api{facade, params}; table_api.MakeResponse(result_tables_pair, snapped_phantoms, estimated_pairs, result); diff --git a/src/engine/routing_algorithms/many_to_many_ch.cpp b/src/engine/routing_algorithms/many_to_many_ch.cpp index f9fa8e509..091dadddd 100644 --- a/src/engine/routing_algorithms/many_to_many_ch.cpp +++ b/src/engine/routing_algorithms/many_to_many_ch.cpp @@ -143,7 +143,8 @@ void forwardRoutingStep(const DataFacade &facade, middle_nodes_table[row_index * number_of_targets + column_index] = node; } } - else if (std::tie(new_weight, new_duration) < std::tie(current_weight, current_duration)) + else if (std::tie(new_weight, new_duration) < std::tie(current_weight, current_duration) && + new_distance >= 0) { current_weight = new_weight; current_duration = new_duration; diff --git a/test/nodejs/table.js b/test/nodejs/table.js index fc66b3c73..4439accdb 100644 --- a/test/nodejs/table.js +++ b/test/nodejs/table.js @@ -318,5 +318,46 @@ tables.forEach(function(annotation) { assert.throws(()=>osrm.table(options, (err, res) => {}), /scale_factor must be > 0/, "should throw on invalid scale_factor value"); }); + + test('table: ' + annotation + ' table in Monaco with start_stop_acceleration_factor values', function(assert) { + assert.plan(12); + var osrm = new OSRM({path: mld_data_path, algorithm: 'MLD'}); + var options = { + coordinates: two_test_coordinates, + annotations: [annotation.slice(0,-1)], + start_stop_acceleration_factor: [] + }; + + assert.throws(()=>osrm.table(options, (err, res) => {}), /start_stop_acceleration_factor must be a decimal number or one of/, "should throw on empty array"); + + options.start_stop_acceleration_factor = 'a'; + assert.throws(()=>osrm.table(options, (err, res) => {}), /start_stop_acceleration_factor must be a decimal number or one of/, "should throw on non-numeric value"); + + options.start_stop_acceleration_factor = [1]; + assert.throws(()=>osrm.table(options, (err, res) => {}), /start_stop_acceleration_factor must be a decimal number or one of/, "should throw on non-numeric value"); + + options.start_stop_acceleration_factor = -0.1; + assert.throws(()=>osrm.table(options, (err, res) => {}), /start_stop_acceleration_factor must be a decimal number or one of/, "should throw on non-numeric value"); + + options.start_stop_acceleration_factor = 0.; + assert.ok(()=>osrm.table(options, (err, res) => {}), "should work with zero"); + + options.start_stop_acceleration_factor = 2.0; + assert.ok(()=>osrm.table(options, (err, res) => {}), "Should work with positive numeric values"); + options.start_stop_acceleration_factor = 'car'; + assert.ok(()=>osrm.table(options, (err, res) => {}), "Should work with car defaults"); + options.start_stop_acceleration_factor = 'fast_car'; + assert.ok(()=>osrm.table(options, (err, res) => {}), "Should work with fast car defaults"); + options.start_stop_acceleration_factor = 'slow_car'; + assert.ok(()=>osrm.table(options, (err, res) => {}), "Should work with slow car defaults"); + options.start_stop_acceleration_factor = 'truck'; + assert.ok(()=>osrm.table(options, (err, res) => {}), "Should work with truck defaults"); + options.start_stop_acceleration_factor = 'tractor_trailer'; + assert.ok(()=>osrm.table(options, (err, res) => {}), "Should work with tractor trailer defaults"); + + options.start_stop_acceleration_factor = 'yes'; + assert.throws(()=>osrm.table(options, (err, res) => {}), /start_stop_acceleration_factor must be a decimal number or one of/, "should throw on non-recognized string"); + + }); });