422 lines
17 KiB
C++
422 lines
17 KiB
C++
#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 <boost/assert.hpp>
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <iterator>
|
|
#include <memory>
|
|
#include <numeric>
|
|
#include <stack>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace osrm::engine::routing_algorithms
|
|
{
|
|
|
|
namespace details
|
|
{
|
|
template <typename Heap>
|
|
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 <typename Heap>
|
|
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 loop forcing
|
|
// e.g. if source and destination nodes are on the same segment.
|
|
std::vector<NodeID> getForwardLoopNodes(const PhantomEndpointCandidates &candidates);
|
|
std::vector<NodeID> getForwardLoopNodes(const PhantomCandidatesToTarget &candidates);
|
|
std::vector<NodeID> getBackwardLoopNodes(const PhantomEndpointCandidates &candidates);
|
|
std::vector<NodeID> getBackwardLoopNodes(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<NodeID> &path);
|
|
|
|
template <typename HeapNodeT>
|
|
inline bool force_loop(const std::vector<NodeID> &force_nodes, const HeapNodeT &heap_node)
|
|
{
|
|
// if loops are forced, they are so at the source
|
|
return !force_nodes.empty() &&
|
|
std::find(force_nodes.begin(), force_nodes.end(), heap_node.node) != force_nodes.end() &&
|
|
heap_node.data.parent == heap_node.node;
|
|
}
|
|
|
|
template <typename Heap>
|
|
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 <typename Heap>
|
|
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 <typename ManyToManyQueryHeap>
|
|
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 <typename ManyToManyQueryHeap>
|
|
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 <typename FacadeT>
|
|
void annotatePath(const FacadeT &facade,
|
|
const PhantomEndpoints &endpoints,
|
|
const std::vector<NodeID> &unpacked_nodes,
|
|
const std::vector<EdgeID> &unpacked_edges,
|
|
std::vector<PathData> &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<NodeID> id_vector;
|
|
std::vector<SegmentWeight> weight_vector;
|
|
std::vector<SegmentDuration> duration_vector;
|
|
std::vector<DatasourceID> 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<std::size_t>(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<EdgeWeight>(weight_vector[segment_idx]),
|
|
{0},
|
|
alias_cast<EdgeDuration>(duration_vector[segment_idx]),
|
|
{0},
|
|
datasource_vector[segment_idx],
|
|
boost::none});
|
|
}
|
|
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<EdgeDuration>(turn_duration);
|
|
unpacked_path.back().duration_of_turn = alias_cast<EdgeDuration>(turn_duration);
|
|
unpacked_path.back().weight_until_turn += alias_cast<EdgeWeight>(turn_weight);
|
|
unpacked_path.back().weight_of_turn = alias_cast<EdgeWeight>(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<std::size_t>(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<EdgeWeight>(weight_vector[segment_idx]),
|
|
{0},
|
|
alias_cast<EdgeDuration>(duration_vector[segment_idx]),
|
|
{0},
|
|
datasource_vector[segment_idx],
|
|
boost::none});
|
|
}
|
|
|
|
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 <typename Algorithm>
|
|
double getPathDistance(const DataFacade<Algorithm> &facade,
|
|
const std::vector<PathData> &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 <typename AlgorithmT>
|
|
InternalRouteResult extractRoute(const DataFacade<AlgorithmT> &facade,
|
|
const EdgeWeight weight,
|
|
const PhantomEndpointCandidates &endpoint_candidates,
|
|
const std::vector<NodeID> &unpacked_nodes,
|
|
const std::vector<EdgeID> &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 <typename FacadeT> 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
|