#ifndef OSRM_ENGINE_ROUTING_BASE_HPP #define OSRM_ENGINE_ROUTING_BASE_HPP #include "guidance/turn_bearing.hpp" #include "guidance/turn_instruction.hpp" #include "engine/algorithm.hpp" #include "engine/datafacade.hpp" #include "engine/internal_route_result.hpp" #include "engine/phantom_node.hpp" #include "engine/search_engine_data.hpp" #include "util/coordinate_calculation.hpp" #include "util/typedefs.hpp" #include #include #include #include #include #include #include #include #include #include #include namespace osrm::engine::routing_algorithms { namespace details { template void insertSourceInForwardHeap(Heap &forward_heap, const PhantomNode &source) { if (source.IsValidForwardSource()) { forward_heap.Insert(source.forward_segment_id.id, EdgeWeight{0} - source.GetForwardWeightPlusOffset(), source.forward_segment_id.id); } if (source.IsValidReverseSource()) { forward_heap.Insert(source.reverse_segment_id.id, EdgeWeight{0} - source.GetReverseWeightPlusOffset(), source.reverse_segment_id.id); } } template void insertTargetInReverseHeap(Heap &reverse_heap, const PhantomNode &target) { if (target.IsValidForwardTarget()) { reverse_heap.Insert(target.forward_segment_id.id, target.GetForwardWeightPlusOffset(), target.forward_segment_id.id); } if (target.IsValidReverseTarget()) { reverse_heap.Insert(target.reverse_segment_id.id, target.GetReverseWeightPlusOffset(), target.reverse_segment_id.id); } } } // namespace details static constexpr bool FORWARD_DIRECTION = true; static constexpr bool REVERSE_DIRECTION = false; // Identify nodes in the forward(reverse) search direction that will require step forcing // e.g. if source and destination nodes are on the same segment. std::vector getForwardForceNodes(const PhantomEndpointCandidates &candidates); std::vector getForwardForceNodes(const PhantomCandidatesToTarget &candidates); std::vector getBackwardForceNodes(const PhantomEndpointCandidates &candidates); std::vector getBackwardForceNodes(const PhantomCandidatesToTarget &candidates); // Find the specific phantom node endpoints for a given path from a list of candidates. PhantomEndpoints endpointsFromCandidates(const PhantomEndpointCandidates &candidates, const std::vector &path); template inline bool shouldForceStep(const std::vector &force_nodes, const HeapNodeT &forward_heap_node, const HeapNodeT &reverse_heap_node) { // routing steps are forced when the node is a source of both forward and reverse search heaps. return forward_heap_node.data.parent == forward_heap_node.node && reverse_heap_node.data.parent == reverse_heap_node.node && std::find(force_nodes.begin(), force_nodes.end(), forward_heap_node.node) != force_nodes.end(); } template void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomEndpoints &endpoints) { details::insertSourceInForwardHeap(forward_heap, endpoints.source_phantom); details::insertTargetInReverseHeap(reverse_heap, endpoints.target_phantom); } template void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomEndpointCandidates &endpoint_candidates) { for (const auto &source : endpoint_candidates.source_phantoms) { details::insertSourceInForwardHeap(forward_heap, source); } for (const auto &target : endpoint_candidates.target_phantoms) { details::insertTargetInReverseHeap(reverse_heap, target); } } template void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNodeCandidates &source_candidates) { for (const auto &phantom_node : source_candidates) { if (phantom_node.IsValidForwardSource()) { heap.Insert(phantom_node.forward_segment_id.id, EdgeWeight{0} - phantom_node.GetForwardWeightPlusOffset(), {phantom_node.forward_segment_id.id, EdgeDuration{0} - phantom_node.GetForwardDuration(), EdgeDistance{0} - phantom_node.GetForwardDistance()}); } if (phantom_node.IsValidReverseSource()) { heap.Insert(phantom_node.reverse_segment_id.id, EdgeWeight{0} - phantom_node.GetReverseWeightPlusOffset(), {phantom_node.reverse_segment_id.id, EdgeDuration{0} - phantom_node.GetReverseDuration(), EdgeDistance{0} - phantom_node.GetReverseDistance()}); } } } template void insertTargetInHeap(ManyToManyQueryHeap &heap, const PhantomNodeCandidates &target_candidates) { for (const auto &phantom_node : target_candidates) { if (phantom_node.IsValidForwardTarget()) { heap.Insert(phantom_node.forward_segment_id.id, phantom_node.GetForwardWeightPlusOffset(), {phantom_node.forward_segment_id.id, phantom_node.GetForwardDuration(), phantom_node.GetForwardDistance()}); } if (phantom_node.IsValidReverseTarget()) { heap.Insert(phantom_node.reverse_segment_id.id, phantom_node.GetReverseWeightPlusOffset(), {phantom_node.reverse_segment_id.id, phantom_node.GetReverseDuration(), phantom_node.GetReverseDistance()}); } } } template void annotatePath(const FacadeT &facade, const PhantomEndpoints &endpoints, const std::vector &unpacked_nodes, const std::vector &unpacked_edges, std::vector &unpacked_path) { BOOST_ASSERT(!unpacked_nodes.empty()); BOOST_ASSERT(unpacked_nodes.size() == unpacked_edges.size() + 1); const auto source_node_id = unpacked_nodes.front(); const auto target_node_id = unpacked_nodes.back(); const bool start_traversed_in_reverse = endpoints.source_phantom.forward_segment_id.id != source_node_id; const bool target_traversed_in_reverse = endpoints.target_phantom.forward_segment_id.id != target_node_id; BOOST_ASSERT(endpoints.source_phantom.forward_segment_id.id == source_node_id || endpoints.source_phantom.reverse_segment_id.id == source_node_id); BOOST_ASSERT(endpoints.target_phantom.forward_segment_id.id == target_node_id || endpoints.target_phantom.reverse_segment_id.id == target_node_id); // datastructures to hold extracted data from geometry std::vector id_vector; std::vector weight_vector; std::vector duration_vector; std::vector datasource_vector; const auto get_segment_geometry = [&](const auto geometry_index) { const auto copy = [](auto &vector, const auto range) { vector.resize(range.size()); std::copy(range.begin(), range.end(), vector.begin()); }; if (geometry_index.forward) { copy(id_vector, facade.GetUncompressedForwardGeometry(geometry_index.id)); copy(weight_vector, facade.GetUncompressedForwardWeights(geometry_index.id)); copy(duration_vector, facade.GetUncompressedForwardDurations(geometry_index.id)); copy(datasource_vector, facade.GetUncompressedForwardDatasources(geometry_index.id)); } else { copy(id_vector, facade.GetUncompressedReverseGeometry(geometry_index.id)); copy(weight_vector, facade.GetUncompressedReverseWeights(geometry_index.id)); copy(duration_vector, facade.GetUncompressedReverseDurations(geometry_index.id)); copy(datasource_vector, facade.GetUncompressedReverseDatasources(geometry_index.id)); } }; auto node_from = unpacked_nodes.begin(), node_last = std::prev(unpacked_nodes.end()); for (auto edge = unpacked_edges.begin(); node_from != node_last; ++node_from, ++edge) { const auto &edge_data = facade.GetEdgeData(*edge); const auto turn_id = edge_data.turn_id; // edge-based graph edge index const auto node_id = *node_from; // edge-based graph node index const auto geometry_index = facade.GetGeometryIndex(node_id); get_segment_geometry(geometry_index); BOOST_ASSERT(!id_vector.empty()); BOOST_ASSERT(!datasource_vector.empty()); BOOST_ASSERT(weight_vector.size() + 1 == id_vector.size()); BOOST_ASSERT(duration_vector.size() + 1 == id_vector.size()); const bool is_first_segment = unpacked_path.empty(); std::size_t start_index = 0; if (is_first_segment) { unsigned short segment_position = endpoints.source_phantom.fwd_segment_position; if (start_traversed_in_reverse) { segment_position = weight_vector.size() - endpoints.source_phantom.fwd_segment_position - 1; } BOOST_ASSERT(segment_position >= 0); start_index = static_cast(segment_position); } const std::size_t end_index = weight_vector.size(); BOOST_ASSERT(start_index < end_index); for (std::size_t segment_idx = start_index; segment_idx < end_index; ++segment_idx) { unpacked_path.push_back(PathData{node_id, id_vector[segment_idx + 1], alias_cast(weight_vector[segment_idx]), {0}, alias_cast(duration_vector[segment_idx]), {0}, datasource_vector[segment_idx], std::nullopt}); } BOOST_ASSERT(!unpacked_path.empty()); const auto turn_duration = facade.GetDurationPenaltyForEdgeID(turn_id); const auto turn_weight = facade.GetWeightPenaltyForEdgeID(turn_id); unpacked_path.back().duration_until_turn += alias_cast(turn_duration); unpacked_path.back().duration_of_turn = alias_cast(turn_duration); unpacked_path.back().weight_until_turn += alias_cast(turn_weight); unpacked_path.back().weight_of_turn = alias_cast(turn_weight); unpacked_path.back().turn_edge = turn_id; } std::size_t start_index = 0, end_index = 0; const auto source_geometry_id = facade.GetGeometryIndex(source_node_id).id; const auto target_geometry = facade.GetGeometryIndex(target_node_id); const auto is_local_path = source_geometry_id == target_geometry.id && unpacked_path.empty(); get_segment_geometry(target_geometry); if (target_traversed_in_reverse) { if (is_local_path) { start_index = weight_vector.size() - endpoints.source_phantom.fwd_segment_position - 1; } end_index = weight_vector.size() - endpoints.target_phantom.fwd_segment_position - 1; } else { if (is_local_path) { start_index = endpoints.source_phantom.fwd_segment_position; } end_index = endpoints.target_phantom.fwd_segment_position; } // Given the following compressed geometry: // U---v---w---x---y---Z // s t // s: fwd_segment 0 // t: fwd_segment 3 // -> (U, v), (v, w), (w, x) // note that (x, t) is _not_ included but needs to be added later. for (std::size_t segment_idx = start_index; segment_idx != end_index; (start_index < end_index ? ++segment_idx : --segment_idx)) { BOOST_ASSERT(segment_idx < static_cast(id_vector.size() - 1)); unpacked_path.push_back( PathData{target_node_id, id_vector[start_index < end_index ? segment_idx + 1 : segment_idx - 1], alias_cast(weight_vector[segment_idx]), {0}, alias_cast(duration_vector[segment_idx]), {0}, datasource_vector[segment_idx], std::nullopt}); } if (!unpacked_path.empty()) { const auto source_weight = start_traversed_in_reverse ? endpoints.source_phantom.reverse_weight : endpoints.source_phantom.forward_weight; const auto source_duration = start_traversed_in_reverse ? endpoints.source_phantom.reverse_duration : endpoints.source_phantom.forward_duration; // The above code will create segments for (v, w), (w,x), (x, y) and (y, Z). // However the first segment duration needs to be adjusted to the fact that the source // phantom is in the middle of the segment. We do this by subtracting v--s from the // duration. // Since it's possible duration_until_turn can be less than source_weight here if // a negative enough turn penalty is used to modify this edge weight during // osrm-contract, we clamp to 0 here so as not to return a negative duration // for this segment. // TODO this creates a scenario where it's possible the duration from a phantom // node to the first turn would be the same as from end to end of a segment, // which is obviously incorrect and not ideal... unpacked_path.front().weight_until_turn = std::max(unpacked_path.front().weight_until_turn - source_weight, {0}); unpacked_path.front().duration_until_turn = std::max(unpacked_path.front().duration_until_turn - source_duration, {0}); } } template double getPathDistance(const DataFacade &facade, const std::vector &unpacked_path, const PhantomNode &source_phantom, const PhantomNode &target_phantom) { double distance = 0.0; auto prev_coordinate = source_phantom.location; for (const auto &p : unpacked_path) { const auto current_coordinate = facade.GetCoordinateOfNode(p.turn_via_node); distance += util::coordinate_calculation::greatCircleDistance(prev_coordinate, current_coordinate); prev_coordinate = current_coordinate; } distance += util::coordinate_calculation::greatCircleDistance(prev_coordinate, target_phantom.location); return distance; } template InternalRouteResult extractRoute(const DataFacade &facade, const EdgeWeight weight, const PhantomEndpointCandidates &endpoint_candidates, const std::vector &unpacked_nodes, const std::vector &unpacked_edges) { InternalRouteResult raw_route_data; // No path found for both target nodes? if (INVALID_EDGE_WEIGHT == weight) { return raw_route_data; } auto phantom_endpoints = endpointsFromCandidates(endpoint_candidates, unpacked_nodes); raw_route_data.leg_endpoints = {phantom_endpoints}; raw_route_data.shortest_path_weight = weight; raw_route_data.unpacked_path_segments.resize(1); raw_route_data.source_traversed_in_reverse.push_back( (unpacked_nodes.front() != phantom_endpoints.source_phantom.forward_segment_id.id)); raw_route_data.target_traversed_in_reverse.push_back( (unpacked_nodes.back() != phantom_endpoints.target_phantom.forward_segment_id.id)); annotatePath(facade, phantom_endpoints, unpacked_nodes, unpacked_edges, raw_route_data.unpacked_path_segments.front()); return raw_route_data; } template EdgeDistance computeEdgeDistance(const FacadeT &facade, NodeID node_id) { const auto geometry_index = facade.GetGeometryIndex(node_id); EdgeDistance total_distance = {0}; auto geometry_range = facade.GetUncompressedForwardGeometry(geometry_index.id); for (auto current = geometry_range.begin(); current < geometry_range.end() - 1; ++current) { total_distance += util::coordinate_calculation::greatCircleDistance( facade.GetCoordinateOfNode(*current), facade.GetCoordinateOfNode(*std::next(current))); } return total_distance; } } // namespace osrm::engine::routing_algorithms #endif // OSRM_ENGINE_ROUTING_BASE_HPP