renamed: RoutingAlgorithms/* routing_algorithms/

This commit is contained in:
Dennis Luxen
2014-11-28 15:33:08 +01:00
parent 0f7cb12e97
commit 00d3676a20
5 changed files with 17 additions and 17 deletions
+904
View File
@@ -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 &current_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 &current_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 &current_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 &current_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 */
+264
View File
@@ -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 &current_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
+413
View File
@@ -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
+335
View File
@@ -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 */