diff --git a/features/testbot/zero-speed-updates.feature b/features/testbot/zero-speed-updates.feature new file mode 100644 index 000000000..d4ab915c8 --- /dev/null +++ b/features/testbot/zero-speed-updates.feature @@ -0,0 +1,163 @@ +Feature: Check zero speed updates + + Scenario: Matching on restricted way, single segment + Given the query options + | geometries | geojson | + | annotations | true | + + Given the node map + """ + a-1--b--c-2-d + """ + + And the ways + | nodes | + | abcd | + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 2,3,0 + """ + + When I match I should get + | trace | code | + | 12 | NoMatch | + + + Scenario: Matching restricted way, both segments + Given the node map + """ + a-1--b-2-c + """ + + And the ways + | nodes | oneway | + | abc | no | + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 2,3,0 + 3,2,0 + """ + + When I match I should get + | trace | code | + | 12 | NoMatch | + + + Scenario: Matching on restricted oneway + Given the node map + """ + a-1--b-2-c + """ + + And the ways + | nodes | oneway | + | abc | yes | + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 2,3,0 + """ + + When I match I should get + | trace | code | + | 12 | NoMatch | + + + Scenario: Routing on restricted way + Given the node map + """ + a-1-b-2-c + """ + + And the ways + | nodes | oneway | + | abc | no | + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 2,3,0 + 3,2,0 + """ + + When I route I should get + | from | to | code | + | 1 | 2 | NoRoute | + + + Scenario: Routing on restricted oneway + Given the node map + """ + a-1-b-2-c + """ + + And the ways + | nodes | oneway | + | abc | yes | + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 2,3,0 + 3,2,0 + """ + + When I route I should get + | from | to | bearings | code | + | 1 | 2 | 270 270 | NoRoute | + + + Scenario: Via routing on restricted oneway + Given the node map + """ + a-1-b-2-c-3-d + """ + + And the ways + | nodes | oneway | + | abc | no | + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 2,3,0 + 3,2,0 + """ + + When I route I should get + | waypoints | code | + | 1,2,3 | NoRoute | + | 3,2,1 | NoRoute | + + + @trip + Scenario: Trip + Given the node map + """ + a b + c d + """ + + And the ways + | nodes | + | ab | + | bc | + | cb | + | da | + And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file + """ + 1,2,0 + 2,1,0 + """ + + When I plan a trip I should get + | waypoints | trips | code | + | a,b,c,d | abcda | NoTrips | + | d,b,c,a | dbcad | NoTrips | diff --git a/include/engine/geospatial_query.hpp b/include/engine/geospatial_query.hpp index 51d56bdef..e79ec6038 100644 --- a/include/engine/geospatial_query.hpp +++ b/include/engine/geospatial_query.hpp @@ -416,6 +416,20 @@ template class GeospatialQuery reverse_duration -= static_cast(reverse_duration * ratio); } + // check phantom node segments validity + auto areSegmentsValid = [](auto first, auto last) -> bool { + return std::find(first, last, INVALID_EDGE_WEIGHT) == last; + }; + bool is_forward_valid_source = + areSegmentsValid(forward_weight_vector.begin(), forward_weight_vector.end()); + bool is_forward_valid_target = + areSegmentsValid(forward_weight_vector.begin(), + forward_weight_vector.begin() + data.fwd_segment_position + 1); + bool is_reverse_valid_source = + areSegmentsValid(reverse_weight_vector.begin(), reverse_weight_vector.end()); + bool is_reverse_valid_target = areSegmentsValid( + reverse_weight_vector.begin(), reverse_weight_vector.end() - data.fwd_segment_position); + auto transformed = PhantomNodeWithDistance{PhantomNode{data, forward_weight, reverse_weight, @@ -425,6 +439,10 @@ template class GeospatialQuery reverse_duration, forward_duration_offset, reverse_duration_offset, + is_forward_valid_source, + is_forward_valid_target, + is_reverse_valid_source, + is_reverse_valid_target, point_on_segment, input_coordinate}, current_perpendicular_distance}; diff --git a/include/engine/phantom_node.hpp b/include/engine/phantom_node.hpp index fa32cfa49..515f93615 100644 --- a/include/engine/phantom_node.hpp +++ b/include/engine/phantom_node.hpp @@ -46,37 +46,6 @@ namespace engine struct PhantomNode { - PhantomNode(SegmentID forward_segment_id, - SegmentID reverse_segment_id, - unsigned name_id, - EdgeWeight forward_weight, - EdgeWeight reverse_weight, - EdgeWeight forward_weight_offset, - EdgeWeight reverse_weight_offset, - EdgeWeight forward_duration, - EdgeWeight reverse_duration, - EdgeWeight forward_duration_offset, - EdgeWeight reverse_duration_offset, - unsigned packed_geometry_id_, - bool is_tiny_component, - unsigned component_id, - util::Coordinate location, - util::Coordinate input_location, - unsigned short fwd_segment_position, - extractor::TravelMode forward_travel_mode, - extractor::TravelMode backward_travel_mode) - : forward_segment_id(forward_segment_id), reverse_segment_id(reverse_segment_id), - name_id(name_id), forward_weight(forward_weight), reverse_weight(reverse_weight), - forward_weight_offset(forward_weight_offset), - reverse_weight_offset(reverse_weight_offset), forward_duration(forward_duration), - reverse_duration(reverse_duration), forward_duration_offset(forward_duration_offset), - reverse_duration_offset(reverse_duration_offset), packed_geometry_id(packed_geometry_id_), - component{component_id, is_tiny_component}, location(std::move(location)), - input_location(std::move(input_location)), fwd_segment_position(fwd_segment_position), - forward_travel_mode(forward_travel_mode), backward_travel_mode(backward_travel_mode) - { - } - PhantomNode() : forward_segment_id{SPECIAL_SEGMENTID, false}, reverse_segment_id{SPECIAL_SEGMENTID, false}, @@ -86,7 +55,9 @@ struct PhantomNode forward_duration_offset(0), reverse_duration_offset(0), packed_geometry_id(SPECIAL_GEOMETRYID), component{INVALID_COMPONENTID, false}, fwd_segment_position(0), forward_travel_mode(TRAVEL_MODE_INACCESSIBLE), - backward_travel_mode(TRAVEL_MODE_INACCESSIBLE) + backward_travel_mode(TRAVEL_MODE_INACCESSIBLE), is_forward_valid_source(false), + is_forward_valid_target(false), is_reverse_valid_source(false), + is_reverse_valid_target(false) { } @@ -134,6 +105,23 @@ struct PhantomNode bool IsValid() const { return location.IsValid() && (name_id != INVALID_NAMEID); } + bool IsForwardValidSource() const + { + return forward_segment_id.enabled && is_forward_valid_source; + } + bool IsForwardValidTarget() const + { + return forward_segment_id.enabled && is_forward_valid_target; + } + bool IsReverseValidSource() const + { + return reverse_segment_id.enabled && is_reverse_valid_source; + } + bool IsReverseValidTarget() const + { + return reverse_segment_id.enabled && is_reverse_valid_target; + } + bool operator==(const PhantomNode &other) const { return location == other.location; } template @@ -146,6 +134,10 @@ struct PhantomNode EdgeWeight reverse_duration, EdgeWeight forward_duration_offset, EdgeWeight reverse_duration_offset, + bool is_forward_valid_source, + bool is_forward_valid_target, + bool is_reverse_valid_source, + bool is_reverse_valid_target, const util::Coordinate location, const util::Coordinate input_location) : forward_segment_id{other.forward_segment_id}, @@ -159,7 +151,11 @@ struct PhantomNode component{other.component.id, other.component.is_tiny}, location{location}, input_location{input_location}, fwd_segment_position{other.fwd_segment_position}, forward_travel_mode{other.forward_travel_mode}, - backward_travel_mode{other.backward_travel_mode} + backward_travel_mode{other.backward_travel_mode}, + is_forward_valid_source{is_forward_valid_source}, + is_forward_valid_target{is_forward_valid_target}, + is_reverse_valid_source{is_reverse_valid_source}, + is_reverse_valid_target{is_reverse_valid_target} { } @@ -187,8 +183,14 @@ struct PhantomNode unsigned short fwd_segment_position; // note 4 bits would suffice for each, // but the saved byte would be padding anyway - extractor::TravelMode forward_travel_mode; - extractor::TravelMode backward_travel_mode; + extractor::TravelMode forward_travel_mode : 4; + extractor::TravelMode backward_travel_mode : 4; + // is phantom node valid to be used as source or target + private: + bool is_forward_valid_source : 1; + bool is_forward_valid_target : 1; + bool is_reverse_valid_source : 1; + bool is_reverse_valid_target : 1; }; static_assert(sizeof(PhantomNode) == 72, "PhantomNode has more padding then expected"); diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index 23cf32bff..c9e4e396a 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -41,26 +41,6 @@ bool needsLoopForward(const PhantomNode &source_phantom, const PhantomNode &targ bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &target_phantom); -template -void insertNodesInHeap(Heap &heap, const PhantomNode &phantom_node) -{ - BOOST_ASSERT(phantom_node.IsValid()); - - const auto weight_sign = DIRECTION == FORWARD_DIRECTION ? -1 : 1; - if (phantom_node.forward_segment_id.enabled) - { - heap.Insert(phantom_node.forward_segment_id.id, - weight_sign * phantom_node.GetForwardWeightPlusOffset(), - phantom_node.forward_segment_id.id); - } - if (phantom_node.reverse_segment_id.enabled) - { - heap.Insert(phantom_node.reverse_segment_id.id, - weight_sign * phantom_node.GetReverseWeightPlusOffset(), - phantom_node.reverse_segment_id.id); - } -} - template void insertNodesInHeap(SearchEngineData::ManyToManyQueryHeap &heap, const PhantomNode &phantom_node) @@ -68,14 +48,16 @@ void insertNodesInHeap(SearchEngineData::ManyToManyQueryHeap &hea BOOST_ASSERT(phantom_node.IsValid()); const auto weight_sign = DIRECTION == FORWARD_DIRECTION ? -1 : 1; - if (phantom_node.forward_segment_id.enabled) + if ((DIRECTION == FORWARD_DIRECTION && phantom_node.IsForwardValidSource()) || + (DIRECTION == REVERSE_DIRECTION && phantom_node.IsForwardValidTarget())) { heap.Insert( phantom_node.forward_segment_id.id, weight_sign * phantom_node.GetForwardWeightPlusOffset(), {phantom_node.forward_segment_id.id, weight_sign * phantom_node.GetForwardDuration()}); } - if (phantom_node.reverse_segment_id.enabled) + if ((DIRECTION == FORWARD_DIRECTION && phantom_node.IsReverseValidSource()) || + (DIRECTION == REVERSE_DIRECTION && phantom_node.IsReverseValidTarget())) { heap.Insert( phantom_node.reverse_segment_id.id, @@ -87,8 +69,35 @@ void insertNodesInHeap(SearchEngineData::ManyToManyQueryHeap &hea template void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNodes &nodes) { - insertNodesInHeap(forward_heap, nodes.source_phantom); - insertNodesInHeap(reverse_heap, nodes.target_phantom); + const auto &source = nodes.source_phantom; + if (source.IsForwardValidSource()) + { + forward_heap.Insert(source.forward_segment_id.id, + -source.GetForwardWeightPlusOffset(), + source.forward_segment_id.id); + } + + if (source.IsReverseValidSource()) + { + forward_heap.Insert(source.reverse_segment_id.id, + -source.GetReverseWeightPlusOffset(), + source.reverse_segment_id.id); + } + + const auto &target = nodes.target_phantom; + if (target.IsForwardValidTarget()) + { + reverse_heap.Insert(target.forward_segment_id.id, + target.GetForwardWeightPlusOffset(), + target.forward_segment_id.id); + } + + if (target.IsReverseValidTarget()) + { + reverse_heap.Insert(target.reverse_segment_id.id, + target.GetReverseWeightPlusOffset(), + target.reverse_segment_id.id); + } } template diff --git a/include/engine/routing_algorithms/routing_base_mld.hpp b/include/engine/routing_algorithms/routing_base_mld.hpp index 88cfa3c17..340924f38 100644 --- a/include/engine/routing_algorithms/routing_base_mld.hpp +++ b/include/engine/routing_algorithms/routing_base_mld.hpp @@ -190,6 +190,10 @@ search(SearchEngineData &engine_working_data, EdgeWeight weight_upper_bound, Args... args) { + if (forward_heap.Empty() || reverse_heap.Empty()) + { + return std::make_tuple(INVALID_EDGE_WEIGHT, std::vector(), std::vector()); + } const auto &partition = facade.GetMultiLevelPartition(); @@ -387,7 +391,7 @@ getNetworkDistance(SearchEngineData &engine_working_data, const PhantomNodes phantom_nodes{source_phantom, target_phantom}; insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes); - EdgeWeight weight; + EdgeWeight weight = INVALID_EDGE_WEIGHT; std::vector unpacked_nodes; std::vector unpacked_edges; std::tie(weight, unpacked_nodes, unpacked_edges) = search(engine_working_data, @@ -400,7 +404,9 @@ getNetworkDistance(SearchEngineData &engine_working_data, phantom_nodes); if (weight == INVALID_EDGE_WEIGHT) + { return std::numeric_limits::max(); + } std::vector unpacked_path; diff --git a/src/engine/routing_algorithms/direct_shortest_path.cpp b/src/engine/routing_algorithms/direct_shortest_path.cpp index 9a2047e77..2f74c5e1a 100644 --- a/src/engine/routing_algorithms/direct_shortest_path.cpp +++ b/src/engine/routing_algorithms/direct_shortest_path.cpp @@ -131,7 +131,7 @@ InternalRouteResult directShortestPathSearch( // TODO: when structured bindings will be allowed change to // auto [weight, source_node, target_node, unpacked_edges] = ... - EdgeWeight weight; + EdgeWeight weight = INVALID_EDGE_WEIGHT; std::vector unpacked_nodes; std::vector unpacked_edges; std::tie(weight, unpacked_nodes, unpacked_edges) = mld::search(engine_working_data, diff --git a/src/engine/routing_algorithms/routing_base.cpp b/src/engine/routing_algorithms/routing_base.cpp index 08cda29fe..02cb69ca6 100644 --- a/src/engine/routing_algorithms/routing_base.cpp +++ b/src/engine/routing_algorithms/routing_base.cpp @@ -9,7 +9,7 @@ namespace routing_algorithms bool needsLoopForward(const PhantomNode &source_phantom, const PhantomNode &target_phantom) { - return source_phantom.forward_segment_id.enabled && target_phantom.forward_segment_id.enabled && + return source_phantom.IsForwardValidSource() && target_phantom.IsForwardValidTarget() && source_phantom.forward_segment_id.id == target_phantom.forward_segment_id.id && source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset(); @@ -17,7 +17,7 @@ bool needsLoopForward(const PhantomNode &source_phantom, const PhantomNode &targ bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &target_phantom) { - return source_phantom.reverse_segment_id.enabled && target_phantom.reverse_segment_id.enabled && + return source_phantom.IsReverseValidSource() && target_phantom.IsReverseValidTarget() && source_phantom.reverse_segment_id.id == target_phantom.reverse_segment_id.id && source_phantom.GetReverseWeightPlusOffset() > target_phantom.GetReverseWeightPlusOffset(); diff --git a/src/engine/routing_algorithms/routing_base_ch.cpp b/src/engine/routing_algorithms/routing_base_ch.cpp index e581eef70..b693f4330 100644 --- a/src/engine/routing_algorithms/routing_base_ch.cpp +++ b/src/engine/routing_algorithms/routing_base_ch.cpp @@ -82,6 +82,12 @@ void search(SearchEngineData & /*engine_working_data*/, const PhantomNodes & /*phantom_nodes*/, const EdgeWeight weight_upper_bound) { + if (forward_heap.Empty() || reverse_heap.Empty()) + { + weight = INVALID_EDGE_WEIGHT; + return; + } + NodeID middle = SPECIAL_NODEID; weight = weight_upper_bound; @@ -155,31 +161,7 @@ double getNetworkDistance(SearchEngineData &engine_working_data, forward_heap.Clear(); reverse_heap.Clear(); - if (source_phantom.forward_segment_id.enabled) - { - forward_heap.Insert(source_phantom.forward_segment_id.id, - -source_phantom.GetForwardWeightPlusOffset(), - source_phantom.forward_segment_id.id); - } - if (source_phantom.reverse_segment_id.enabled) - { - forward_heap.Insert(source_phantom.reverse_segment_id.id, - -source_phantom.GetReverseWeightPlusOffset(), - source_phantom.reverse_segment_id.id); - } - - if (target_phantom.forward_segment_id.enabled) - { - reverse_heap.Insert(target_phantom.forward_segment_id.id, - target_phantom.GetForwardWeightPlusOffset(), - target_phantom.forward_segment_id.id); - } - if (target_phantom.reverse_segment_id.enabled) - { - reverse_heap.Insert(target_phantom.reverse_segment_id.id, - target_phantom.GetReverseWeightPlusOffset(), - target_phantom.reverse_segment_id.id); - } + insertNodesInHeaps(forward_heap, reverse_heap, {source_phantom, target_phantom}); EdgeWeight weight = INVALID_EDGE_WEIGHT; std::vector packed_path; diff --git a/src/engine/routing_algorithms/shortest_path.cpp b/src/engine/routing_algorithms/shortest_path.cpp index 8c11b772a..d07d50e0d 100644 --- a/src/engine/routing_algorithms/shortest_path.cpp +++ b/src/engine/routing_algorithms/shortest_path.cpp @@ -63,9 +63,6 @@ void searchWithUTurn(SearchEngineData &engine_working_data, target_phantom.reverse_segment_id.id); } - BOOST_ASSERT(forward_heap.Size() > 0); - BOOST_ASSERT(reverse_heap.Size() > 0); - // this is only relevent if source and target are on the same compressed edge auto is_oneway_source = !(search_from_forward_node && search_from_reverse_node); auto is_oneway_target = !(search_to_forward_node && search_to_reverse_node); @@ -236,9 +233,9 @@ shortestPathSearch(SearchEngineData &engine_working_data, int total_weight_to_forward = 0; int total_weight_to_reverse = 0; bool search_from_forward_node = - phantom_nodes_vector.front().source_phantom.forward_segment_id.enabled; + phantom_nodes_vector.front().source_phantom.IsForwardValidSource(); bool search_from_reverse_node = - phantom_nodes_vector.front().source_phantom.reverse_segment_id.enabled; + phantom_nodes_vector.front().source_phantom.IsReverseValidSource(); std::vector prev_packed_leg_to_forward; std::vector prev_packed_leg_to_reverse; @@ -262,13 +259,11 @@ shortestPathSearch(SearchEngineData &engine_working_data, const auto &source_phantom = phantom_node_pair.source_phantom; const auto &target_phantom = phantom_node_pair.target_phantom; - bool search_to_forward_node = target_phantom.forward_segment_id.enabled; - bool search_to_reverse_node = target_phantom.reverse_segment_id.enabled; + bool search_to_forward_node = target_phantom.IsForwardValidTarget(); + bool search_to_reverse_node = target_phantom.IsReverseValidTarget(); - BOOST_ASSERT(!search_from_forward_node || source_phantom.forward_segment_id.enabled); - BOOST_ASSERT(!search_from_reverse_node || source_phantom.reverse_segment_id.enabled); - - BOOST_ASSERT(search_from_forward_node || search_from_reverse_node); + BOOST_ASSERT(!search_from_forward_node || source_phantom.IsForwardValidSource()); + BOOST_ASSERT(!search_from_reverse_node || source_phantom.IsReverseValidSource()); if (search_to_reverse_node || search_to_forward_node) { @@ -290,9 +285,9 @@ shortestPathSearch(SearchEngineData &engine_working_data, packed_leg_to_forward); // if only the reverse node is valid (e.g. when using the match plugin) we // actually need to move - if (!target_phantom.forward_segment_id.enabled) + if (!target_phantom.IsForwardValidTarget()) { - BOOST_ASSERT(target_phantom.reverse_segment_id.enabled); + BOOST_ASSERT(target_phantom.IsReverseValidTarget()); new_total_weight_to_reverse = new_total_weight_to_forward; packed_leg_to_reverse = std::move(packed_leg_to_forward); new_total_weight_to_forward = INVALID_EDGE_WEIGHT; @@ -302,7 +297,7 @@ shortestPathSearch(SearchEngineData &engine_working_data, // Below we have to check if new_total_weight_to_forward is invalid. // This prevents use-after-move on packed_leg_to_forward. } - else if (target_phantom.reverse_segment_id.enabled) + else if (target_phantom.IsReverseValidTarget()) { new_total_weight_to_reverse = new_total_weight_to_forward; packed_leg_to_reverse = packed_leg_to_forward; @@ -390,7 +385,7 @@ shortestPathSearch(SearchEngineData &engine_working_data, if (new_total_weight_to_forward != INVALID_EDGE_WEIGHT) { - BOOST_ASSERT(target_phantom.forward_segment_id.enabled); + BOOST_ASSERT(target_phantom.IsForwardValidTarget()); packed_leg_to_forward_begin.push_back(total_packed_path_to_forward.size()); total_packed_path_to_forward.insert(total_packed_path_to_forward.end(), @@ -407,7 +402,7 @@ shortestPathSearch(SearchEngineData &engine_working_data, if (new_total_weight_to_reverse != INVALID_EDGE_WEIGHT) { - BOOST_ASSERT(target_phantom.reverse_segment_id.enabled); + BOOST_ASSERT(target_phantom.IsReverseValidTarget()); packed_leg_to_reverse_begin.push_back(total_packed_path_to_reverse.size()); total_packed_path_to_reverse.insert(total_packed_path_to_reverse.end(),