renamed: RoutingAlgorithms/* routing_algorithms/
This commit is contained in:
@@ -0,0 +1,904 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ALTERNATIVE_PATH_ROUTING_HPP
|
||||
#define ALTERNATIVE_PATH_ROUTING_HPP
|
||||
|
||||
#include "routing_base.hpp"
|
||||
#include "../data_structures/search_engine_data.hpp"
|
||||
#include "../Util/integer_range.hpp"
|
||||
#include "../Util/container.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <vector>
|
||||
|
||||
const double VIAPATH_ALPHA = 0.10;
|
||||
const double VIAPATH_EPSILON = 0.15; // alternative at most 15% longer
|
||||
const double VIAPATH_GAMMA = 0.75; // alternative shares at most 75% with the shortest.
|
||||
|
||||
template <class DataFacadeT> class AlternativeRouting final : private BasicRoutingInterface<DataFacadeT>
|
||||
{
|
||||
using super = BasicRoutingInterface<DataFacadeT>;
|
||||
using EdgeData = typename DataFacadeT::EdgeData;
|
||||
using QueryHeap = SearchEngineData::QueryHeap;
|
||||
using SearchSpaceEdge = std::pair<NodeID, NodeID>;
|
||||
|
||||
struct RankedCandidateNode
|
||||
{
|
||||
RankedCandidateNode(const NodeID node, const int length, const int sharing)
|
||||
: node(node), length(length), sharing(sharing)
|
||||
{
|
||||
}
|
||||
|
||||
NodeID node;
|
||||
int length;
|
||||
int sharing;
|
||||
|
||||
bool operator<(const RankedCandidateNode &other) const
|
||||
{
|
||||
return (2 * length + sharing) < (2 * other.length + other.sharing);
|
||||
}
|
||||
};
|
||||
DataFacadeT *facade;
|
||||
SearchEngineData &engine_working_data;
|
||||
|
||||
public:
|
||||
AlternativeRouting(DataFacadeT *facade, SearchEngineData &engine_working_data)
|
||||
: super(facade), facade(facade), engine_working_data(engine_working_data)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~AlternativeRouting() {}
|
||||
|
||||
void operator()(const PhantomNodes &phantom_node_pair, RawRouteData &raw_route_data)
|
||||
{
|
||||
std::vector<NodeID> alternative_path;
|
||||
std::vector<NodeID> via_node_candidate_list;
|
||||
std::vector<SearchSpaceEdge> forward_search_space;
|
||||
std::vector<SearchSpaceEdge> reverse_search_space;
|
||||
|
||||
// Init queues, semi-expensive because access to TSS invokes a sys-call
|
||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
engine_working_data.InitializeOrClearSecondThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
engine_working_data.InitializeOrClearThirdThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
|
||||
QueryHeap &forward_heap1 = *(engine_working_data.forwardHeap);
|
||||
QueryHeap &reverse_heap1 = *(engine_working_data.backwardHeap);
|
||||
QueryHeap &forward_heap2 = *(engine_working_data.forwardHeap2);
|
||||
QueryHeap &reverse_heap2 = *(engine_working_data.backwardHeap2);
|
||||
|
||||
int upper_bound_to_shortest_path_distance = INVALID_EDGE_WEIGHT;
|
||||
NodeID middle_node = SPECIAL_NODEID;
|
||||
EdgeWeight min_edge_offset =
|
||||
std::min(0, -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset());
|
||||
min_edge_offset = std::min(min_edge_offset,
|
||||
-phantom_node_pair.source_phantom.GetReverseWeightPlusOffset());
|
||||
|
||||
if (phantom_node_pair.source_phantom.forward_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
// SimpleLogger().Write(logDEBUG) << "fwd-a insert: " <<
|
||||
// phantom_node_pair.source_phantom.forward_node_id << ", w: " <<
|
||||
// -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset();
|
||||
forward_heap1.Insert(phantom_node_pair.source_phantom.forward_node_id,
|
||||
-phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(),
|
||||
phantom_node_pair.source_phantom.forward_node_id);
|
||||
}
|
||||
if (phantom_node_pair.source_phantom.reverse_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
// SimpleLogger().Write(logDEBUG) << "fwd-b insert: " <<
|
||||
// phantom_node_pair.source_phantom.reverse_node_id << ", w: " <<
|
||||
// -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset();
|
||||
forward_heap1.Insert(phantom_node_pair.source_phantom.reverse_node_id,
|
||||
-phantom_node_pair.source_phantom.GetReverseWeightPlusOffset(),
|
||||
phantom_node_pair.source_phantom.reverse_node_id);
|
||||
}
|
||||
|
||||
if (phantom_node_pair.target_phantom.forward_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
// SimpleLogger().Write(logDEBUG) << "rev-a insert: " <<
|
||||
// phantom_node_pair.target_phantom.forward_node_id << ", w: " <<
|
||||
// phantom_node_pair.target_phantom.GetForwardWeightPlusOffset();
|
||||
reverse_heap1.Insert(phantom_node_pair.target_phantom.forward_node_id,
|
||||
phantom_node_pair.target_phantom.GetForwardWeightPlusOffset(),
|
||||
phantom_node_pair.target_phantom.forward_node_id);
|
||||
}
|
||||
if (phantom_node_pair.target_phantom.reverse_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
// SimpleLogger().Write(logDEBUG) << "rev-b insert: " <<
|
||||
// phantom_node_pair.target_phantom.reverse_node_id << ", w: " <<
|
||||
// phantom_node_pair.target_phantom.GetReverseWeightPlusOffset();
|
||||
reverse_heap1.Insert(phantom_node_pair.target_phantom.reverse_node_id,
|
||||
phantom_node_pair.target_phantom.GetReverseWeightPlusOffset(),
|
||||
phantom_node_pair.target_phantom.reverse_node_id);
|
||||
}
|
||||
|
||||
// search from s and t till new_min/(1+epsilon) > length_of_shortest_path
|
||||
while (0 < (forward_heap1.Size() + reverse_heap1.Size()))
|
||||
{
|
||||
if (0 < forward_heap1.Size())
|
||||
{
|
||||
AlternativeRoutingStep<true>(forward_heap1,
|
||||
reverse_heap1,
|
||||
&middle_node,
|
||||
&upper_bound_to_shortest_path_distance,
|
||||
via_node_candidate_list,
|
||||
forward_search_space,
|
||||
min_edge_offset);
|
||||
}
|
||||
if (0 < reverse_heap1.Size())
|
||||
{
|
||||
AlternativeRoutingStep<false>(reverse_heap1,
|
||||
forward_heap1,
|
||||
&middle_node,
|
||||
&upper_bound_to_shortest_path_distance,
|
||||
via_node_candidate_list,
|
||||
reverse_search_space,
|
||||
min_edge_offset);
|
||||
}
|
||||
}
|
||||
|
||||
if (INVALID_EDGE_WEIGHT == upper_bound_to_shortest_path_distance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
osrm::sort_unique_resize(via_node_candidate_list);
|
||||
|
||||
std::vector<NodeID> packed_forward_path;
|
||||
std::vector<NodeID> packed_reverse_path;
|
||||
|
||||
super::RetrievePackedPathFromSingleHeap(forward_heap1, middle_node, packed_forward_path);
|
||||
super::RetrievePackedPathFromSingleHeap(reverse_heap1, middle_node, packed_reverse_path);
|
||||
|
||||
// this set is is used as an indicator if a node is on the shortest path
|
||||
std::unordered_set<NodeID> nodes_in_path(packed_forward_path.size() +
|
||||
packed_reverse_path.size());
|
||||
nodes_in_path.insert(packed_forward_path.begin(), packed_forward_path.end());
|
||||
nodes_in_path.insert(middle_node);
|
||||
nodes_in_path.insert(packed_reverse_path.begin(), packed_reverse_path.end());
|
||||
|
||||
std::unordered_map<NodeID, int> approximated_forward_sharing;
|
||||
std::unordered_map<NodeID, int> approximated_reverse_sharing;
|
||||
|
||||
// sweep over search space, compute forward sharing for each current edge (u,v)
|
||||
for (const SearchSpaceEdge ¤t_edge : forward_search_space)
|
||||
{
|
||||
const NodeID u = current_edge.first;
|
||||
const NodeID v = current_edge.second;
|
||||
|
||||
if (nodes_in_path.find(v) != nodes_in_path.end())
|
||||
{
|
||||
// current_edge is on shortest path => sharing(v):=queue.GetKey(v);
|
||||
approximated_forward_sharing.emplace(v, forward_heap1.GetKey(v));
|
||||
}
|
||||
else
|
||||
{
|
||||
// current edge is not on shortest path. Check if we know a value for the other
|
||||
// endpoint
|
||||
const auto sharing_of_u_iterator = approximated_forward_sharing.find(u);
|
||||
if (sharing_of_u_iterator != approximated_forward_sharing.end())
|
||||
{
|
||||
approximated_forward_sharing.emplace(v, sharing_of_u_iterator->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// sweep over search space, compute backward sharing
|
||||
for (const SearchSpaceEdge ¤t_edge : reverse_search_space)
|
||||
{
|
||||
const NodeID u = current_edge.first;
|
||||
const NodeID v = current_edge.second;
|
||||
if (nodes_in_path.find(v) != nodes_in_path.end())
|
||||
{
|
||||
// current_edge is on shortest path => sharing(u):=queue.GetKey(u);
|
||||
approximated_reverse_sharing.emplace(v, reverse_heap1.GetKey(v));
|
||||
}
|
||||
else
|
||||
{
|
||||
// current edge is not on shortest path. Check if we know a value for the other
|
||||
// endpoint
|
||||
const auto sharing_of_u_iterator = approximated_reverse_sharing.find(u);
|
||||
if (sharing_of_u_iterator != approximated_reverse_sharing.end())
|
||||
{
|
||||
approximated_reverse_sharing.emplace(v, sharing_of_u_iterator->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// SimpleLogger().Write(logDEBUG) << "fwd_search_space size: " <<
|
||||
// forward_search_space.size() << ", marked " << approximated_forward_sharing.size() << "
|
||||
// nodes";
|
||||
// SimpleLogger().Write(logDEBUG) << "rev_search_space size: " <<
|
||||
// reverse_search_space.size() << ", marked " << approximated_reverse_sharing.size() << "
|
||||
// nodes";
|
||||
|
||||
std::vector<NodeID> preselected_node_list;
|
||||
for (const NodeID node : via_node_candidate_list)
|
||||
{
|
||||
const auto fwd_iterator = approximated_forward_sharing.find(node);
|
||||
const int fwd_sharing =
|
||||
(fwd_iterator != approximated_forward_sharing.end()) ? fwd_iterator->second : 0;
|
||||
const auto rev_iterator = approximated_reverse_sharing.find(node);
|
||||
const int rev_sharing =
|
||||
(rev_iterator != approximated_reverse_sharing.end()) ? rev_iterator->second : 0;
|
||||
|
||||
const int approximated_sharing = fwd_sharing + rev_sharing;
|
||||
const int approximated_length = forward_heap1.GetKey(node) + reverse_heap1.GetKey(node);
|
||||
const bool length_passes =
|
||||
(approximated_length <
|
||||
upper_bound_to_shortest_path_distance * (1 + VIAPATH_EPSILON));
|
||||
const bool sharing_passes =
|
||||
(approximated_sharing <= upper_bound_to_shortest_path_distance * VIAPATH_GAMMA);
|
||||
const bool stretch_passes =
|
||||
(approximated_length - approximated_sharing) <
|
||||
((1. + VIAPATH_ALPHA) *
|
||||
(upper_bound_to_shortest_path_distance - approximated_sharing));
|
||||
|
||||
if (length_passes && sharing_passes && stretch_passes)
|
||||
{
|
||||
preselected_node_list.emplace_back(node);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<NodeID> &packed_shortest_path = packed_forward_path;
|
||||
std::reverse(packed_shortest_path.begin(), packed_shortest_path.end());
|
||||
packed_shortest_path.emplace_back(middle_node);
|
||||
packed_shortest_path.insert(
|
||||
packed_shortest_path.end(), packed_reverse_path.begin(), packed_reverse_path.end());
|
||||
std::vector<RankedCandidateNode> ranked_candidates_list;
|
||||
|
||||
// prioritizing via nodes for deep inspection
|
||||
for (const NodeID node : preselected_node_list)
|
||||
{
|
||||
int length_of_via_path = 0, sharing_of_via_path = 0;
|
||||
ComputeLengthAndSharingOfViaPath(node,
|
||||
&length_of_via_path,
|
||||
&sharing_of_via_path,
|
||||
packed_shortest_path,
|
||||
min_edge_offset);
|
||||
const int maximum_allowed_sharing =
|
||||
static_cast<int>(upper_bound_to_shortest_path_distance * VIAPATH_GAMMA);
|
||||
if (sharing_of_via_path <= maximum_allowed_sharing &&
|
||||
length_of_via_path <= upper_bound_to_shortest_path_distance * (1 + VIAPATH_EPSILON))
|
||||
{
|
||||
ranked_candidates_list.emplace_back(node, length_of_via_path, sharing_of_via_path);
|
||||
}
|
||||
}
|
||||
std::sort(ranked_candidates_list.begin(), ranked_candidates_list.end());
|
||||
|
||||
NodeID selected_via_node = SPECIAL_NODEID;
|
||||
int length_of_via_path = INVALID_EDGE_WEIGHT;
|
||||
NodeID s_v_middle = SPECIAL_NODEID, v_t_middle = SPECIAL_NODEID;
|
||||
for (const RankedCandidateNode &candidate : ranked_candidates_list)
|
||||
{
|
||||
if (ViaNodeCandidatePassesTTest(forward_heap1,
|
||||
reverse_heap1,
|
||||
forward_heap2,
|
||||
reverse_heap2,
|
||||
candidate,
|
||||
upper_bound_to_shortest_path_distance,
|
||||
&length_of_via_path,
|
||||
&s_v_middle,
|
||||
&v_t_middle,
|
||||
min_edge_offset))
|
||||
{
|
||||
// select first admissable
|
||||
selected_via_node = candidate.node;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Unpack shortest path and alternative, if they exist
|
||||
if (INVALID_EDGE_WEIGHT != upper_bound_to_shortest_path_distance)
|
||||
{
|
||||
BOOST_ASSERT(!packed_shortest_path.empty());
|
||||
raw_route_data.unpacked_path_segments.resize(1);
|
||||
raw_route_data.source_traversed_in_reverse.push_back(
|
||||
(packed_shortest_path.front() != phantom_node_pair.source_phantom.forward_node_id));
|
||||
raw_route_data.target_traversed_in_reverse.push_back(
|
||||
(packed_shortest_path.back() != phantom_node_pair.target_phantom.forward_node_id));
|
||||
|
||||
super::UnpackPath(
|
||||
// -- packed input
|
||||
packed_shortest_path,
|
||||
// -- start of route
|
||||
phantom_node_pair,
|
||||
// -- unpacked output
|
||||
raw_route_data.unpacked_path_segments.front());
|
||||
raw_route_data.shortest_path_length = upper_bound_to_shortest_path_distance;
|
||||
}
|
||||
|
||||
if (SPECIAL_NODEID != selected_via_node)
|
||||
{
|
||||
std::vector<NodeID> packed_alternate_path;
|
||||
// retrieve alternate path
|
||||
RetrievePackedAlternatePath(forward_heap1,
|
||||
reverse_heap1,
|
||||
forward_heap2,
|
||||
reverse_heap2,
|
||||
s_v_middle,
|
||||
v_t_middle,
|
||||
packed_alternate_path);
|
||||
|
||||
raw_route_data.alt_source_traversed_in_reverse.push_back((
|
||||
packed_alternate_path.front() != phantom_node_pair.source_phantom.forward_node_id));
|
||||
raw_route_data.alt_target_traversed_in_reverse.push_back(
|
||||
(packed_alternate_path.back() != phantom_node_pair.target_phantom.forward_node_id));
|
||||
|
||||
// unpack the alternate path
|
||||
super::UnpackPath(
|
||||
packed_alternate_path, phantom_node_pair, raw_route_data.unpacked_alternative);
|
||||
|
||||
raw_route_data.alternative_path_length = length_of_via_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(raw_route_data.alternative_path_length == INVALID_EDGE_WEIGHT);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// unpack alternate <s,..,v,..,t> by exploring search spaces from v
|
||||
inline void RetrievePackedAlternatePath(const QueryHeap &forward_heap1,
|
||||
const QueryHeap &reverse_heap1,
|
||||
const QueryHeap &forward_heap2,
|
||||
const QueryHeap &reverse_heap2,
|
||||
const NodeID s_v_middle,
|
||||
const NodeID v_t_middle,
|
||||
std::vector<NodeID> &packed_path) const
|
||||
{
|
||||
// fetch packed path [s,v)
|
||||
std::vector<NodeID> packed_v_t_path;
|
||||
super::RetrievePackedPathFromHeap(forward_heap1, reverse_heap2, s_v_middle, packed_path);
|
||||
packed_path.pop_back(); // remove middle node. It's in both half-paths
|
||||
|
||||
// fetch patched path [v,t]
|
||||
super::RetrievePackedPathFromHeap(
|
||||
forward_heap2, reverse_heap1, v_t_middle, packed_v_t_path);
|
||||
|
||||
packed_path.insert(packed_path.end(), packed_v_t_path.begin(), packed_v_t_path.end());
|
||||
}
|
||||
|
||||
// TODO: reorder parameters
|
||||
// compute and unpack <s,..,v> and <v,..,t> by exploring search spaces
|
||||
// from v and intersecting against queues. only half-searches have to be
|
||||
// done at this stage
|
||||
inline void ComputeLengthAndSharingOfViaPath(const NodeID via_node,
|
||||
int *real_length_of_via_path,
|
||||
int *sharing_of_via_path,
|
||||
const std::vector<NodeID> &packed_shortest_path,
|
||||
const EdgeWeight min_edge_offset)
|
||||
{
|
||||
engine_working_data.InitializeOrClearSecondThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
|
||||
QueryHeap &existing_forward_heap = *engine_working_data.forwardHeap;
|
||||
QueryHeap &existing_reverse_heap = *engine_working_data.backwardHeap;
|
||||
QueryHeap &new_forward_heap = *engine_working_data.forwardHeap2;
|
||||
QueryHeap &new_reverse_heap = *engine_working_data.backwardHeap2;
|
||||
|
||||
std::vector<NodeID> packed_s_v_path;
|
||||
std::vector<NodeID> packed_v_t_path;
|
||||
|
||||
std::vector<NodeID> partially_unpacked_shortest_path;
|
||||
std::vector<NodeID> partially_unpacked_via_path;
|
||||
|
||||
NodeID s_v_middle = SPECIAL_NODEID;
|
||||
int upper_bound_s_v_path_length = INVALID_EDGE_WEIGHT;
|
||||
new_reverse_heap.Insert(via_node, 0, via_node);
|
||||
// compute path <s,..,v> by reusing forward search from s
|
||||
while (!new_reverse_heap.Empty())
|
||||
{
|
||||
super::RoutingStep(new_reverse_heap,
|
||||
existing_forward_heap,
|
||||
&s_v_middle,
|
||||
&upper_bound_s_v_path_length,
|
||||
min_edge_offset,
|
||||
false);
|
||||
}
|
||||
// compute path <v,..,t> by reusing backward search from node t
|
||||
NodeID v_t_middle = SPECIAL_NODEID;
|
||||
int upper_bound_of_v_t_path_length = INVALID_EDGE_WEIGHT;
|
||||
new_forward_heap.Insert(via_node, 0, via_node);
|
||||
while (!new_forward_heap.Empty())
|
||||
{
|
||||
super::RoutingStep(new_forward_heap,
|
||||
existing_reverse_heap,
|
||||
&v_t_middle,
|
||||
&upper_bound_of_v_t_path_length,
|
||||
min_edge_offset,
|
||||
true);
|
||||
}
|
||||
*real_length_of_via_path = upper_bound_s_v_path_length + upper_bound_of_v_t_path_length;
|
||||
|
||||
if (SPECIAL_NODEID == s_v_middle || SPECIAL_NODEID == v_t_middle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// retrieve packed paths
|
||||
super::RetrievePackedPathFromHeap(
|
||||
existing_forward_heap, new_reverse_heap, s_v_middle, packed_s_v_path);
|
||||
super::RetrievePackedPathFromHeap(
|
||||
new_forward_heap, existing_reverse_heap, v_t_middle, packed_v_t_path);
|
||||
|
||||
// partial unpacking, compute sharing
|
||||
// First partially unpack s-->v until paths deviate, note length of common path.
|
||||
const int64_t s_v_min_path_size =
|
||||
std::min(packed_s_v_path.size(), packed_shortest_path.size()) - 1;
|
||||
for (const int64_t current_node : osrm::irange<int64_t>(0, s_v_min_path_size))
|
||||
{
|
||||
if (packed_s_v_path[current_node] == packed_shortest_path[current_node] &&
|
||||
packed_s_v_path[current_node + 1] == packed_shortest_path[current_node + 1])
|
||||
{
|
||||
EdgeID edgeID = facade->FindEdgeInEitherDirection(
|
||||
packed_s_v_path[current_node], packed_s_v_path[current_node + 1]);
|
||||
*sharing_of_via_path += facade->GetEdgeData(edgeID).distance;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (packed_s_v_path[current_node] == packed_shortest_path[current_node])
|
||||
{
|
||||
super::UnpackEdge(packed_s_v_path[current_node],
|
||||
packed_s_v_path[current_node + 1],
|
||||
partially_unpacked_via_path);
|
||||
super::UnpackEdge(packed_shortest_path[current_node],
|
||||
packed_shortest_path[current_node + 1],
|
||||
partially_unpacked_shortest_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// traverse partially unpacked edge and note common prefix
|
||||
const int64_t packed_path_length =
|
||||
std::min(partially_unpacked_via_path.size(), partially_unpacked_shortest_path.size()) -
|
||||
1;
|
||||
for (int64_t current_node = 0;
|
||||
(current_node < packed_path_length) &&
|
||||
(partially_unpacked_via_path[current_node] ==
|
||||
partially_unpacked_shortest_path[current_node] &&
|
||||
partially_unpacked_via_path[current_node + 1] ==
|
||||
partially_unpacked_shortest_path[current_node + 1]);
|
||||
++current_node)
|
||||
{
|
||||
EdgeID selected_edge =
|
||||
facade->FindEdgeInEitherDirection(partially_unpacked_via_path[current_node],
|
||||
partially_unpacked_via_path[current_node + 1]);
|
||||
*sharing_of_via_path += facade->GetEdgeData(selected_edge).distance;
|
||||
}
|
||||
|
||||
// Second, partially unpack v-->t in reverse order until paths deviate and note lengths
|
||||
int64_t via_path_index = packed_v_t_path.size() - 1;
|
||||
int64_t shortest_path_index = packed_shortest_path.size() - 1;
|
||||
for (; via_path_index > 0 && shortest_path_index > 0;
|
||||
--via_path_index, --shortest_path_index)
|
||||
{
|
||||
if (packed_v_t_path[via_path_index - 1] ==
|
||||
packed_shortest_path[shortest_path_index - 1] &&
|
||||
packed_v_t_path[via_path_index] == packed_shortest_path[shortest_path_index])
|
||||
{
|
||||
EdgeID edgeID = facade->FindEdgeInEitherDirection(
|
||||
packed_v_t_path[via_path_index - 1], packed_v_t_path[via_path_index]);
|
||||
*sharing_of_via_path += facade->GetEdgeData(edgeID).distance;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (packed_v_t_path[via_path_index] == packed_shortest_path[shortest_path_index])
|
||||
{
|
||||
super::UnpackEdge(packed_v_t_path[via_path_index - 1],
|
||||
packed_v_t_path[via_path_index],
|
||||
partially_unpacked_via_path);
|
||||
super::UnpackEdge(packed_shortest_path[shortest_path_index - 1],
|
||||
packed_shortest_path[shortest_path_index],
|
||||
partially_unpacked_shortest_path);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
via_path_index = partially_unpacked_via_path.size() - 1;
|
||||
shortest_path_index = partially_unpacked_shortest_path.size() - 1;
|
||||
for (; via_path_index > 0 && shortest_path_index > 0;
|
||||
--via_path_index, --shortest_path_index)
|
||||
{
|
||||
if (partially_unpacked_via_path[via_path_index - 1] ==
|
||||
partially_unpacked_shortest_path[shortest_path_index - 1] &&
|
||||
partially_unpacked_via_path[via_path_index] ==
|
||||
partially_unpacked_shortest_path[shortest_path_index])
|
||||
{
|
||||
EdgeID edgeID = facade->FindEdgeInEitherDirection(
|
||||
partially_unpacked_via_path[via_path_index - 1],
|
||||
partially_unpacked_via_path[via_path_index]);
|
||||
*sharing_of_via_path += facade->GetEdgeData(edgeID).distance;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// finished partial unpacking spree! Amount of sharing is stored to appropriate pointer
|
||||
// variable
|
||||
}
|
||||
|
||||
// inline int approximateAmountOfSharing(
|
||||
// const NodeID alternate_path_middle_node_id,
|
||||
// QueryHeap & forward_heap,
|
||||
// QueryHeap & reverse_heap,
|
||||
// const std::vector<NodeID> & packed_shortest_path
|
||||
// ) const {
|
||||
// std::vector<NodeID> packed_alternate_path;
|
||||
// super::RetrievePackedPathFromHeap(
|
||||
// forward_heap,
|
||||
// reverse_heap,
|
||||
// alternate_path_middle_node_id,
|
||||
// packed_alternate_path
|
||||
// );
|
||||
|
||||
// if(packed_shortest_path.size() < 2 || packed_alternate_path.size() < 2) {
|
||||
// return 0;
|
||||
// }
|
||||
|
||||
// int sharing = 0;
|
||||
// int aindex = 0;
|
||||
// //compute forward sharing
|
||||
// while( (packed_alternate_path[aindex] == packed_shortest_path[aindex]) &&
|
||||
// (packed_alternate_path[aindex+1] == packed_shortest_path[aindex+1]) ) {
|
||||
// // SimpleLogger().Write() << "retrieving edge (" <<
|
||||
// packed_alternate_path[aindex] << "," << packed_alternate_path[aindex+1] << ")";
|
||||
// EdgeID edgeID = facade->FindEdgeInEitherDirection(packed_alternate_path[aindex],
|
||||
// packed_alternate_path[aindex+1]);
|
||||
// sharing += facade->GetEdgeData(edgeID).distance;
|
||||
// ++aindex;
|
||||
// }
|
||||
|
||||
// aindex = packed_alternate_path.size()-1;
|
||||
// int bindex = packed_shortest_path.size()-1;
|
||||
// //compute backward sharing
|
||||
// while( aindex > 0 && bindex > 0 && (packed_alternate_path[aindex] ==
|
||||
// packed_shortest_path[bindex]) && (packed_alternate_path[aindex-1] ==
|
||||
// packed_shortest_path[bindex-1]) ) {
|
||||
// EdgeID edgeID = facade->FindEdgeInEitherDirection(packed_alternate_path[aindex],
|
||||
// packed_alternate_path[aindex-1]);
|
||||
// sharing += facade->GetEdgeData(edgeID).distance;
|
||||
// --aindex; --bindex;
|
||||
// }
|
||||
// return sharing;
|
||||
// }
|
||||
|
||||
// todo: reorder parameters
|
||||
template <bool is_forward_directed>
|
||||
inline void AlternativeRoutingStep(QueryHeap &forward_heap,
|
||||
QueryHeap &reverse_heap,
|
||||
NodeID *middle_node,
|
||||
int *upper_bound_to_shortest_path_distance,
|
||||
std::vector<NodeID> &search_space_intersection,
|
||||
std::vector<SearchSpaceEdge> &search_space,
|
||||
const EdgeWeight min_edge_offset) const
|
||||
{
|
||||
const NodeID node = forward_heap.DeleteMin();
|
||||
const int distance = forward_heap.GetKey(node);
|
||||
// const NodeID parentnode = forward_heap.GetData(node).parent;
|
||||
// SimpleLogger().Write() << (is_forward_directed ? "[fwd] " : "[rev] ") << "settled edge ("
|
||||
// << parentnode << "," << node << "), dist: " << distance;
|
||||
|
||||
const int scaled_distance =
|
||||
static_cast<int>((distance + min_edge_offset) / (1. + VIAPATH_EPSILON));
|
||||
if ((INVALID_EDGE_WEIGHT != *upper_bound_to_shortest_path_distance) &&
|
||||
(scaled_distance > *upper_bound_to_shortest_path_distance))
|
||||
{
|
||||
forward_heap.DeleteAll();
|
||||
return;
|
||||
}
|
||||
|
||||
search_space.emplace_back(forward_heap.GetData(node).parent, node);
|
||||
|
||||
if (reverse_heap.WasInserted(node))
|
||||
{
|
||||
search_space_intersection.emplace_back(node);
|
||||
const int new_distance = reverse_heap.GetKey(node) + distance;
|
||||
if (new_distance < *upper_bound_to_shortest_path_distance)
|
||||
{
|
||||
if (new_distance >= 0)
|
||||
{
|
||||
*middle_node = node;
|
||||
*upper_bound_to_shortest_path_distance = new_distance;
|
||||
// SimpleLogger().Write() << "accepted middle_node " << *middle_node << " at
|
||||
// distance " << new_distance;
|
||||
// } else {
|
||||
// SimpleLogger().Write() << "discarded middle_node " << *middle_node << "
|
||||
// at distance " << new_distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto edge : facade->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const EdgeData &data = facade->GetEdgeData(edge);
|
||||
const bool edge_is_forward_directed =
|
||||
(is_forward_directed ? data.forward : data.backward);
|
||||
if (edge_is_forward_directed)
|
||||
{
|
||||
|
||||
const NodeID to = facade->GetTarget(edge);
|
||||
const int edge_weight = data.distance;
|
||||
|
||||
BOOST_ASSERT(edge_weight > 0);
|
||||
const int to_distance = distance + edge_weight;
|
||||
|
||||
// New Node discovered -> Add to Heap + Node Info Storage
|
||||
if (!forward_heap.WasInserted(to))
|
||||
{
|
||||
forward_heap.Insert(to, to_distance, node);
|
||||
}
|
||||
// Found a shorter Path -> Update distance
|
||||
else if (to_distance < forward_heap.GetKey(to))
|
||||
{
|
||||
// new parent
|
||||
forward_heap.GetData(to).parent = node;
|
||||
// decreased distance
|
||||
forward_heap.DecreaseKey(to, to_distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// conduct T-Test
|
||||
inline bool ViaNodeCandidatePassesTTest(QueryHeap &existing_forward_heap,
|
||||
QueryHeap &existing_reverse_heap,
|
||||
QueryHeap &new_forward_heap,
|
||||
QueryHeap &new_reverse_heap,
|
||||
const RankedCandidateNode &candidate,
|
||||
const int length_of_shortest_path,
|
||||
int *length_of_via_path,
|
||||
NodeID *s_v_middle,
|
||||
NodeID *v_t_middle,
|
||||
const EdgeWeight min_edge_offset) const
|
||||
{
|
||||
new_forward_heap.Clear();
|
||||
new_reverse_heap.Clear();
|
||||
std::vector<NodeID> packed_s_v_path;
|
||||
std::vector<NodeID> packed_v_t_path;
|
||||
|
||||
*s_v_middle = SPECIAL_NODEID;
|
||||
int upper_bound_s_v_path_length = INVALID_EDGE_WEIGHT;
|
||||
// compute path <s,..,v> by reusing forward search from s
|
||||
new_reverse_heap.Insert(candidate.node, 0, candidate.node);
|
||||
while (new_reverse_heap.Size() > 0)
|
||||
{
|
||||
super::RoutingStep(new_reverse_heap,
|
||||
existing_forward_heap,
|
||||
s_v_middle,
|
||||
&upper_bound_s_v_path_length,
|
||||
min_edge_offset,
|
||||
false);
|
||||
}
|
||||
|
||||
if (INVALID_EDGE_WEIGHT == upper_bound_s_v_path_length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// compute path <v,..,t> by reusing backward search from t
|
||||
*v_t_middle = SPECIAL_NODEID;
|
||||
int upper_bound_of_v_t_path_length = INVALID_EDGE_WEIGHT;
|
||||
new_forward_heap.Insert(candidate.node, 0, candidate.node);
|
||||
while (new_forward_heap.Size() > 0)
|
||||
{
|
||||
super::RoutingStep(new_forward_heap,
|
||||
existing_reverse_heap,
|
||||
v_t_middle,
|
||||
&upper_bound_of_v_t_path_length,
|
||||
min_edge_offset,
|
||||
true);
|
||||
}
|
||||
|
||||
if (INVALID_EDGE_WEIGHT == upper_bound_of_v_t_path_length)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*length_of_via_path = upper_bound_s_v_path_length + upper_bound_of_v_t_path_length;
|
||||
|
||||
// retrieve packed paths
|
||||
super::RetrievePackedPathFromHeap(
|
||||
existing_forward_heap, new_reverse_heap, *s_v_middle, packed_s_v_path);
|
||||
|
||||
super::RetrievePackedPathFromHeap(
|
||||
new_forward_heap, existing_reverse_heap, *v_t_middle, packed_v_t_path);
|
||||
|
||||
NodeID s_P = *s_v_middle, t_P = *v_t_middle;
|
||||
if (SPECIAL_NODEID == s_P)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (SPECIAL_NODEID == t_P)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const int T_threshold = static_cast<int>(VIAPATH_EPSILON * length_of_shortest_path);
|
||||
int unpacked_until_distance = 0;
|
||||
|
||||
std::stack<SearchSpaceEdge> unpack_stack;
|
||||
// Traverse path s-->v
|
||||
for (std::size_t i = packed_s_v_path.size() - 1; (i > 0) && unpack_stack.empty(); --i)
|
||||
{
|
||||
const EdgeID current_edge_id =
|
||||
facade->FindEdgeInEitherDirection(packed_s_v_path[i - 1], packed_s_v_path[i]);
|
||||
const int length_of_current_edge = facade->GetEdgeData(current_edge_id).distance;
|
||||
if ((length_of_current_edge + unpacked_until_distance) >= T_threshold)
|
||||
{
|
||||
unpack_stack.emplace(packed_s_v_path[i - 1], packed_s_v_path[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
unpacked_until_distance += length_of_current_edge;
|
||||
s_P = packed_s_v_path[i - 1];
|
||||
}
|
||||
}
|
||||
|
||||
while (!unpack_stack.empty())
|
||||
{
|
||||
const SearchSpaceEdge via_path_edge = unpack_stack.top();
|
||||
unpack_stack.pop();
|
||||
EdgeID edge_in_via_path_id =
|
||||
facade->FindEdgeInEitherDirection(via_path_edge.first, via_path_edge.second);
|
||||
|
||||
if (SPECIAL_EDGEID == edge_in_via_path_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const EdgeData ¤t_edge_data = facade->GetEdgeData(edge_in_via_path_id);
|
||||
const bool current_edge_is_shortcut = current_edge_data.shortcut;
|
||||
if (current_edge_is_shortcut)
|
||||
{
|
||||
const NodeID via_path_middle_node_id = current_edge_data.id;
|
||||
const EdgeID second_segment_edge_id = facade->FindEdgeInEitherDirection(
|
||||
via_path_middle_node_id, via_path_edge.second);
|
||||
const int second_segment_length =
|
||||
facade->GetEdgeData(second_segment_edge_id).distance;
|
||||
// attention: !unpacking in reverse!
|
||||
// Check if second segment is the one to go over treshold? if yes add second segment
|
||||
// to stack, else push first segment to stack and add distance of second one.
|
||||
if (unpacked_until_distance + second_segment_length >= T_threshold)
|
||||
{
|
||||
unpack_stack.emplace(via_path_middle_node_id, via_path_edge.second);
|
||||
}
|
||||
else
|
||||
{
|
||||
unpacked_until_distance += second_segment_length;
|
||||
unpack_stack.emplace(via_path_edge.first, via_path_middle_node_id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// edge is not a shortcut, set the start node for T-Test to end of edge.
|
||||
unpacked_until_distance += current_edge_data.distance;
|
||||
s_P = via_path_edge.first;
|
||||
}
|
||||
}
|
||||
|
||||
int t_test_path_length = unpacked_until_distance;
|
||||
unpacked_until_distance = 0;
|
||||
// Traverse path s-->v
|
||||
BOOST_ASSERT(!packed_v_t_path.empty());
|
||||
for (unsigned i = 0, packed_path_length = static_cast<unsigned>(packed_v_t_path.size() - 1);
|
||||
(i < packed_path_length) && unpack_stack.empty();
|
||||
++i)
|
||||
{
|
||||
const EdgeID edgeID =
|
||||
facade->FindEdgeInEitherDirection(packed_v_t_path[i], packed_v_t_path[i + 1]);
|
||||
int length_of_current_edge = facade->GetEdgeData(edgeID).distance;
|
||||
if (length_of_current_edge + unpacked_until_distance >= T_threshold)
|
||||
{
|
||||
unpack_stack.emplace(packed_v_t_path[i], packed_v_t_path[i + 1]);
|
||||
}
|
||||
else
|
||||
{
|
||||
unpacked_until_distance += length_of_current_edge;
|
||||
t_P = packed_v_t_path[i + 1];
|
||||
}
|
||||
}
|
||||
|
||||
while (!unpack_stack.empty())
|
||||
{
|
||||
const SearchSpaceEdge via_path_edge = unpack_stack.top();
|
||||
unpack_stack.pop();
|
||||
EdgeID edge_in_via_path_id =
|
||||
facade->FindEdgeInEitherDirection(via_path_edge.first, via_path_edge.second);
|
||||
if (SPECIAL_EDGEID == edge_in_via_path_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const EdgeData ¤t_edge_data = facade->GetEdgeData(edge_in_via_path_id);
|
||||
const bool IsViaEdgeShortCut = current_edge_data.shortcut;
|
||||
if (IsViaEdgeShortCut)
|
||||
{
|
||||
const NodeID middleOfViaPath = current_edge_data.id;
|
||||
EdgeID edgeIDOfFirstSegment =
|
||||
facade->FindEdgeInEitherDirection(via_path_edge.first, middleOfViaPath);
|
||||
int lengthOfFirstSegment = facade->GetEdgeData(edgeIDOfFirstSegment).distance;
|
||||
// Check if first segment is the one to go over treshold? if yes first segment to
|
||||
// stack, else push second segment to stack and add distance of first one.
|
||||
if (unpacked_until_distance + lengthOfFirstSegment >= T_threshold)
|
||||
{
|
||||
unpack_stack.emplace(via_path_edge.first, middleOfViaPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
unpacked_until_distance += lengthOfFirstSegment;
|
||||
unpack_stack.emplace(middleOfViaPath, via_path_edge.second);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// edge is not a shortcut, set the start node for T-Test to end of edge.
|
||||
unpacked_until_distance += current_edge_data.distance;
|
||||
t_P = via_path_edge.second;
|
||||
}
|
||||
}
|
||||
|
||||
t_test_path_length += unpacked_until_distance;
|
||||
// Run actual T-Test query and compare if distances equal.
|
||||
engine_working_data.InitializeOrClearThirdThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
|
||||
QueryHeap &forward_heap3 = *engine_working_data.forwardHeap3;
|
||||
QueryHeap &reverse_heap3 = *engine_working_data.backwardHeap3;
|
||||
int upper_bound = INVALID_EDGE_WEIGHT;
|
||||
NodeID middle = SPECIAL_NODEID;
|
||||
|
||||
forward_heap3.Insert(s_P, 0, s_P);
|
||||
reverse_heap3.Insert(t_P, 0, t_P);
|
||||
// exploration from s and t until deletemin/(1+epsilon) > _lengt_oO_sShortest_path
|
||||
while ((forward_heap3.Size() + reverse_heap3.Size()) > 0)
|
||||
{
|
||||
if (!forward_heap3.Empty())
|
||||
{
|
||||
super::RoutingStep(
|
||||
forward_heap3, reverse_heap3, &middle, &upper_bound, min_edge_offset, true);
|
||||
}
|
||||
if (!reverse_heap3.Empty())
|
||||
{
|
||||
super::RoutingStep(
|
||||
reverse_heap3, forward_heap3, &middle, &upper_bound, min_edge_offset, false);
|
||||
}
|
||||
}
|
||||
return (upper_bound <= t_test_path_length);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* ALTERNATIVE_PATH_ROUTING_HPP */
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef MANY_TO_MANY_ROUTING_HPP
|
||||
#define MANY_TO_MANY_ROUTING_HPP
|
||||
|
||||
#include "routing_base.hpp"
|
||||
#include "../data_structures/search_engine_data.hpp"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
template <class DataFacadeT> class ManyToManyRouting final : public BasicRoutingInterface<DataFacadeT>
|
||||
{
|
||||
using super = BasicRoutingInterface<DataFacadeT>;
|
||||
using QueryHeap = SearchEngineData::QueryHeap;
|
||||
SearchEngineData &engine_working_data;
|
||||
|
||||
struct NodeBucket
|
||||
{
|
||||
unsigned target_id; // essentially a row in the distance matrix
|
||||
EdgeWeight distance;
|
||||
NodeBucket(const unsigned target_id, const EdgeWeight distance)
|
||||
: target_id(target_id), distance(distance)
|
||||
{
|
||||
}
|
||||
};
|
||||
using SearchSpaceWithBuckets = std::unordered_map<NodeID, std::vector<NodeBucket>>;
|
||||
|
||||
public:
|
||||
ManyToManyRouting(DataFacadeT *facade, SearchEngineData &engine_working_data)
|
||||
: super(facade), engine_working_data(engine_working_data)
|
||||
{
|
||||
}
|
||||
|
||||
~ManyToManyRouting() {}
|
||||
|
||||
std::shared_ptr<std::vector<EdgeWeight>> operator()(const PhantomNodeArray &phantom_nodes_array)
|
||||
const
|
||||
{
|
||||
const unsigned number_of_locations = static_cast<unsigned>(phantom_nodes_array.size());
|
||||
std::shared_ptr<std::vector<EdgeWeight>> result_table =
|
||||
std::make_shared<std::vector<EdgeWeight>>(number_of_locations * number_of_locations,
|
||||
std::numeric_limits<EdgeWeight>::max());
|
||||
|
||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
|
||||
QueryHeap &query_heap = *(engine_working_data.forwardHeap);
|
||||
|
||||
SearchSpaceWithBuckets search_space_with_buckets;
|
||||
|
||||
unsigned target_id = 0;
|
||||
for (const std::vector<PhantomNode> &phantom_node_vector : phantom_nodes_array)
|
||||
{
|
||||
query_heap.Clear();
|
||||
// insert target(s) at distance 0
|
||||
|
||||
for (const PhantomNode &phantom_node : phantom_node_vector)
|
||||
{
|
||||
if (SPECIAL_NODEID != phantom_node.forward_node_id)
|
||||
{
|
||||
query_heap.Insert(phantom_node.forward_node_id,
|
||||
phantom_node.GetForwardWeightPlusOffset(),
|
||||
phantom_node.forward_node_id);
|
||||
}
|
||||
if (SPECIAL_NODEID != phantom_node.reverse_node_id)
|
||||
{
|
||||
query_heap.Insert(phantom_node.reverse_node_id,
|
||||
phantom_node.GetReverseWeightPlusOffset(),
|
||||
phantom_node.reverse_node_id);
|
||||
}
|
||||
}
|
||||
|
||||
// explore search space
|
||||
while (!query_heap.Empty())
|
||||
{
|
||||
BackwardRoutingStep(target_id, query_heap, search_space_with_buckets);
|
||||
}
|
||||
++target_id;
|
||||
}
|
||||
|
||||
// for each source do forward search
|
||||
unsigned source_id = 0;
|
||||
for (const std::vector<PhantomNode> &phantom_node_vector : phantom_nodes_array)
|
||||
{
|
||||
query_heap.Clear();
|
||||
for (const PhantomNode &phantom_node : phantom_node_vector)
|
||||
{
|
||||
// insert sources at distance 0
|
||||
if (SPECIAL_NODEID != phantom_node.forward_node_id)
|
||||
{
|
||||
query_heap.Insert(phantom_node.forward_node_id,
|
||||
-phantom_node.GetForwardWeightPlusOffset(),
|
||||
phantom_node.forward_node_id);
|
||||
}
|
||||
if (SPECIAL_NODEID != phantom_node.reverse_node_id)
|
||||
{
|
||||
query_heap.Insert(phantom_node.reverse_node_id,
|
||||
-phantom_node.GetReverseWeightPlusOffset(),
|
||||
phantom_node.reverse_node_id);
|
||||
}
|
||||
}
|
||||
|
||||
// explore search space
|
||||
while (!query_heap.Empty())
|
||||
{
|
||||
ForwardRoutingStep(source_id,
|
||||
number_of_locations,
|
||||
query_heap,
|
||||
search_space_with_buckets,
|
||||
result_table);
|
||||
}
|
||||
|
||||
++source_id;
|
||||
}
|
||||
BOOST_ASSERT(source_id == target_id);
|
||||
return result_table;
|
||||
}
|
||||
|
||||
void ForwardRoutingStep(const unsigned source_id,
|
||||
const unsigned number_of_locations,
|
||||
QueryHeap &query_heap,
|
||||
const SearchSpaceWithBuckets &search_space_with_buckets,
|
||||
std::shared_ptr<std::vector<EdgeWeight>> result_table) const
|
||||
{
|
||||
const NodeID node = query_heap.DeleteMin();
|
||||
const int source_distance = query_heap.GetKey(node);
|
||||
|
||||
// check if each encountered node has an entry
|
||||
const auto bucket_iterator = search_space_with_buckets.find(node);
|
||||
// iterate bucket if there exists one
|
||||
if (bucket_iterator != search_space_with_buckets.end())
|
||||
{
|
||||
const std::vector<NodeBucket> &bucket_list = bucket_iterator->second;
|
||||
for (const NodeBucket ¤t_bucket : bucket_list)
|
||||
{
|
||||
// get target id from bucket entry
|
||||
const unsigned target_id = current_bucket.target_id;
|
||||
const int target_distance = current_bucket.distance;
|
||||
const EdgeWeight current_distance =
|
||||
(*result_table)[source_id * number_of_locations + target_id];
|
||||
// check if new distance is better
|
||||
const EdgeWeight new_distance = source_distance + target_distance;
|
||||
if (new_distance > 0 && new_distance < current_distance)
|
||||
{
|
||||
(*result_table)[source_id * number_of_locations + target_id] =
|
||||
(source_distance + target_distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (StallAtNode<true>(node, source_distance, query_heap))
|
||||
{
|
||||
return;
|
||||
}
|
||||
RelaxOutgoingEdges<true>(node, source_distance, query_heap);
|
||||
}
|
||||
|
||||
void BackwardRoutingStep(const unsigned target_id,
|
||||
QueryHeap &query_heap,
|
||||
SearchSpaceWithBuckets &search_space_with_buckets) const
|
||||
{
|
||||
const NodeID node = query_heap.DeleteMin();
|
||||
const int target_distance = query_heap.GetKey(node);
|
||||
|
||||
// store settled nodes in search space bucket
|
||||
search_space_with_buckets[node].emplace_back(target_id, target_distance);
|
||||
|
||||
if (StallAtNode<false>(node, target_distance, query_heap))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RelaxOutgoingEdges<false>(node, target_distance, query_heap);
|
||||
}
|
||||
|
||||
template <bool forward_direction>
|
||||
inline void
|
||||
RelaxOutgoingEdges(const NodeID node, const EdgeWeight distance, QueryHeap &query_heap) const
|
||||
{
|
||||
for (auto edge : super::facade->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const auto &data = super::facade->GetEdgeData(edge);
|
||||
const bool direction_flag = (forward_direction ? data.forward : data.backward);
|
||||
if (direction_flag)
|
||||
{
|
||||
const NodeID to = super::facade->GetTarget(edge);
|
||||
const int edge_weight = data.distance;
|
||||
|
||||
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
|
||||
const int to_distance = distance + edge_weight;
|
||||
|
||||
// New Node discovered -> Add to Heap + Node Info Storage
|
||||
if (!query_heap.WasInserted(to))
|
||||
{
|
||||
query_heap.Insert(to, to_distance, node);
|
||||
}
|
||||
// Found a shorter Path -> Update distance
|
||||
else if (to_distance < query_heap.GetKey(to))
|
||||
{
|
||||
// new parent
|
||||
query_heap.GetData(to).parent = node;
|
||||
query_heap.DecreaseKey(to, to_distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Stalling
|
||||
template <bool forward_direction>
|
||||
inline bool StallAtNode(const NodeID node, const EdgeWeight distance, QueryHeap &query_heap)
|
||||
const
|
||||
{
|
||||
for (auto edge : super::facade->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const auto &data = super::facade->GetEdgeData(edge);
|
||||
const bool reverse_flag = ((!forward_direction) ? data.forward : data.backward);
|
||||
if (reverse_flag)
|
||||
{
|
||||
const NodeID to = super::facade->GetTarget(edge);
|
||||
const int edge_weight = data.distance;
|
||||
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
|
||||
if (query_heap.WasInserted(to))
|
||||
{
|
||||
if (query_heap.GetKey(to) + edge_weight < distance)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,413 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef ROUTING_BASE_HPP
|
||||
#define ROUTING_BASE_HPP
|
||||
|
||||
#include "../data_structures/raw_route_data.hpp"
|
||||
#include "../data_structures/search_engine_data.hpp"
|
||||
#include "../data_structures/turn_instructions.hpp"
|
||||
// #include "../Util/simple_logger.hpp.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <stack>
|
||||
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forwardHeap;
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::backwardHeap;
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forwardHeap2;
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::backwardHeap2;
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::forwardHeap3;
|
||||
SearchEngineData::SearchEngineHeapPtr SearchEngineData::backwardHeap3;
|
||||
|
||||
template <class DataFacadeT> class BasicRoutingInterface
|
||||
{
|
||||
private:
|
||||
typedef typename DataFacadeT::EdgeData EdgeData;
|
||||
|
||||
protected:
|
||||
DataFacadeT *facade;
|
||||
|
||||
public:
|
||||
BasicRoutingInterface() = delete;
|
||||
BasicRoutingInterface(const BasicRoutingInterface &) = delete;
|
||||
explicit BasicRoutingInterface(DataFacadeT *facade) : facade(facade) {}
|
||||
virtual ~BasicRoutingInterface() {};
|
||||
|
||||
inline void RoutingStep(SearchEngineData::QueryHeap &forward_heap,
|
||||
SearchEngineData::QueryHeap &reverse_heap,
|
||||
NodeID *middle_node_id,
|
||||
int *upper_bound,
|
||||
const int min_edge_offset,
|
||||
const bool forward_direction) const
|
||||
{
|
||||
const NodeID node = forward_heap.DeleteMin();
|
||||
const int distance = forward_heap.GetKey(node);
|
||||
|
||||
// const NodeID parentnode = forward_heap.GetData(node).parent;
|
||||
// SimpleLogger().Write() << (forward_direction ? "[fwd] " : "[rev] ") << "settled edge (" << parentnode << "," << node << "), dist: " << distance;
|
||||
|
||||
if (reverse_heap.WasInserted(node))
|
||||
{
|
||||
const int new_distance = reverse_heap.GetKey(node) + distance;
|
||||
if (new_distance < *upper_bound)
|
||||
{
|
||||
if (new_distance >= 0)
|
||||
{
|
||||
*middle_node_id = node;
|
||||
*upper_bound = new_distance;
|
||||
// SimpleLogger().Write() << "accepted middle node " << node << " at distance " << new_distance;
|
||||
// } else {
|
||||
// SimpleLogger().Write() << "discared middle node " << node << " at distance " << new_distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (distance + min_edge_offset > *upper_bound)
|
||||
{
|
||||
// SimpleLogger().Write() << "min_edge_offset: " << min_edge_offset;
|
||||
forward_heap.DeleteAll();
|
||||
return;
|
||||
}
|
||||
|
||||
// Stalling
|
||||
for (const auto edge : facade->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const EdgeData &data = facade->GetEdgeData(edge);
|
||||
const bool reverse_flag = ((!forward_direction) ? data.forward : data.backward);
|
||||
if (reverse_flag)
|
||||
{
|
||||
const NodeID to = facade->GetTarget(edge);
|
||||
const int edge_weight = data.distance;
|
||||
|
||||
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
|
||||
|
||||
if (forward_heap.WasInserted(to))
|
||||
{
|
||||
if (forward_heap.GetKey(to) + edge_weight < distance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto edge : facade->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const EdgeData &data = facade->GetEdgeData(edge);
|
||||
bool forward_directionFlag = (forward_direction ? data.forward : data.backward);
|
||||
if (forward_directionFlag)
|
||||
{
|
||||
|
||||
const NodeID to = facade->GetTarget(edge);
|
||||
const int edge_weight = data.distance;
|
||||
|
||||
BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid");
|
||||
const int to_distance = distance + edge_weight;
|
||||
|
||||
// New Node discovered -> Add to Heap + Node Info Storage
|
||||
if (!forward_heap.WasInserted(to))
|
||||
{
|
||||
forward_heap.Insert(to, to_distance, node);
|
||||
}
|
||||
// Found a shorter Path -> Update distance
|
||||
else if (to_distance < forward_heap.GetKey(to))
|
||||
{
|
||||
// new parent
|
||||
forward_heap.GetData(to).parent = node;
|
||||
forward_heap.DecreaseKey(to, to_distance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void UnpackPath(const std::vector<NodeID> &packed_path,
|
||||
const PhantomNodes &phantom_node_pair,
|
||||
std::vector<PathData> &unpacked_path) const
|
||||
{
|
||||
const bool start_traversed_in_reverse =
|
||||
(packed_path.front() != phantom_node_pair.source_phantom.forward_node_id);
|
||||
const bool target_traversed_in_reverse =
|
||||
(packed_path.back() != phantom_node_pair.target_phantom.forward_node_id);
|
||||
|
||||
const unsigned packed_path_size = static_cast<unsigned>(packed_path.size());
|
||||
std::stack<std::pair<NodeID, NodeID>> recursion_stack;
|
||||
|
||||
// We have to push the path in reverse order onto the stack because it's LIFO.
|
||||
for (unsigned i = packed_path_size - 1; i > 0; --i)
|
||||
{
|
||||
recursion_stack.emplace(packed_path[i - 1], packed_path[i]);
|
||||
}
|
||||
|
||||
std::pair<NodeID, NodeID> edge;
|
||||
while (!recursion_stack.empty())
|
||||
{
|
||||
/*
|
||||
Graphical representation of variables:
|
||||
|
||||
edge.first edge.second
|
||||
*------------------>*
|
||||
edge_id
|
||||
*/
|
||||
edge = recursion_stack.top();
|
||||
recursion_stack.pop();
|
||||
|
||||
// facade->FindEdge does not suffice here in case of shortcuts.
|
||||
// The above explanation unclear? Think!
|
||||
EdgeID smaller_edge_id = SPECIAL_EDGEID;
|
||||
int edge_weight = std::numeric_limits<EdgeWeight>::max();
|
||||
for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first))
|
||||
{
|
||||
const int weight = facade->GetEdgeData(edge_id).distance;
|
||||
if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) &&
|
||||
facade->GetEdgeData(edge_id).forward)
|
||||
{
|
||||
smaller_edge_id = edge_id;
|
||||
edge_weight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Graphical representation of variables:
|
||||
|
||||
edge.first edge.second
|
||||
*<------------------*
|
||||
edge_id
|
||||
*/
|
||||
if (SPECIAL_EDGEID == smaller_edge_id)
|
||||
{
|
||||
for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second))
|
||||
{
|
||||
const int weight = facade->GetEdgeData(edge_id).distance;
|
||||
if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) &&
|
||||
facade->GetEdgeData(edge_id).backward)
|
||||
{
|
||||
smaller_edge_id = edge_id;
|
||||
edge_weight = weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
BOOST_ASSERT_MSG(edge_weight != INVALID_EDGE_WEIGHT, "edge id invalid");
|
||||
|
||||
const EdgeData &ed = facade->GetEdgeData(smaller_edge_id);
|
||||
if (ed.shortcut)
|
||||
{ // unpack
|
||||
const NodeID middle_node_id = ed.id;
|
||||
// again, we need to this in reversed order
|
||||
recursion_stack.emplace(middle_node_id, edge.second);
|
||||
recursion_stack.emplace(edge.first, middle_node_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT_MSG(!ed.shortcut, "original edge flagged as shortcut");
|
||||
unsigned name_index = facade->GetNameIndexFromEdgeID(ed.id);
|
||||
const TurnInstruction turn_instruction = facade->GetTurnInstructionForEdgeID(ed.id);
|
||||
const TravelMode travel_mode = facade->GetTravelModeForEdgeID(ed.id);
|
||||
|
||||
|
||||
if (!facade->EdgeIsCompressed(ed.id))
|
||||
{
|
||||
BOOST_ASSERT(!facade->EdgeIsCompressed(ed.id));
|
||||
unpacked_path.emplace_back(facade->GetGeometryIndexForEdgeID(ed.id),
|
||||
name_index,
|
||||
turn_instruction,
|
||||
ed.distance,
|
||||
travel_mode);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<unsigned> id_vector;
|
||||
facade->GetUncompressedGeometry(facade->GetGeometryIndexForEdgeID(ed.id),
|
||||
id_vector);
|
||||
|
||||
const std::size_t start_index =
|
||||
(unpacked_path.empty()
|
||||
? ((start_traversed_in_reverse)
|
||||
? id_vector.size() -
|
||||
phantom_node_pair.source_phantom.fwd_segment_position - 1
|
||||
: phantom_node_pair.source_phantom.fwd_segment_position)
|
||||
: 0);
|
||||
const std::size_t end_index = id_vector.size();
|
||||
|
||||
BOOST_ASSERT(start_index >= 0);
|
||||
BOOST_ASSERT(start_index <= end_index);
|
||||
for (std::size_t i = start_index; i < end_index; ++i)
|
||||
{
|
||||
unpacked_path.emplace_back(id_vector[i], name_index, TurnInstruction::NoTurn, 0, travel_mode);
|
||||
}
|
||||
unpacked_path.back().turn_instruction = turn_instruction;
|
||||
unpacked_path.back().segment_duration = ed.distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (SPECIAL_EDGEID != phantom_node_pair.target_phantom.packed_geometry_id)
|
||||
{
|
||||
std::vector<unsigned> id_vector;
|
||||
facade->GetUncompressedGeometry(phantom_node_pair.target_phantom.packed_geometry_id,
|
||||
id_vector);
|
||||
const bool is_local_path = (phantom_node_pair.source_phantom.packed_geometry_id ==
|
||||
phantom_node_pair.target_phantom.packed_geometry_id) &&
|
||||
unpacked_path.empty();
|
||||
|
||||
std::size_t start_index = 0;
|
||||
if (is_local_path)
|
||||
{
|
||||
start_index = phantom_node_pair.source_phantom.fwd_segment_position;
|
||||
if (target_traversed_in_reverse)
|
||||
{
|
||||
start_index =
|
||||
id_vector.size() - phantom_node_pair.source_phantom.fwd_segment_position;
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t end_index = phantom_node_pair.target_phantom.fwd_segment_position;
|
||||
if (target_traversed_in_reverse)
|
||||
{
|
||||
std::reverse(id_vector.begin(), id_vector.end());
|
||||
end_index =
|
||||
id_vector.size() - phantom_node_pair.target_phantom.fwd_segment_position;
|
||||
}
|
||||
|
||||
if (start_index > end_index)
|
||||
{
|
||||
start_index = std::min(start_index, id_vector.size()-1);
|
||||
}
|
||||
|
||||
for (std::size_t i = start_index; i != end_index; (start_index < end_index ? ++i : --i))
|
||||
{
|
||||
BOOST_ASSERT(i < id_vector.size());
|
||||
BOOST_ASSERT(phantom_node_pair.target_phantom.forward_travel_mode>0 );
|
||||
unpacked_path.emplace_back(PathData{id_vector[i],
|
||||
phantom_node_pair.target_phantom.name_id,
|
||||
TurnInstruction::NoTurn,
|
||||
0,
|
||||
phantom_node_pair.target_phantom.forward_travel_mode});
|
||||
}
|
||||
}
|
||||
|
||||
// there is no equivalent to a node-based node in an edge-expanded graph.
|
||||
// two equivalent routes may start (or end) at different node-based edges
|
||||
// as they are added with the offset how much "distance" on the edge
|
||||
// has already been traversed. Depending on offset one needs to remove
|
||||
// the last node.
|
||||
if (unpacked_path.size() > 1)
|
||||
{
|
||||
const std::size_t last_index = unpacked_path.size() - 1;
|
||||
const std::size_t second_to_last_index = last_index - 1;
|
||||
|
||||
// looks like a trivially true check but tests for underflow
|
||||
BOOST_ASSERT(last_index > second_to_last_index);
|
||||
|
||||
if (unpacked_path[last_index].node == unpacked_path[second_to_last_index].node)
|
||||
{
|
||||
unpacked_path.pop_back();
|
||||
}
|
||||
BOOST_ASSERT(!unpacked_path.empty());
|
||||
}
|
||||
}
|
||||
|
||||
inline void UnpackEdge(const NodeID s, const NodeID t, std::vector<NodeID> &unpacked_path) const
|
||||
{
|
||||
std::stack<std::pair<NodeID, NodeID>> recursion_stack;
|
||||
recursion_stack.emplace(s, t);
|
||||
|
||||
std::pair<NodeID, NodeID> edge;
|
||||
while (!recursion_stack.empty())
|
||||
{
|
||||
edge = recursion_stack.top();
|
||||
recursion_stack.pop();
|
||||
|
||||
EdgeID smaller_edge_id = SPECIAL_EDGEID;
|
||||
int edge_weight = std::numeric_limits<EdgeWeight>::max();
|
||||
for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.first))
|
||||
{
|
||||
const int weight = facade->GetEdgeData(edge_id).distance;
|
||||
if ((facade->GetTarget(edge_id) == edge.second) && (weight < edge_weight) &&
|
||||
facade->GetEdgeData(edge_id).forward)
|
||||
{
|
||||
smaller_edge_id = edge_id;
|
||||
edge_weight = weight;
|
||||
}
|
||||
}
|
||||
|
||||
if (SPECIAL_EDGEID == smaller_edge_id)
|
||||
{
|
||||
for (const auto edge_id : facade->GetAdjacentEdgeRange(edge.second))
|
||||
{
|
||||
const int weight = facade->GetEdgeData(edge_id).distance;
|
||||
if ((facade->GetTarget(edge_id) == edge.first) && (weight < edge_weight) &&
|
||||
facade->GetEdgeData(edge_id).backward)
|
||||
{
|
||||
smaller_edge_id = edge_id;
|
||||
edge_weight = weight;
|
||||
}
|
||||
}
|
||||
}
|
||||
BOOST_ASSERT_MSG(edge_weight != std::numeric_limits<EdgeWeight>::max(), "edge weight invalid");
|
||||
|
||||
const EdgeData &ed = facade->GetEdgeData(smaller_edge_id);
|
||||
if (ed.shortcut)
|
||||
{ // unpack
|
||||
const NodeID middle_node_id = ed.id;
|
||||
// again, we need to this in reversed order
|
||||
recursion_stack.emplace(middle_node_id, edge.second);
|
||||
recursion_stack.emplace(edge.first, middle_node_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT_MSG(!ed.shortcut, "edge must be shortcut");
|
||||
unpacked_path.emplace_back(edge.first);
|
||||
}
|
||||
}
|
||||
unpacked_path.emplace_back(t);
|
||||
}
|
||||
|
||||
inline void RetrievePackedPathFromHeap(const SearchEngineData::QueryHeap &forward_heap,
|
||||
const SearchEngineData::QueryHeap &reverse_heap,
|
||||
const NodeID middle_node_id,
|
||||
std::vector<NodeID> &packed_path) const
|
||||
{
|
||||
RetrievePackedPathFromSingleHeap(forward_heap, middle_node_id, packed_path);
|
||||
std::reverse(packed_path.begin(), packed_path.end());
|
||||
packed_path.emplace_back(middle_node_id);
|
||||
RetrievePackedPathFromSingleHeap(reverse_heap, middle_node_id, packed_path);
|
||||
}
|
||||
|
||||
inline void RetrievePackedPathFromSingleHeap(const SearchEngineData::QueryHeap &search_heap,
|
||||
const NodeID middle_node_id,
|
||||
std::vector<NodeID> &packed_path) const
|
||||
{
|
||||
NodeID current_node_id = middle_node_id;
|
||||
while (current_node_id != search_heap.GetData(current_node_id).parent)
|
||||
{
|
||||
current_node_id = search_heap.GetData(current_node_id).parent;
|
||||
packed_path.emplace_back(current_node_id);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // ROUTING_BASE_HPP
|
||||
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Project OSRM, Dennis Luxen, others
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list
|
||||
of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this
|
||||
list of conditions and the following disclaimer in the documentation and/or
|
||||
other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef SHORTEST_PATH_HPP
|
||||
#define SHORTEST_PATH_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include "routing_base.hpp"
|
||||
#include "../data_structures/search_engine_data.hpp"
|
||||
#include "../Util/integer_range.hpp"
|
||||
#include "../typedefs.h"
|
||||
|
||||
template <class DataFacadeT> class ShortestPathRouting final : public BasicRoutingInterface<DataFacadeT>
|
||||
{
|
||||
using super = BasicRoutingInterface<DataFacadeT>;
|
||||
using QueryHeap = SearchEngineData::QueryHeap;
|
||||
SearchEngineData &engine_working_data;
|
||||
|
||||
public:
|
||||
ShortestPathRouting(DataFacadeT *facade, SearchEngineData &engine_working_data)
|
||||
: super(facade), engine_working_data(engine_working_data)
|
||||
{
|
||||
}
|
||||
|
||||
~ShortestPathRouting() {}
|
||||
|
||||
void operator()(const std::vector<PhantomNodes> &phantom_nodes_vector,
|
||||
const std::vector<bool> &uturn_indicators,
|
||||
RawRouteData &raw_route_data) const
|
||||
{
|
||||
int distance1 = 0;
|
||||
int distance2 = 0;
|
||||
bool search_from_1st_node = true;
|
||||
bool search_from_2nd_node = true;
|
||||
NodeID middle1 = SPECIAL_NODEID;
|
||||
NodeID middle2 = SPECIAL_NODEID;
|
||||
std::vector<std::vector<NodeID>> packed_legs1(phantom_nodes_vector.size());
|
||||
std::vector<std::vector<NodeID>> packed_legs2(phantom_nodes_vector.size());
|
||||
|
||||
engine_working_data.InitializeOrClearFirstThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
engine_working_data.InitializeOrClearSecondThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
engine_working_data.InitializeOrClearThirdThreadLocalStorage(
|
||||
super::facade->GetNumberOfNodes());
|
||||
|
||||
QueryHeap &forward_heap1 = *(engine_working_data.forwardHeap);
|
||||
QueryHeap &reverse_heap1 = *(engine_working_data.backwardHeap);
|
||||
QueryHeap &forward_heap2 = *(engine_working_data.forwardHeap2);
|
||||
QueryHeap &reverse_heap2 = *(engine_working_data.backwardHeap2);
|
||||
|
||||
std::size_t current_leg = 0;
|
||||
// Get distance to next pair of target nodes.
|
||||
for (const PhantomNodes &phantom_node_pair : phantom_nodes_vector)
|
||||
{
|
||||
forward_heap1.Clear();
|
||||
forward_heap2.Clear();
|
||||
reverse_heap1.Clear();
|
||||
reverse_heap2.Clear();
|
||||
int local_upper_bound1 = INVALID_EDGE_WEIGHT;
|
||||
int local_upper_bound2 = INVALID_EDGE_WEIGHT;
|
||||
|
||||
middle1 = SPECIAL_NODEID;
|
||||
middle2 = SPECIAL_NODEID;
|
||||
|
||||
const bool allow_u_turn = current_leg > 0 && uturn_indicators.size() > current_leg && uturn_indicators[current_leg-1];
|
||||
EdgeWeight min_edge_offset = 0;
|
||||
|
||||
// insert new starting nodes into forward heap, adjusted by previous distances.
|
||||
if ((allow_u_turn || search_from_1st_node) &&
|
||||
phantom_node_pair.source_phantom.forward_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
forward_heap1.Insert(
|
||||
phantom_node_pair.source_phantom.forward_node_id,
|
||||
(allow_u_turn ? 0 : distance1) - phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(),
|
||||
phantom_node_pair.source_phantom.forward_node_id);
|
||||
min_edge_offset = std::min(min_edge_offset, (allow_u_turn ? 0 : distance1) - phantom_node_pair.source_phantom.GetForwardWeightPlusOffset());
|
||||
// SimpleLogger().Write(logDEBUG) << "fwd-a2 insert: " << phantom_node_pair.source_phantom.forward_node_id << ", w: " << (allow_u_turn ? 0 : distance1) - phantom_node_pair.source_phantom.GetForwardWeightPlusOffset();
|
||||
forward_heap2.Insert(
|
||||
phantom_node_pair.source_phantom.forward_node_id,
|
||||
(allow_u_turn ? 0 : distance1) - phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(),
|
||||
phantom_node_pair.source_phantom.forward_node_id);
|
||||
min_edge_offset = std::min(min_edge_offset, (allow_u_turn ? 0 : distance1) - phantom_node_pair.source_phantom.GetForwardWeightPlusOffset());
|
||||
// SimpleLogger().Write(logDEBUG) << "fwd-b2 insert: " << phantom_node_pair.source_phantom.forward_node_id << ", w: " << (allow_u_turn ? 0 : distance1) - phantom_node_pair.source_phantom.GetForwardWeightPlusOffset();
|
||||
|
||||
}
|
||||
if ((allow_u_turn || search_from_2nd_node) &&
|
||||
phantom_node_pair.source_phantom.reverse_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
forward_heap1.Insert(
|
||||
phantom_node_pair.source_phantom.reverse_node_id,
|
||||
(allow_u_turn ? 0 : distance2) - phantom_node_pair.source_phantom.GetReverseWeightPlusOffset(),
|
||||
phantom_node_pair.source_phantom.reverse_node_id);
|
||||
min_edge_offset = std::min(min_edge_offset, (allow_u_turn ? 0 : distance2) - phantom_node_pair.source_phantom.GetReverseWeightPlusOffset());
|
||||
// SimpleLogger().Write(logDEBUG) << "fwd-a2 insert: " << phantom_node_pair.source_phantom.reverse_node_id <<
|
||||
// ", w: " << (allow_u_turn ? 0 : distance2) - phantom_node_pair.source_phantom.GetReverseWeightPlusOffset();
|
||||
forward_heap2.Insert(
|
||||
phantom_node_pair.source_phantom.reverse_node_id,
|
||||
(allow_u_turn ? 0 : distance2) - phantom_node_pair.source_phantom.GetReverseWeightPlusOffset(),
|
||||
phantom_node_pair.source_phantom.reverse_node_id);
|
||||
min_edge_offset = std::min(min_edge_offset, (allow_u_turn ? 0 : distance2) - phantom_node_pair.source_phantom.GetReverseWeightPlusOffset());
|
||||
// SimpleLogger().Write(logDEBUG) << "fwd-b2 insert: " << phantom_node_pair.source_phantom.reverse_node_id <<
|
||||
// ", w: " << (allow_u_turn ? 0 : distance2) - phantom_node_pair.source_phantom.GetReverseWeightPlusOffset();
|
||||
}
|
||||
|
||||
// insert new backward nodes into backward heap, unadjusted.
|
||||
if (phantom_node_pair.target_phantom.forward_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
reverse_heap1.Insert(phantom_node_pair.target_phantom.forward_node_id,
|
||||
phantom_node_pair.target_phantom.GetForwardWeightPlusOffset(),
|
||||
phantom_node_pair.target_phantom.forward_node_id);
|
||||
// SimpleLogger().Write(logDEBUG) << "rev-a insert: " << phantom_node_pair.target_phantom.forward_node_id <<
|
||||
// ", w: " << phantom_node_pair.target_phantom.GetForwardWeightPlusOffset();
|
||||
}
|
||||
|
||||
if (phantom_node_pair.target_phantom.reverse_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
reverse_heap2.Insert(phantom_node_pair.target_phantom.reverse_node_id,
|
||||
phantom_node_pair.target_phantom.GetReverseWeightPlusOffset(),
|
||||
phantom_node_pair.target_phantom.reverse_node_id);
|
||||
// SimpleLogger().Write(logDEBUG) << "rev-a insert: " << phantom_node_pair.target_phantom.reverse_node_id <<
|
||||
// ", w: " << phantom_node_pair.target_phantom.GetReverseWeightPlusOffset();
|
||||
}
|
||||
|
||||
// run two-Target Dijkstra routing step.
|
||||
while (0 < (forward_heap1.Size() + reverse_heap1.Size()))
|
||||
{
|
||||
if (!forward_heap1.Empty())
|
||||
{
|
||||
super::RoutingStep(
|
||||
forward_heap1, reverse_heap1, &middle1, &local_upper_bound1, min_edge_offset, true);
|
||||
}
|
||||
if (!reverse_heap1.Empty())
|
||||
{
|
||||
super::RoutingStep(
|
||||
reverse_heap1, forward_heap1, &middle1, &local_upper_bound1, min_edge_offset, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!reverse_heap2.Empty())
|
||||
{
|
||||
while (0 < (forward_heap2.Size() + reverse_heap2.Size()))
|
||||
{
|
||||
if (!forward_heap2.Empty())
|
||||
{
|
||||
super::RoutingStep(
|
||||
forward_heap2, reverse_heap2, &middle2, &local_upper_bound2, min_edge_offset, true);
|
||||
}
|
||||
if (!reverse_heap2.Empty())
|
||||
{
|
||||
super::RoutingStep(
|
||||
reverse_heap2, forward_heap2, &middle2, &local_upper_bound2, min_edge_offset, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No path found for both target nodes?
|
||||
if ((INVALID_EDGE_WEIGHT == local_upper_bound1) &&
|
||||
(INVALID_EDGE_WEIGHT == local_upper_bound2))
|
||||
{
|
||||
raw_route_data.shortest_path_length = INVALID_EDGE_WEIGHT;
|
||||
raw_route_data.alternative_path_length = INVALID_EDGE_WEIGHT;
|
||||
return;
|
||||
}
|
||||
|
||||
search_from_1st_node = true;
|
||||
search_from_2nd_node = true;
|
||||
if (SPECIAL_NODEID == middle1)
|
||||
{
|
||||
search_from_1st_node = false;
|
||||
}
|
||||
if (SPECIAL_NODEID == middle2)
|
||||
{
|
||||
search_from_2nd_node = false;
|
||||
}
|
||||
|
||||
// Was at most one of the two paths not found?
|
||||
BOOST_ASSERT_MSG((INVALID_EDGE_WEIGHT != distance1 || INVALID_EDGE_WEIGHT != distance2), "no path found");
|
||||
|
||||
// Unpack paths if they exist
|
||||
std::vector<NodeID> temporary_packed_leg1;
|
||||
std::vector<NodeID> temporary_packed_leg2;
|
||||
|
||||
BOOST_ASSERT((unsigned)current_leg < packed_legs1.size());
|
||||
BOOST_ASSERT((unsigned)current_leg < packed_legs2.size());
|
||||
|
||||
if (INVALID_EDGE_WEIGHT != local_upper_bound1)
|
||||
{
|
||||
super::RetrievePackedPathFromHeap(
|
||||
forward_heap1, reverse_heap1, middle1, temporary_packed_leg1);
|
||||
}
|
||||
|
||||
if (INVALID_EDGE_WEIGHT != local_upper_bound2)
|
||||
{
|
||||
super::RetrievePackedPathFromHeap(
|
||||
forward_heap2, reverse_heap2, middle2, temporary_packed_leg2);
|
||||
}
|
||||
|
||||
// if one of the paths was not found, replace it with the other one.
|
||||
if ((allow_u_turn && local_upper_bound1 > local_upper_bound2) || temporary_packed_leg1.empty())
|
||||
{
|
||||
temporary_packed_leg1.clear();
|
||||
temporary_packed_leg1.insert(temporary_packed_leg1.end(),
|
||||
temporary_packed_leg2.begin(),
|
||||
temporary_packed_leg2.end());
|
||||
local_upper_bound1 = local_upper_bound2;
|
||||
}
|
||||
if ((allow_u_turn && local_upper_bound2 > local_upper_bound1) || temporary_packed_leg2.empty())
|
||||
{
|
||||
temporary_packed_leg2.clear();
|
||||
temporary_packed_leg2.insert(temporary_packed_leg2.end(),
|
||||
temporary_packed_leg1.begin(),
|
||||
temporary_packed_leg1.end());
|
||||
local_upper_bound2 = local_upper_bound1;
|
||||
}
|
||||
|
||||
BOOST_ASSERT_MSG(!temporary_packed_leg1.empty() || !temporary_packed_leg2.empty(),
|
||||
"tempory packed paths empty");
|
||||
|
||||
BOOST_ASSERT((0 == current_leg) || !packed_legs1[current_leg - 1].empty());
|
||||
BOOST_ASSERT((0 == current_leg) || !packed_legs2[current_leg - 1].empty());
|
||||
|
||||
if (!allow_u_turn && 0 < current_leg)
|
||||
{
|
||||
const NodeID end_id_of_segment1 = packed_legs1[current_leg - 1].back();
|
||||
const NodeID end_id_of_segment2 = packed_legs2[current_leg - 1].back();
|
||||
BOOST_ASSERT(!temporary_packed_leg1.empty());
|
||||
const NodeID start_id_of_leg1 = temporary_packed_leg1.front();
|
||||
const NodeID start_id_of_leg2 = temporary_packed_leg2.front();
|
||||
if ((end_id_of_segment1 != start_id_of_leg1) &&
|
||||
(end_id_of_segment2 != start_id_of_leg2))
|
||||
{
|
||||
std::swap(temporary_packed_leg1, temporary_packed_leg2);
|
||||
std::swap(local_upper_bound1, local_upper_bound2);
|
||||
}
|
||||
|
||||
// remove the shorter path if both legs end at the same segment
|
||||
if (start_id_of_leg1 == start_id_of_leg2)
|
||||
{
|
||||
const NodeID last_id_of_packed_legs1 = packed_legs1[current_leg - 1].back();
|
||||
const NodeID last_id_of_packed_legs2 = packed_legs2[current_leg - 1].back();
|
||||
if (start_id_of_leg1 != last_id_of_packed_legs1)
|
||||
{
|
||||
packed_legs1 = packed_legs2;
|
||||
BOOST_ASSERT(start_id_of_leg1 == temporary_packed_leg1.front());
|
||||
}
|
||||
else if (start_id_of_leg2 != last_id_of_packed_legs2)
|
||||
{
|
||||
packed_legs2 = packed_legs1;
|
||||
BOOST_ASSERT(start_id_of_leg2 == temporary_packed_leg2.front());
|
||||
}
|
||||
}
|
||||
}
|
||||
BOOST_ASSERT(packed_legs1.size() == packed_legs2.size());
|
||||
|
||||
packed_legs1[current_leg].insert(packed_legs1[current_leg].end(),
|
||||
temporary_packed_leg1.begin(),
|
||||
temporary_packed_leg1.end());
|
||||
BOOST_ASSERT(packed_legs1[current_leg].size() == temporary_packed_leg1.size());
|
||||
packed_legs2[current_leg].insert(packed_legs2[current_leg].end(),
|
||||
temporary_packed_leg2.begin(),
|
||||
temporary_packed_leg2.end());
|
||||
BOOST_ASSERT(packed_legs2[current_leg].size() == temporary_packed_leg2.size());
|
||||
|
||||
if (!allow_u_turn && (packed_legs1[current_leg].back() == packed_legs2[current_leg].back()) &&
|
||||
phantom_node_pair.target_phantom.is_bidirected())
|
||||
{
|
||||
const NodeID last_node_id = packed_legs2[current_leg].back();
|
||||
search_from_1st_node &=
|
||||
!(last_node_id == phantom_node_pair.target_phantom.reverse_node_id);
|
||||
search_from_2nd_node &=
|
||||
!(last_node_id == phantom_node_pair.target_phantom.forward_node_id);
|
||||
BOOST_ASSERT(search_from_1st_node != search_from_2nd_node);
|
||||
}
|
||||
|
||||
distance1 = local_upper_bound1;
|
||||
distance2 = local_upper_bound2;
|
||||
++current_leg;
|
||||
}
|
||||
|
||||
if (distance1 > distance2)
|
||||
{
|
||||
std::swap(packed_legs1, packed_legs2);
|
||||
}
|
||||
raw_route_data.unpacked_path_segments.resize(packed_legs1.size());
|
||||
|
||||
for (const std::size_t index : osrm::irange<std::size_t>(0, packed_legs1.size()))
|
||||
{
|
||||
BOOST_ASSERT(!phantom_nodes_vector.empty());
|
||||
BOOST_ASSERT(packed_legs1.size() == raw_route_data.unpacked_path_segments.size());
|
||||
|
||||
PhantomNodes unpack_phantom_node_pair = phantom_nodes_vector[index];
|
||||
super::UnpackPath(
|
||||
// -- packed input
|
||||
packed_legs1[index],
|
||||
// -- start and end of (sub-)route
|
||||
unpack_phantom_node_pair,
|
||||
// -- unpacked output
|
||||
raw_route_data.unpacked_path_segments[index]);
|
||||
|
||||
raw_route_data.source_traversed_in_reverse.push_back(
|
||||
(packed_legs1[index].front() != phantom_nodes_vector[index].source_phantom.forward_node_id));
|
||||
raw_route_data.target_traversed_in_reverse.push_back(
|
||||
(packed_legs1[index].back() != phantom_nodes_vector[index].target_phantom.forward_node_id));
|
||||
}
|
||||
raw_route_data.shortest_path_length = std::min(distance1, distance2);
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* SHORTEST_PATH_HPP */
|
||||
Reference in New Issue
Block a user