Consider acceleration profile of vehicle travelling - particularly affects very short routes.
This commit is contained in:
parent
23c69f4c3d
commit
3d4d51d6b7
168
features/testbot/stoppage.feature
Normal file
168
features/testbot/stoppage.feature
Normal file
@ -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 |
|
@ -81,6 +81,8 @@ struct BaseParameters
|
|||||||
bool generate_hints = true;
|
bool generate_hints = true;
|
||||||
|
|
||||||
SnappingType snapping = SnappingType::Default;
|
SnappingType snapping = SnappingType::Default;
|
||||||
|
// Whether or not to add acceleration/decelleration penalties at waypoints
|
||||||
|
double waypoint_acceleration_factor = 0.;
|
||||||
|
|
||||||
BaseParameters(const std::vector<util::Coordinate> coordinates_ = {},
|
BaseParameters(const std::vector<util::Coordinate> coordinates_ = {},
|
||||||
const std::vector<boost::optional<Hint>> hints_ = {},
|
const std::vector<boost::optional<Hint>> hints_ = {},
|
||||||
@ -89,10 +91,11 @@ struct BaseParameters
|
|||||||
std::vector<boost::optional<Approach>> approaches_ = {},
|
std::vector<boost::optional<Approach>> approaches_ = {},
|
||||||
bool generate_hints_ = true,
|
bool generate_hints_ = true,
|
||||||
std::vector<std::string> exclude = {},
|
std::vector<std::string> exclude = {},
|
||||||
const SnappingType snapping_ = SnappingType::Default)
|
const SnappingType snapping_ = SnappingType::Default,
|
||||||
|
bool waypoint_acceleration_factor_ = 0.)
|
||||||
: coordinates(coordinates_), hints(hints_), radiuses(radiuses_), bearings(bearings_),
|
: coordinates(coordinates_), hints(hints_), radiuses(radiuses_), bearings(bearings_),
|
||||||
approaches(approaches_), exclude(std::move(exclude)), generate_hints(generate_hints_),
|
approaches(approaches_), exclude(std::move(exclude)), generate_hints(generate_hints_),
|
||||||
snapping(snapping_)
|
snapping(snapping_), waypoint_acceleration_factor(waypoint_acceleration_factor_)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -721,6 +721,48 @@ inline bool argumentsToParameter(const Nan::FunctionCallbackInfo<v8::Value> &arg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (obj->Has(Nan::New("acceleration_profile").ToLocalChecked()))
|
||||||
|
{
|
||||||
|
v8::Local<v8::Value> 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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -135,6 +135,19 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
|||||||
},
|
},
|
||||||
qi::_1)];
|
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 =
|
query_rule =
|
||||||
((location_rule % ';') | polyline_rule |
|
((location_rule % ';') | polyline_rule |
|
||||||
polyline6_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1];
|
polyline6_rule)[ph::bind(&engine::api::BaseParameters::coordinates, qi::_r1) = qi::_1];
|
||||||
@ -179,7 +192,8 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
|||||||
| generate_hints_rule(qi::_r1) //
|
| generate_hints_rule(qi::_r1) //
|
||||||
| approach_rule(qi::_r1) //
|
| approach_rule(qi::_r1) //
|
||||||
| exclude_rule(qi::_r1) //
|
| exclude_rule(qi::_r1) //
|
||||||
| snapping_rule(qi::_r1);
|
| snapping_rule(qi::_r1) //
|
||||||
|
| acceleration_profile_rule(qi::_r1);//
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@ -196,6 +210,7 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
|||||||
qi::rule<Iterator, Signature> generate_hints_rule;
|
qi::rule<Iterator, Signature> generate_hints_rule;
|
||||||
qi::rule<Iterator, Signature> approach_rule;
|
qi::rule<Iterator, Signature> approach_rule;
|
||||||
qi::rule<Iterator, Signature> exclude_rule;
|
qi::rule<Iterator, Signature> exclude_rule;
|
||||||
|
qi::rule<Iterator, Signature> acceleration_profile_rule;
|
||||||
|
|
||||||
qi::rule<Iterator, osrm::engine::Bearing()> bearing_rule;
|
qi::rule<Iterator, osrm::engine::Bearing()> bearing_rule;
|
||||||
qi::rule<Iterator, osrm::util::Coordinate()> location_rule;
|
qi::rule<Iterator, osrm::util::Coordinate()> location_rule;
|
||||||
@ -207,6 +222,8 @@ struct BaseParametersGrammar : boost::spirit::qi::grammar<Iterator, Signature>
|
|||||||
qi::rule<Iterator, double()> unlimited_rule;
|
qi::rule<Iterator, double()> unlimited_rule;
|
||||||
qi::rule<Iterator, Signature> snapping_rule;
|
qi::rule<Iterator, Signature> snapping_rule;
|
||||||
|
|
||||||
|
qi::rule<Iterator, double()> acceleration_alpha_defaults_rule;
|
||||||
|
|
||||||
qi::symbols<char, engine::Approach> approach_type;
|
qi::symbols<char, engine::Approach> approach_type;
|
||||||
qi::symbols<char, engine::api::BaseParameters::SnappingType> snapping_type;
|
qi::symbols<char, engine::api::BaseParameters::SnappingType> snapping_type;
|
||||||
};
|
};
|
||||||
|
@ -488,8 +488,8 @@ inline void Prettify(char *buffer, int length, int k)
|
|||||||
inline void dtoa_milo(double value, char *buffer)
|
inline void dtoa_milo(double value, char *buffer)
|
||||||
{
|
{
|
||||||
// Not handling NaN and inf
|
// Not handling NaN and inf
|
||||||
assert(!isnan(value));
|
assert(!std::isnan(value));
|
||||||
assert(!isinf(value));
|
assert(!std::isinf(value));
|
||||||
|
|
||||||
if (value == 0)
|
if (value == 0)
|
||||||
{
|
{
|
||||||
|
@ -118,6 +118,14 @@ static const TurnPenalty INVALID_TURN_PENALTY = std::numeric_limits<TurnPenalty>
|
|||||||
static const EdgeDistance INVALID_EDGE_DISTANCE = std::numeric_limits<EdgeDistance>::max();
|
static const EdgeDistance INVALID_EDGE_DISTANCE = std::numeric_limits<EdgeDistance>::max();
|
||||||
static const EdgeDistance INVALID_FALLBACK_SPEED = std::numeric_limits<double>::max();
|
static const EdgeDistance INVALID_FALLBACK_SPEED = std::numeric_limits<double>::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
|
// 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
|
// 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
|
// min() / operator< checks due to the invalid truncation. In addition, using signed and unsigned
|
||||||
|
@ -86,8 +86,11 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
|||||||
bool request_distance = params.annotations & api::TableParameters::AnnotationsType::Distance;
|
bool request_distance = params.annotations & api::TableParameters::AnnotationsType::Distance;
|
||||||
bool request_duration = params.annotations & api::TableParameters::AnnotationsType::Duration;
|
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(
|
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()) ||
|
if ((request_duration && result_tables_pair.first.empty()) ||
|
||||||
(request_distance && result_tables_pair.second.empty()))
|
(request_distance && result_tables_pair.second.empty()))
|
||||||
@ -97,8 +100,71 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms,
|
|||||||
|
|
||||||
std::vector<api::TableAPI::TableCellRef> estimated_pairs;
|
std::vector<api::TableAPI::TableCellRef> 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
|
// 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++)
|
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;
|
const auto &table_index = row * num_destinations + column;
|
||||||
BOOST_ASSERT(table_index < result_tables_pair.first.size());
|
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 &&
|
if (params.fallback_speed != INVALID_FALLBACK_SPEED && params.fallback_speed > 0 &&
|
||||||
result_tables_pair.first[table_index] == MAXIMAL_EDGE_DURATION)
|
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);
|
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 &&
|
if (params.scale_factor > 0 && params.scale_factor != 1 &&
|
||||||
result_tables_pair.first[table_index] != MAXIMAL_EDGE_DURATION &&
|
result_tables_pair.first[table_index] != MAXIMAL_EDGE_DURATION &&
|
||||||
result_tables_pair.first[table_index] != 0)
|
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<EdgeDistance> empty;
|
||||||
|
result_tables_pair.second.swap(empty);
|
||||||
|
}
|
||||||
|
|
||||||
api::TableAPI table_api{facade, params};
|
api::TableAPI table_api{facade, params};
|
||||||
table_api.MakeResponse(result_tables_pair, snapped_phantoms, estimated_pairs, result);
|
table_api.MakeResponse(result_tables_pair, snapped_phantoms, estimated_pairs, result);
|
||||||
|
|
||||||
|
@ -143,7 +143,8 @@ void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
|||||||
middle_nodes_table[row_index * number_of_targets + column_index] = node;
|
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_weight = new_weight;
|
||||||
current_duration = new_duration;
|
current_duration = new_duration;
|
||||||
|
@ -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");
|
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");
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user