diff --git a/features/car/stoppage.feature b/features/car/stoppage.feature deleted file mode 100644 index 762cca14f..000000000 --- a/features/car/stoppage.feature +++ /dev/null @@ -1,36 +0,0 @@ -@routing @maxspeed @car -Feature: Car - Acceleration profiles - - Background: Use specific speeds - Given the profile "car" - Given a grid size of 1000 meters - - Scenario: Car - Use stoppage penalty at waypoints - 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 - | stoppage_penalty | 5,100 | - - When I route I should get - | from | to | route | time | - | a | b | ab,ab | 481.1s | - - When I route I should get - | from | to | route | time | - | b | a | ab,ab | 609.9s | - - When I route I should get - | from | to | route | time | - | a | a | ab,ab | 0s | - - When I request a travel time matrix I should get - | | a | b | - | a | 0 | 481.1 | - | b | 609.9 | 0 | diff --git a/features/testbot/stoppage.feature b/features/testbot/stoppage.feature index 34a874c13..39ddc5964 100644 --- a/features/testbot/stoppage.feature +++ b/features/testbot/stoppage.feature @@ -3,9 +3,102 @@ Feature: Testbot - Acceleration profiles Background: Use specific speeds Given the profile "testbot" - Given a grid size of 1000 meters + + 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 | 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 | 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 - 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 | + + And the query options + | stoppage_penalty | 1.85,15 | + + When I request a travel time matrix I should get + | | a | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | b | + | a | 0 | 1.4 | 2 | 2.5 | 2.9 | 3.2 | 3.6 | 3.8 | 4.1 | 4.4 | 4.6 | + | 1 | 1.4 | 0 | 1.4 | 2 | 2.5 | 2.9 | 3.2 | 3.6 | 3.8 | 4.1 | 4.4 | + | 2 | 2 | 1.4 | 0 | 1.4 | 2 | 2.5 | 2.9 | 3.2 | 3.6 | 3.8 | 4.1 | + | 3 | 2.5 | 2 | 1.4 | 0 | 1.4 | 2 | 2.5 | 2.9 | 3.2 | 3.6 | 3.8 | + | 4 | 2.9 | 2.5 | 2 | 1.4 | 0 | 1.4 | 2 | 2.5 | 2.9 | 3.2 | 3.6 | + | 5 | 3.2 | 2.9 | 2.5 | 2 | 1.4 | 0 | 1.4 | 2 | 2.5 | 2.9 | 3.2 | + | 6 | 3.6 | 3.2 | 2.9 | 2.5 | 2 | 1.4 | 0 | 1.4 | 2 | 2.5 | 2.9 | + | 7 | 3.8 | 3.6 | 3.2 | 2.9 | 2.5 | 2 | 1.4 | 0 | 1.4 | 2 | 2.5 | + | 8 | 4.1 | 3.8 | 3.6 | 3.2 | 2.9 | 2.5 | 2 | 1.4 | 0 | 1.4 | 2 | + | 9 | 4.4 | 4.1 | 3.8 | 3.6 | 3.2 | 2.9 | 2.5 | 2 | 1.4 | 0 | 1.4 | + | b | 4.6 | 4.4 | 4.1 | 3.8 | 3.6 | 3.2 | 2.9 | 2.5 | 2 | 1.4 | 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 @@ -16,21 +109,60 @@ Feature: Testbot - Acceleration profiles | ab | trunk | 60 | 45 | And the query options - | stoppage_penalty | 5,100 | - - When I route I should get - | from | to | route | time | - | a | b | ab,ab | 412.3s | - - When I route I should get - | from | to | route | time | - | b | a | ab,ab | 505.9s | - - When I route I should get - | from | to | route | time | - | a | a | ab,ab | 0s | + | stoppage_penalty | 1.85,15 | When I request a travel time matrix I should get - | | a | b | - | a | 0 | 412.3 | - | b | 505.9 | 0 | + | | a | 1 | 2 | 3 | 4 | 5 | b | + | a | 0 | 4.6 | 6.5 | 8 | 9.3 | 10.4 | 11.3 | + | 1 | 4.6 | 0 | 4.6 | 6.5 | 8 | 9.3 | 10.3 | + | 2 | 6.5 | 4.6 | 0 | 4.6 | 6.5 | 8 | 9.2 | + | 3 | 8 | 6.5 | 4.6 | 0 | 4.6 | 6.5 | 8 | + | 4 | 9.3 | 8 | 6.5 | 4.6 | 0 | 4.6 | 6.5 | + | 5 | 10.4 | 9.3 | 8 | 6.5 | 4.6 | 0 | 4.6 | + | b | 11.3 | 10.3 | 9.2 | 8 | 6.5 | 4.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 + | stoppage_penalty | 1.85,15 | + + When I request a travel time matrix I should get + | | a | 1 | 2 | 3 | 4 | 5 | b | + | a | 0 | 68.9 | 128.9 | 188.9 | 248.9 | 308.9 | 368.9 | + | 1 | 86.6 | 0 | 69 | 129 | 189 | 249 | 309 | + | 2 | 166.6 | 86.7 | 0 | 69 | 129 | 189 | 249 | + | 3 | 246.6 | 166.7 | 86.7 | 0 | 69 | 129 | 189 | + | 4 | 326.6 | 246.7 | 166.7 | 86.7 | 0 | 69 | 129 | + | 5 | 406.6 | 326.7 | 246.7 | 166.7 | 86.7 | 0 | 69 | + | b | 486.6 | 406.7 | 326.7 | 246.7 | 166.7 | 86.7 | 0 | \ No newline at end of file diff --git a/profiles/car.lua b/profiles/car.lua index 02f185abb..65ae9b89b 100644 --- a/profiles/car.lua +++ b/profiles/car.lua @@ -120,9 +120,6 @@ function setup() -- classes to support for exclude flags excludable = Sequence { - Set {'toll'}, - Set {'motorway'}, - Set {'ferry'} }, avoid = Set { diff --git a/src/engine/plugins/table.cpp b/src/engine/plugins/table.cpp index 23490e007..973cc9f64 100644 --- a/src/engine/plugins/table.cpp +++ b/src/engine/plugins/table.cpp @@ -86,8 +86,10 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, bool request_distance = params.annotations & api::TableParameters::AnnotationsType::Distance; bool request_duration = params.annotations & api::TableParameters::AnnotationsType::Duration; + constexpr bool always_calculate_distance = true; + auto result_tables_pair = algorithms.ManyToManySearch( - snapped_phantoms, params.sources, params.destinations, request_distance); + snapped_phantoms, params.sources, params.destinations, always_calculate_distance); if ((request_duration && result_tables_pair.first.empty()) || (request_distance && result_tables_pair.second.empty())) @@ -97,20 +99,55 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, std::vector estimated_pairs; - auto penalize = [¶ms](float speed, EdgeDuration duration) -> EdgeDuration { - // this is the penalty before adjusting for the length of the route - auto speed_ratio = speed * MAXIMAL_ACCEL_DECEL_PENALIZABLE_SPEED_INVERSE; - auto penalty = speed_ratio * (params.max_stoppage_penalty - params.min_stoppage_penalty) + - params.min_stoppage_penalty; - // how much distance would we cover if we took half of it at this speed - // scale the penalty based on this distance, if its small we dont get much - // of the penalty, buf if its large we get up to the whole penalty - // .05 is to scale the duration to second and cut it in half - // .005 is like / 200m, so the estimated distance as a ratio of 200m - // so you get the full penalty if your route is 200m or more - auto distance_ratio = (speed * duration * .05) * 0.008; - return static_cast( - (distance_ratio < 1.f ? distance_ratio * penalty : penalty) * 10.); + // 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 &comfortable_acceleration, + const EdgeDuration &duration, + const EdgeDistance &distance) -> EdgeDuration { + + // Assume linear acceleration from 0 to velocity + // auto comfortable_acceleration = 1.85; // m/s^2 + + // 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 + + // Using the equations of motion as a simple approximation, assuming constant acceleration + // https://en.wikipedia.org/wiki/Equations_of_motion#Constant_translational_acceleration_in_a_straight_line + const auto distance_to_full_speed = + (average_speed * average_speed) / (2 * comfortable_acceleration); + + /* + std::cout << "Comfortable accel is " << comfortable_acceleration << std::endl; + std::cout << "Average speed is " << average_speed << " duration is " << duration + << std::endl; + std::cout << "Distance is " << distance << " distance to full speed is " + << distance_to_full_speed << std::endl; + */ + + if (distance_to_full_speed > distance / 2) + { + // std::cout << "Distance was too short, so only using half" << std::endl; + const auto time_to_halfway = std::sqrt(distance / comfortable_acceleration); + return (2 * time_to_halfway) * 10; // result is in deciseconds + } + else + { + // std::cout << "Distance was long, using cruising speed" << std::endl; + const auto cruising_distance = distance - 2 * distance_to_full_speed; + const auto cruising_time = cruising_distance / average_speed; + const auto acceleration_time = average_speed / comfortable_acceleration; + + // std::cout << "Cruising distance is " << cruising_distance << std::endl; + // std::cout << "Cruising time is " << cruising_time << std::endl; + // std::cout << "Acceleration time is " << acceleration_time << std::endl; + return (cruising_time + 2 * acceleration_time) * 10; // result is in deciseconds + } }; // Scan table for null results - if any exist, replace with distance estimates @@ -127,16 +164,17 @@ Status TablePlugin::HandleRequest(const RoutingAlgorithmsInterface &algorithms, // apply an accel/deceleration penalty to the duration if (result_tables_pair.first[table_index] != MAXIMAL_EDGE_DURATION && row != column) { + /* const auto &source = snapped_phantoms[params.sources.empty() ? row : params.sources[row]]; const auto &destination = snapped_phantoms[params.destinations.empty() ? column : params.destinations[column]]; - result_tables_pair.first[table_index] += - penalize(source.speed_approximation, - result_tables_pair.first[table_index]) + - penalize(destination.speed_approximation, - result_tables_pair.first[table_index]); + */ + result_tables_pair.first[table_index] = + adjust_for_startstop(params.min_stoppage_penalty, + 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 && @@ -187,6 +225,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);