renamed: Contractor/* -> contractor/*
This commit is contained in:
@@ -0,0 +1,973 @@
|
||||
/*
|
||||
|
||||
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 CONTRACTOR_HPP
|
||||
#define CONTRACTOR_HPP
|
||||
|
||||
#include "../DataStructures/BinaryHeap.h"
|
||||
#include "../DataStructures/DeallocatingVector.h"
|
||||
#include "../DataStructures/DynamicGraph.h"
|
||||
#include "../DataStructures/Percent.h"
|
||||
#include "../DataStructures/QueryEdge.h"
|
||||
#include "../DataStructures/XORFastHash.h"
|
||||
#include "../DataStructures/XORFastHashStorage.h"
|
||||
#include "../Util/integer_range.hpp"
|
||||
#include "../Util/simple_logger.hpp"
|
||||
#include "../Util/StringUtil.h"
|
||||
#include "../Util/TimingUtil.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <stxxl/vector>
|
||||
|
||||
#include <tbb/enumerable_thread_specific.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
class Contractor
|
||||
{
|
||||
|
||||
private:
|
||||
struct ContractorEdgeData
|
||||
{
|
||||
ContractorEdgeData()
|
||||
: distance(0), id(0), originalEdges(0), shortcut(0), forward(0), backward(0),
|
||||
is_original_via_node_ID(false)
|
||||
{
|
||||
}
|
||||
ContractorEdgeData(unsigned distance,
|
||||
unsigned original_edges,
|
||||
unsigned id,
|
||||
bool shortcut,
|
||||
bool forward,
|
||||
bool backward)
|
||||
: distance(distance), id(id),
|
||||
originalEdges(std::min((unsigned)1 << 28, original_edges)), shortcut(shortcut),
|
||||
forward(forward), backward(backward), is_original_via_node_ID(false)
|
||||
{
|
||||
}
|
||||
unsigned distance;
|
||||
unsigned id;
|
||||
unsigned originalEdges : 28;
|
||||
bool shortcut : 1;
|
||||
bool forward : 1;
|
||||
bool backward : 1;
|
||||
bool is_original_via_node_ID : 1;
|
||||
} data;
|
||||
|
||||
struct ContractorHeapData
|
||||
{
|
||||
short hop;
|
||||
bool target;
|
||||
ContractorHeapData() : hop(0), target(false) {}
|
||||
ContractorHeapData(short h, bool t) : hop(h), target(t) {}
|
||||
};
|
||||
|
||||
using ContractorGraph = DynamicGraph<ContractorEdgeData>;
|
||||
// using ContractorHeap = BinaryHeap<NodeID, NodeID, int, ContractorHeapData, ArrayStorage<NodeID, NodeID>
|
||||
// >;
|
||||
using ContractorHeap = BinaryHeap<NodeID, NodeID, int, ContractorHeapData, XORFastHashStorage<NodeID, NodeID>>;
|
||||
using ContractorEdge = ContractorGraph::InputEdge;
|
||||
|
||||
struct ContractorThreadData
|
||||
{
|
||||
ContractorHeap heap;
|
||||
std::vector<ContractorEdge> inserted_edges;
|
||||
std::vector<NodeID> neighbours;
|
||||
explicit ContractorThreadData(NodeID nodes) : heap(nodes) {}
|
||||
};
|
||||
|
||||
struct NodePriorityData
|
||||
{
|
||||
int depth;
|
||||
NodePriorityData() : depth(0) {}
|
||||
};
|
||||
|
||||
struct ContractionStats
|
||||
{
|
||||
int edges_deleted_count;
|
||||
int edges_added_count;
|
||||
int original_edges_deleted_count;
|
||||
int original_edges_added_count;
|
||||
ContractionStats()
|
||||
: edges_deleted_count(0), edges_added_count(0), original_edges_deleted_count(0),
|
||||
original_edges_added_count(0)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
struct RemainingNodeData
|
||||
{
|
||||
RemainingNodeData() : id(0), is_independent(false) {}
|
||||
NodeID id : 31;
|
||||
bool is_independent : 1;
|
||||
};
|
||||
|
||||
|
||||
struct ThreadDataContainer
|
||||
{
|
||||
explicit ThreadDataContainer(int number_of_nodes) : number_of_nodes(number_of_nodes) {}
|
||||
|
||||
inline ContractorThreadData* getThreadData()
|
||||
{
|
||||
bool exists = false;
|
||||
auto& ref = data.local(exists);
|
||||
if (!exists)
|
||||
{
|
||||
ref = std::make_shared<ContractorThreadData>(number_of_nodes);
|
||||
}
|
||||
|
||||
return ref.get();
|
||||
}
|
||||
|
||||
int number_of_nodes;
|
||||
using EnumerableThreadData = tbb::enumerable_thread_specific<std::shared_ptr<ContractorThreadData>>;
|
||||
EnumerableThreadData data;
|
||||
};
|
||||
|
||||
public:
|
||||
template <class ContainerT> Contractor(int nodes, ContainerT &input_edge_list)
|
||||
{
|
||||
std::vector<ContractorEdge> edges;
|
||||
edges.reserve(input_edge_list.size() * 2);
|
||||
|
||||
const auto dend = input_edge_list.dend();
|
||||
for (auto diter = input_edge_list.dbegin(); diter != dend; ++diter)
|
||||
{
|
||||
BOOST_ASSERT_MSG(static_cast<unsigned int>(std::max(diter->weight, 1)) > 0, "edge distance < 1");
|
||||
#ifndef NDEBUG
|
||||
if (static_cast<unsigned int>(std::max(diter->weight, 1)) > 24 * 60 * 60 * 10)
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "Edge weight large -> "
|
||||
<< static_cast<unsigned int>(std::max(diter->weight, 1));
|
||||
}
|
||||
#endif
|
||||
edges.emplace_back(diter->source, diter->target,
|
||||
static_cast<unsigned int>(std::max(diter->weight, 1)),
|
||||
1,
|
||||
diter->edge_id,
|
||||
false,
|
||||
diter->forward ? true : false,
|
||||
diter->backward ? true : false);
|
||||
|
||||
edges.emplace_back(diter->target, diter->source,
|
||||
static_cast<unsigned int>(std::max(diter->weight, 1)),
|
||||
1,
|
||||
diter->edge_id,
|
||||
false,
|
||||
diter->backward ? true : false,
|
||||
diter->forward ? true : false);
|
||||
}
|
||||
// clear input vector
|
||||
input_edge_list.clear();
|
||||
edges.shrink_to_fit();
|
||||
|
||||
tbb::parallel_sort(edges.begin(), edges.end());
|
||||
NodeID edge = 0;
|
||||
for (NodeID i = 0; i < edges.size();)
|
||||
{
|
||||
const NodeID source = edges[i].source;
|
||||
const NodeID target = edges[i].target;
|
||||
const NodeID id = edges[i].data.id;
|
||||
// remove eigenloops
|
||||
if (source == target)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
ContractorEdge forward_edge;
|
||||
ContractorEdge reverse_edge;
|
||||
forward_edge.source = reverse_edge.source = source;
|
||||
forward_edge.target = reverse_edge.target = target;
|
||||
forward_edge.data.forward = reverse_edge.data.backward = true;
|
||||
forward_edge.data.backward = reverse_edge.data.forward = false;
|
||||
forward_edge.data.shortcut = reverse_edge.data.shortcut = false;
|
||||
forward_edge.data.id = reverse_edge.data.id = id;
|
||||
forward_edge.data.originalEdges = reverse_edge.data.originalEdges = 1;
|
||||
forward_edge.data.distance = reverse_edge.data.distance =
|
||||
std::numeric_limits<int>::max();
|
||||
// remove parallel edges
|
||||
while (i < edges.size() && edges[i].source == source && edges[i].target == target)
|
||||
{
|
||||
if (edges[i].data.forward)
|
||||
{
|
||||
forward_edge.data.distance =
|
||||
std::min(edges[i].data.distance, forward_edge.data.distance);
|
||||
}
|
||||
if (edges[i].data.backward)
|
||||
{
|
||||
reverse_edge.data.distance =
|
||||
std::min(edges[i].data.distance, reverse_edge.data.distance);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
// merge edges (s,t) and (t,s) into bidirectional edge
|
||||
if (forward_edge.data.distance == reverse_edge.data.distance)
|
||||
{
|
||||
if ((int)forward_edge.data.distance != std::numeric_limits<int>::max())
|
||||
{
|
||||
forward_edge.data.backward = true;
|
||||
edges[edge++] = forward_edge;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // insert seperate edges
|
||||
if (((int)forward_edge.data.distance) != std::numeric_limits<int>::max())
|
||||
{
|
||||
edges[edge++] = forward_edge;
|
||||
}
|
||||
if ((int)reverse_edge.data.distance != std::numeric_limits<int>::max())
|
||||
{
|
||||
edges[edge++] = reverse_edge;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::cout << "merged " << edges.size() - edge << " edges out of " << edges.size()
|
||||
<< std::endl;
|
||||
edges.resize(edge);
|
||||
contractor_graph = std::make_shared<ContractorGraph>(nodes, edges);
|
||||
edges.clear();
|
||||
edges.shrink_to_fit();
|
||||
|
||||
BOOST_ASSERT(0 == edges.capacity());
|
||||
// unsigned maxdegree = 0;
|
||||
// NodeID highestNode = 0;
|
||||
//
|
||||
// for(unsigned i = 0; i < contractor_graph->GetNumberOfNodes(); ++i) {
|
||||
// unsigned degree = contractor_graph->EndEdges(i) -
|
||||
// contractor_graph->BeginEdges(i);
|
||||
// if(degree > maxdegree) {
|
||||
// maxdegree = degree;
|
||||
// highestNode = i;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// SimpleLogger().Write() << "edges at node with id " << highestNode << " has degree
|
||||
// " << maxdegree;
|
||||
// for(unsigned i = contractor_graph->BeginEdges(highestNode); i <
|
||||
// contractor_graph->EndEdges(highestNode); ++i) {
|
||||
// SimpleLogger().Write() << " ->(" << highestNode << "," <<
|
||||
// contractor_graph->GetTarget(i)
|
||||
// << "); via: " << contractor_graph->GetEdgeData(i).via;
|
||||
// }
|
||||
|
||||
std::cout << "contractor finished initalization" << std::endl;
|
||||
}
|
||||
|
||||
~Contractor() { }
|
||||
|
||||
void Run()
|
||||
{
|
||||
// for the preperation we can use a big grain size, which is much faster (probably cache)
|
||||
constexpr size_t InitGrainSize = 100000;
|
||||
constexpr size_t PQGrainSize = 100000;
|
||||
// auto_partitioner will automatically increase the blocksize if we have
|
||||
// a lot of data. It is *important* for the last loop iterations
|
||||
// (which have a very small dataset) that it is devisible.
|
||||
constexpr size_t IndependentGrainSize = 1;
|
||||
constexpr size_t ContractGrainSize = 1;
|
||||
constexpr size_t NeighboursGrainSize = 1;
|
||||
constexpr size_t DeleteGrainSize = 1;
|
||||
|
||||
const NodeID number_of_nodes = contractor_graph->GetNumberOfNodes();
|
||||
Percent p(number_of_nodes);
|
||||
|
||||
ThreadDataContainer thread_data_list(number_of_nodes);
|
||||
|
||||
NodeID number_of_contracted_nodes = 0;
|
||||
std::vector<RemainingNodeData> remaining_nodes(number_of_nodes);
|
||||
std::vector<float> node_priorities(number_of_nodes);
|
||||
std::vector<NodePriorityData> node_data(number_of_nodes);
|
||||
|
||||
|
||||
// initialize priorities in parallel
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, number_of_nodes, InitGrainSize),
|
||||
[&remaining_nodes](const tbb::blocked_range<int>& range)
|
||||
{
|
||||
for (int x = range.begin(); x != range.end(); ++x)
|
||||
{
|
||||
remaining_nodes[x].id = x;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
std::cout << "initializing elimination PQ ..." << std::flush;
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, number_of_nodes, PQGrainSize),
|
||||
[this, &node_priorities, &node_data, &thread_data_list](const tbb::blocked_range<int>& range)
|
||||
{
|
||||
ContractorThreadData *data = thread_data_list.getThreadData();
|
||||
for (int x = range.begin(); x != range.end(); ++x)
|
||||
{
|
||||
node_priorities[x] = this->EvaluateNodePriority(data, &node_data[x], x);
|
||||
}
|
||||
}
|
||||
);
|
||||
std::cout << "ok" << std::endl << "preprocessing " << number_of_nodes << " nodes ..."
|
||||
<< std::flush;
|
||||
|
||||
bool flushed_contractor = false;
|
||||
while (number_of_nodes > 2 && number_of_contracted_nodes < number_of_nodes)
|
||||
{
|
||||
if (!flushed_contractor && (number_of_contracted_nodes > (number_of_nodes * 0.65)))
|
||||
{
|
||||
DeallocatingVector<ContractorEdge> new_edge_set; // this one is not explicitely
|
||||
// cleared since it goes out of
|
||||
// scope anywa
|
||||
std::cout << " [flush " << number_of_contracted_nodes << " nodes] " << std::flush;
|
||||
|
||||
// Delete old heap data to free memory that we need for the coming operations
|
||||
thread_data_list.data.clear();
|
||||
|
||||
// Create new priority array
|
||||
std::vector<float> new_node_priority(remaining_nodes.size());
|
||||
// this map gives the old IDs from the new ones, necessary to get a consistent graph
|
||||
// at the end of contraction
|
||||
orig_node_id_to_new_id_map.resize(remaining_nodes.size());
|
||||
// this map gives the new IDs from the old ones, necessary to remap targets from the
|
||||
// remaining graph
|
||||
std::vector<NodeID> new_node_id_from_orig_id_map(number_of_nodes, UINT_MAX);
|
||||
|
||||
// build forward and backward renumbering map and remap ids in remaining_nodes and
|
||||
// Priorities.
|
||||
for (const auto new_node_id : osrm::irange<std::size_t>(0, remaining_nodes.size()))
|
||||
{
|
||||
// create renumbering maps in both directions
|
||||
orig_node_id_to_new_id_map[new_node_id] = remaining_nodes[new_node_id].id;
|
||||
new_node_id_from_orig_id_map[remaining_nodes[new_node_id].id] = new_node_id;
|
||||
new_node_priority[new_node_id] =
|
||||
node_priorities[remaining_nodes[new_node_id].id];
|
||||
remaining_nodes[new_node_id].id = new_node_id;
|
||||
}
|
||||
// walk over all nodes
|
||||
for (const auto i : osrm::irange<std::size_t>(0, contractor_graph->GetNumberOfNodes()))
|
||||
{
|
||||
const NodeID source = i;
|
||||
for (auto current_edge : contractor_graph->GetAdjacentEdgeRange(source))
|
||||
{
|
||||
ContractorGraph::EdgeData &data =
|
||||
contractor_graph->GetEdgeData(current_edge);
|
||||
const NodeID target = contractor_graph->GetTarget(current_edge);
|
||||
if (SPECIAL_NODEID == new_node_id_from_orig_id_map[i])
|
||||
{
|
||||
external_edge_list.push_back({source, target, data});
|
||||
}
|
||||
else
|
||||
{
|
||||
// node is not yet contracted.
|
||||
// add (renumbered) outgoing edges to new DynamicGraph.
|
||||
ContractorEdge new_edge = {
|
||||
new_node_id_from_orig_id_map[source],
|
||||
new_node_id_from_orig_id_map[target],
|
||||
data
|
||||
};
|
||||
|
||||
new_edge.data.is_original_via_node_ID = true;
|
||||
BOOST_ASSERT_MSG(UINT_MAX != new_node_id_from_orig_id_map[source],
|
||||
"new source id not resolveable");
|
||||
BOOST_ASSERT_MSG(UINT_MAX != new_node_id_from_orig_id_map[target],
|
||||
"new target id not resolveable");
|
||||
new_edge_set.push_back(new_edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Delete map from old NodeIDs to new ones.
|
||||
new_node_id_from_orig_id_map.clear();
|
||||
new_node_id_from_orig_id_map.shrink_to_fit();
|
||||
|
||||
// Replace old priorities array by new one
|
||||
node_priorities.swap(new_node_priority);
|
||||
// Delete old node_priorities vector
|
||||
new_node_priority.clear();
|
||||
new_node_priority.shrink_to_fit();
|
||||
// old Graph is removed
|
||||
contractor_graph.reset();
|
||||
|
||||
// create new graph
|
||||
std::sort(new_edge_set.begin(), new_edge_set.end());
|
||||
contractor_graph =
|
||||
std::make_shared<ContractorGraph>(remaining_nodes.size(), new_edge_set);
|
||||
|
||||
new_edge_set.clear();
|
||||
flushed_contractor = true;
|
||||
|
||||
// INFO: MAKE SURE THIS IS THE LAST OPERATION OF THE FLUSH!
|
||||
// reinitialize heaps and ThreadData objects with appropriate size
|
||||
thread_data_list.number_of_nodes = contractor_graph->GetNumberOfNodes();
|
||||
}
|
||||
|
||||
const int last = (int)remaining_nodes.size();
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, last, IndependentGrainSize),
|
||||
[this, &node_priorities, &remaining_nodes, &thread_data_list](const tbb::blocked_range<int>& range)
|
||||
{
|
||||
ContractorThreadData *data = thread_data_list.getThreadData();
|
||||
// determine independent node set
|
||||
for (int i = range.begin(); i != range.end(); ++i)
|
||||
{
|
||||
const NodeID node = remaining_nodes[i].id;
|
||||
remaining_nodes[i].is_independent =
|
||||
this->IsNodeIndependent(node_priorities, data, node);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const auto first = stable_partition(remaining_nodes.begin(),
|
||||
remaining_nodes.end(),
|
||||
[](RemainingNodeData node_data)
|
||||
{ return !node_data.is_independent; });
|
||||
const int first_independent_node = static_cast<int>(first - remaining_nodes.begin());
|
||||
|
||||
// contract independent nodes
|
||||
tbb::parallel_for(tbb::blocked_range<int>(first_independent_node, last, ContractGrainSize),
|
||||
[this, &remaining_nodes, &thread_data_list](const tbb::blocked_range<int>& range)
|
||||
{
|
||||
ContractorThreadData *data = thread_data_list.getThreadData();
|
||||
for (int position = range.begin(); position != range.end(); ++position)
|
||||
{
|
||||
const NodeID x = remaining_nodes[position].id;
|
||||
this->ContractNode<false>(data, x);
|
||||
}
|
||||
}
|
||||
);
|
||||
// make sure we really sort each block
|
||||
tbb::parallel_for(thread_data_list.data.range(),
|
||||
[&](const ThreadDataContainer::EnumerableThreadData::range_type& range)
|
||||
{
|
||||
for (auto& data : range)
|
||||
std::sort(data->inserted_edges.begin(),
|
||||
data->inserted_edges.end());
|
||||
}
|
||||
);
|
||||
tbb::parallel_for(tbb::blocked_range<int>(first_independent_node, last, DeleteGrainSize),
|
||||
[this, &remaining_nodes, &thread_data_list](const tbb::blocked_range<int>& range)
|
||||
{
|
||||
ContractorThreadData *data = thread_data_list.getThreadData();
|
||||
for (int position = range.begin(); position != range.end(); ++position)
|
||||
{
|
||||
const NodeID x = remaining_nodes[position].id;
|
||||
this->DeleteIncomingEdges(data, x);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// insert new edges
|
||||
for (auto& data : thread_data_list.data)
|
||||
{
|
||||
for (const ContractorEdge &edge : data->inserted_edges)
|
||||
{
|
||||
const EdgeID current_edge_ID = contractor_graph->FindEdge(edge.source, edge.target);
|
||||
if (current_edge_ID < contractor_graph->EndEdges(edge.source))
|
||||
{
|
||||
ContractorGraph::EdgeData ¤t_data =
|
||||
contractor_graph->GetEdgeData(current_edge_ID);
|
||||
if (current_data.shortcut && edge.data.forward == current_data.forward &&
|
||||
edge.data.backward == current_data.backward &&
|
||||
edge.data.distance < current_data.distance)
|
||||
{
|
||||
// found a duplicate edge with smaller weight, update it.
|
||||
current_data = edge.data;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
contractor_graph->InsertEdge(edge.source, edge.target, edge.data);
|
||||
}
|
||||
data->inserted_edges.clear();
|
||||
}
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<int>(first_independent_node, last, NeighboursGrainSize),
|
||||
[this, &remaining_nodes, &node_priorities, &node_data, &thread_data_list](const tbb::blocked_range<int>& range)
|
||||
{
|
||||
ContractorThreadData *data = thread_data_list.getThreadData();
|
||||
for (int position = range.begin(); position != range.end(); ++position)
|
||||
{
|
||||
NodeID x = remaining_nodes[position].id;
|
||||
this->UpdateNodeNeighbours(node_priorities, node_data, data, x);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// remove contracted nodes from the pool
|
||||
number_of_contracted_nodes += last - first_independent_node;
|
||||
remaining_nodes.resize(first_independent_node);
|
||||
remaining_nodes.shrink_to_fit();
|
||||
// unsigned maxdegree = 0;
|
||||
// unsigned avgdegree = 0;
|
||||
// unsigned mindegree = UINT_MAX;
|
||||
// unsigned quaddegree = 0;
|
||||
//
|
||||
// for(unsigned i = 0; i < remaining_nodes.size(); ++i) {
|
||||
// unsigned degree = contractor_graph->EndEdges(remaining_nodes[i].first)
|
||||
// -
|
||||
// contractor_graph->BeginEdges(remaining_nodes[i].first);
|
||||
// if(degree > maxdegree)
|
||||
// maxdegree = degree;
|
||||
// if(degree < mindegree)
|
||||
// mindegree = degree;
|
||||
//
|
||||
// avgdegree += degree;
|
||||
// quaddegree += (degree*degree);
|
||||
// }
|
||||
//
|
||||
// avgdegree /= std::max((unsigned)1,(unsigned)remaining_nodes.size() );
|
||||
// quaddegree /= std::max((unsigned)1,(unsigned)remaining_nodes.size() );
|
||||
//
|
||||
// SimpleLogger().Write() << "rest: " << remaining_nodes.size() << ", max: "
|
||||
// << maxdegree << ", min: " << mindegree << ", avg: " << avgdegree << ",
|
||||
// quad: " << quaddegree;
|
||||
|
||||
p.printStatus(number_of_contracted_nodes);
|
||||
}
|
||||
|
||||
thread_data_list.data.clear();
|
||||
}
|
||||
|
||||
template <class Edge> inline void GetEdges(DeallocatingVector<Edge> &edges)
|
||||
{
|
||||
Percent p(contractor_graph->GetNumberOfNodes());
|
||||
SimpleLogger().Write() << "Getting edges of minimized graph";
|
||||
const NodeID number_of_nodes = contractor_graph->GetNumberOfNodes();
|
||||
if (contractor_graph->GetNumberOfNodes())
|
||||
{
|
||||
Edge new_edge;
|
||||
for (const auto node : osrm::irange(0u, number_of_nodes))
|
||||
{
|
||||
p.printStatus(node);
|
||||
for (auto edge : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const NodeID target = contractor_graph->GetTarget(edge);
|
||||
const ContractorGraph::EdgeData &data = contractor_graph->GetEdgeData(edge);
|
||||
if (!orig_node_id_to_new_id_map.empty())
|
||||
{
|
||||
new_edge.source = orig_node_id_to_new_id_map[node];
|
||||
new_edge.target = orig_node_id_to_new_id_map[target];
|
||||
}
|
||||
else
|
||||
{
|
||||
new_edge.source = node;
|
||||
new_edge.target = target;
|
||||
}
|
||||
BOOST_ASSERT_MSG(UINT_MAX != new_edge.source, "Source id invalid");
|
||||
BOOST_ASSERT_MSG(UINT_MAX != new_edge.target, "Target id invalid");
|
||||
new_edge.data.distance = data.distance;
|
||||
new_edge.data.shortcut = data.shortcut;
|
||||
if (!data.is_original_via_node_ID && !orig_node_id_to_new_id_map.empty())
|
||||
{
|
||||
new_edge.data.id = orig_node_id_to_new_id_map[data.id];
|
||||
}
|
||||
else
|
||||
{
|
||||
new_edge.data.id = data.id;
|
||||
}
|
||||
BOOST_ASSERT_MSG(new_edge.data.id != INT_MAX, // 2^31
|
||||
"edge id invalid");
|
||||
new_edge.data.forward = data.forward;
|
||||
new_edge.data.backward = data.backward;
|
||||
edges.push_back(new_edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
contractor_graph.reset();
|
||||
orig_node_id_to_new_id_map.clear();
|
||||
orig_node_id_to_new_id_map.shrink_to_fit();
|
||||
|
||||
BOOST_ASSERT(0 == orig_node_id_to_new_id_map.capacity());
|
||||
|
||||
edges.append(external_edge_list.begin(), external_edge_list.end());
|
||||
external_edge_list.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
inline void Dijkstra(const int max_distance,
|
||||
const unsigned number_of_targets,
|
||||
const int maxNodes,
|
||||
ContractorThreadData *const data,
|
||||
const NodeID middleNode)
|
||||
{
|
||||
|
||||
ContractorHeap &heap = data->heap;
|
||||
|
||||
int nodes = 0;
|
||||
unsigned number_of_targets_found = 0;
|
||||
while (!heap.Empty())
|
||||
{
|
||||
const NodeID node = heap.DeleteMin();
|
||||
const int distance = heap.GetKey(node);
|
||||
const short current_hop = heap.GetData(node).hop + 1;
|
||||
|
||||
if (++nodes > maxNodes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (distance > max_distance)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Destination settled?
|
||||
if (heap.GetData(node).target)
|
||||
{
|
||||
++number_of_targets_found;
|
||||
if (number_of_targets_found >= number_of_targets)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// iterate over all edges of node
|
||||
for (auto edge : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const ContractorEdgeData &data = contractor_graph->GetEdgeData(edge);
|
||||
if (!data.forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID to = contractor_graph->GetTarget(edge);
|
||||
if (middleNode == to)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const int to_distance = distance + data.distance;
|
||||
|
||||
// New Node discovered -> Add to Heap + Node Info Storage
|
||||
if (!heap.WasInserted(to))
|
||||
{
|
||||
heap.Insert(to, to_distance, ContractorHeapData(current_hop, false));
|
||||
}
|
||||
// Found a shorter Path -> Update distance
|
||||
else if (to_distance < heap.GetKey(to))
|
||||
{
|
||||
heap.DecreaseKey(to, to_distance);
|
||||
heap.GetData(to).hop = current_hop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline float EvaluateNodePriority(ContractorThreadData *const data,
|
||||
NodePriorityData *const node_data,
|
||||
const NodeID node)
|
||||
{
|
||||
ContractionStats stats;
|
||||
|
||||
// perform simulated contraction
|
||||
ContractNode<true>(data, node, &stats);
|
||||
|
||||
// Result will contain the priority
|
||||
float result;
|
||||
if (0 == (stats.edges_deleted_count * stats.original_edges_deleted_count))
|
||||
{
|
||||
result = 1.f * node_data->depth;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = 2.f * (((float)stats.edges_added_count) / stats.edges_deleted_count) +
|
||||
4.f * (((float)stats.original_edges_added_count) /
|
||||
stats.original_edges_deleted_count) +
|
||||
1.f * node_data->depth;
|
||||
}
|
||||
BOOST_ASSERT(result >= 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <bool RUNSIMULATION>
|
||||
inline bool
|
||||
ContractNode(ContractorThreadData *data, const NodeID node, ContractionStats *stats = nullptr)
|
||||
{
|
||||
ContractorHeap &heap = data->heap;
|
||||
int inserted_edges_size = data->inserted_edges.size();
|
||||
std::vector<ContractorEdge> &inserted_edges = data->inserted_edges;
|
||||
|
||||
for (auto in_edge : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const ContractorEdgeData &in_data = contractor_graph->GetEdgeData(in_edge);
|
||||
const NodeID source = contractor_graph->GetTarget(in_edge);
|
||||
if (RUNSIMULATION)
|
||||
{
|
||||
BOOST_ASSERT(stats != nullptr);
|
||||
++stats->edges_deleted_count;
|
||||
stats->original_edges_deleted_count += in_data.originalEdges;
|
||||
}
|
||||
if (!in_data.backward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
heap.Clear();
|
||||
heap.Insert(source, 0, ContractorHeapData());
|
||||
int max_distance = 0;
|
||||
unsigned number_of_targets = 0;
|
||||
|
||||
for (auto out_edge : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const ContractorEdgeData &out_data = contractor_graph->GetEdgeData(out_edge);
|
||||
if (!out_data.forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID target = contractor_graph->GetTarget(out_edge);
|
||||
const int path_distance = in_data.distance + out_data.distance;
|
||||
max_distance = std::max(max_distance, path_distance);
|
||||
if (!heap.WasInserted(target))
|
||||
{
|
||||
heap.Insert(target, INT_MAX, ContractorHeapData(0, true));
|
||||
++number_of_targets;
|
||||
}
|
||||
}
|
||||
|
||||
if (RUNSIMULATION)
|
||||
{
|
||||
Dijkstra(max_distance, number_of_targets, 1000, data, node);
|
||||
}
|
||||
else
|
||||
{
|
||||
Dijkstra(max_distance, number_of_targets, 2000, data, node);
|
||||
}
|
||||
for (auto out_edge : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const ContractorEdgeData &out_data = contractor_graph->GetEdgeData(out_edge);
|
||||
if (!out_data.forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID target = contractor_graph->GetTarget(out_edge);
|
||||
const int path_distance = in_data.distance + out_data.distance;
|
||||
const int distance = heap.GetKey(target);
|
||||
if (path_distance < distance)
|
||||
{
|
||||
if (RUNSIMULATION)
|
||||
{
|
||||
BOOST_ASSERT(stats != nullptr);
|
||||
stats->edges_added_count += 2;
|
||||
stats->original_edges_added_count +=
|
||||
2 * (out_data.originalEdges + in_data.originalEdges);
|
||||
}
|
||||
else
|
||||
{
|
||||
inserted_edges.emplace_back(source, target, path_distance,
|
||||
out_data.originalEdges + in_data.originalEdges,
|
||||
node,
|
||||
true,
|
||||
true,
|
||||
false);
|
||||
|
||||
inserted_edges.emplace_back(target, source, path_distance,
|
||||
out_data.originalEdges + in_data.originalEdges,
|
||||
node,
|
||||
true,
|
||||
false,
|
||||
true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!RUNSIMULATION)
|
||||
{
|
||||
int iend = inserted_edges.size();
|
||||
for (int i = inserted_edges_size; i < iend; ++i)
|
||||
{
|
||||
bool found = false;
|
||||
for (int other = i + 1; other < iend; ++other)
|
||||
{
|
||||
if (inserted_edges[other].source != inserted_edges[i].source)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (inserted_edges[other].target != inserted_edges[i].target)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (inserted_edges[other].data.distance != inserted_edges[i].data.distance)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (inserted_edges[other].data.shortcut != inserted_edges[i].data.shortcut)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
inserted_edges[other].data.forward |= inserted_edges[i].data.forward;
|
||||
inserted_edges[other].data.backward |= inserted_edges[i].data.backward;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
if (!found)
|
||||
{
|
||||
inserted_edges[inserted_edges_size++] = inserted_edges[i];
|
||||
}
|
||||
}
|
||||
inserted_edges.resize(inserted_edges_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void DeleteIncomingEdges(ContractorThreadData *data, const NodeID node)
|
||||
{
|
||||
std::vector<NodeID> &neighbours = data->neighbours;
|
||||
neighbours.clear();
|
||||
|
||||
// find all neighbours
|
||||
for (auto e : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const NodeID u = contractor_graph->GetTarget(e);
|
||||
if (u != node)
|
||||
{
|
||||
neighbours.push_back(u);
|
||||
}
|
||||
}
|
||||
// eliminate duplicate entries ( forward + backward edges )
|
||||
std::sort(neighbours.begin(), neighbours.end());
|
||||
neighbours.resize(std::unique(neighbours.begin(), neighbours.end()) - neighbours.begin());
|
||||
|
||||
for (const auto i : osrm::irange<std::size_t>(0, neighbours.size()))
|
||||
{
|
||||
contractor_graph->DeleteEdgesTo(neighbours[i], node);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool UpdateNodeNeighbours(std::vector<float> &priorities,
|
||||
std::vector<NodePriorityData> &node_data,
|
||||
ContractorThreadData *const data,
|
||||
const NodeID node)
|
||||
{
|
||||
std::vector<NodeID> &neighbours = data->neighbours;
|
||||
neighbours.clear();
|
||||
|
||||
// find all neighbours
|
||||
for (auto e : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const NodeID u = contractor_graph->GetTarget(e);
|
||||
if (u == node)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
neighbours.push_back(u);
|
||||
node_data[u].depth = (std::max)(node_data[node].depth + 1, node_data[u].depth);
|
||||
}
|
||||
// eliminate duplicate entries ( forward + backward edges )
|
||||
std::sort(neighbours.begin(), neighbours.end());
|
||||
neighbours.resize(std::unique(neighbours.begin(), neighbours.end()) - neighbours.begin());
|
||||
|
||||
// re-evaluate priorities of neighboring nodes
|
||||
for (const NodeID u : neighbours)
|
||||
{
|
||||
priorities[u] = EvaluateNodePriority(data, &(node_data)[u], u);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool IsNodeIndependent(
|
||||
const std::vector<float> &priorities,
|
||||
ContractorThreadData *const data,
|
||||
NodeID node) const
|
||||
{
|
||||
const float priority = priorities[node];
|
||||
|
||||
std::vector<NodeID> &neighbours = data->neighbours;
|
||||
neighbours.clear();
|
||||
|
||||
for (auto e : contractor_graph->GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const NodeID target = contractor_graph->GetTarget(e);
|
||||
if (node == target)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const float target_priority = priorities[target];
|
||||
BOOST_ASSERT(target_priority >= 0);
|
||||
// found a neighbour with lower priority?
|
||||
if (priority > target_priority)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// tie breaking
|
||||
if (std::abs(priority - target_priority) < std::numeric_limits<float>::epsilon() &&
|
||||
bias(node, target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
neighbours.push_back(target);
|
||||
}
|
||||
|
||||
std::sort(neighbours.begin(), neighbours.end());
|
||||
neighbours.resize(std::unique(neighbours.begin(), neighbours.end()) - neighbours.begin());
|
||||
|
||||
// examine all neighbours that are at most 2 hops away
|
||||
for (const NodeID u : neighbours)
|
||||
{
|
||||
for (auto e : contractor_graph->GetAdjacentEdgeRange(u))
|
||||
{
|
||||
const NodeID target = contractor_graph->GetTarget(e);
|
||||
if (node == target)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const float target_priority = priorities[target];
|
||||
BOOST_ASSERT(target_priority >= 0);
|
||||
// found a neighbour with lower priority?
|
||||
if (priority > target_priority)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// tie breaking
|
||||
if (std::abs(priority - target_priority) < std::numeric_limits<float>::epsilon() &&
|
||||
bias(node, target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// This bias function takes up 22 assembly instructions in total on X86
|
||||
inline bool bias(const NodeID a, const NodeID b) const
|
||||
{
|
||||
const unsigned short hasha = fast_hash(a);
|
||||
const unsigned short hashb = fast_hash(b);
|
||||
|
||||
// The compiler optimizes that to conditional register flags but without branching
|
||||
// statements!
|
||||
if (hasha != hashb)
|
||||
{
|
||||
return hasha < hashb;
|
||||
}
|
||||
return a < b;
|
||||
}
|
||||
|
||||
std::shared_ptr<ContractorGraph> contractor_graph;
|
||||
std::vector<ContractorGraph::InputEdge> contracted_edge_list;
|
||||
stxxl::vector<QueryEdge> external_edge_list;
|
||||
std::vector<NodeID> orig_node_id_to_new_id_map;
|
||||
XORFastHash fast_hash;
|
||||
};
|
||||
|
||||
#endif // CONTRACTOR_HPP
|
||||
@@ -0,0 +1,782 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include "edge_based_graph_factory.hpp"
|
||||
#include "../algorithms/bfs_components.hpp"
|
||||
#include "../DataStructures/Percent.h"
|
||||
#include "../Util/compute_angle.hpp"
|
||||
#include "../Util/integer_range.hpp"
|
||||
#include "../Util/lua_util.hpp"
|
||||
#include "../Util/simple_logger.hpp"
|
||||
#include "../Util/TimingUtil.h"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
|
||||
EdgeBasedGraphFactory::EdgeBasedGraphFactory(
|
||||
const std::shared_ptr<NodeBasedDynamicGraph> &node_based_graph,
|
||||
std::unique_ptr<RestrictionMap> restriction_map,
|
||||
std::vector<NodeID> &barrier_node_list,
|
||||
std::vector<NodeID> &traffic_light_node_list,
|
||||
std::vector<QueryNode> &node_info_list,
|
||||
SpeedProfileProperties &speed_profile)
|
||||
: speed_profile(speed_profile),
|
||||
m_number_of_edge_based_nodes(std::numeric_limits<unsigned>::max()),
|
||||
m_node_info_list(node_info_list), m_node_based_graph(node_based_graph),
|
||||
m_restriction_map(std::move(restriction_map)), max_id(0)
|
||||
{
|
||||
|
||||
// insert into unordered sets for fast lookup
|
||||
m_barrier_nodes.insert(barrier_node_list.begin(), barrier_node_list.end());
|
||||
m_traffic_lights.insert(traffic_light_node_list.begin(), traffic_light_node_list.end());
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedEdges(DeallocatingVector<EdgeBasedEdge> &output_edge_list)
|
||||
{
|
||||
BOOST_ASSERT_MSG(0 == output_edge_list.size(), "Vector is not empty");
|
||||
m_edge_based_edge_list.swap(output_edge_list);
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedNodes(std::vector<EdgeBasedNode> &nodes)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
for (const EdgeBasedNode &node : m_edge_based_node_list)
|
||||
{
|
||||
BOOST_ASSERT(m_node_info_list.at(node.u).lat != INT_MAX);
|
||||
BOOST_ASSERT(m_node_info_list.at(node.u).lon != INT_MAX);
|
||||
BOOST_ASSERT(m_node_info_list.at(node.v).lon != INT_MAX);
|
||||
BOOST_ASSERT(m_node_info_list.at(node.v).lat != INT_MAX);
|
||||
}
|
||||
#endif
|
||||
nodes.swap(m_edge_based_node_list);
|
||||
}
|
||||
|
||||
void
|
||||
EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u, const NodeID node_v, const bool belongs_to_tiny_cc)
|
||||
{
|
||||
// merge edges together into one EdgeBasedNode
|
||||
BOOST_ASSERT(node_u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(node_v != SPECIAL_NODEID);
|
||||
|
||||
// find forward edge id and
|
||||
const EdgeID e1 = m_node_based_graph->FindEdge(node_u, node_v);
|
||||
BOOST_ASSERT(e1 != SPECIAL_EDGEID);
|
||||
|
||||
const EdgeData &forward_data = m_node_based_graph->GetEdgeData(e1);
|
||||
|
||||
// find reverse edge id and
|
||||
const EdgeID e2 = m_node_based_graph->FindEdge(node_v, node_u);
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (e2 == m_node_based_graph->EndEdges(node_v))
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "Did not find edge (" << node_v << "," << node_u << ")";
|
||||
}
|
||||
#endif
|
||||
BOOST_ASSERT(e2 != SPECIAL_EDGEID);
|
||||
BOOST_ASSERT(e2 < m_node_based_graph->EndEdges(node_v));
|
||||
const EdgeData &reverse_data = m_node_based_graph->GetEdgeData(e2);
|
||||
|
||||
if (forward_data.edgeBasedNodeID == SPECIAL_NODEID &&
|
||||
reverse_data.edgeBasedNodeID == SPECIAL_NODEID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(m_geometry_compressor.HasEntryForID(e1) ==
|
||||
m_geometry_compressor.HasEntryForID(e2));
|
||||
if (m_geometry_compressor.HasEntryForID(e1))
|
||||
{
|
||||
BOOST_ASSERT(m_geometry_compressor.HasEntryForID(e2));
|
||||
|
||||
// reconstruct geometry and put in each individual edge with its offset
|
||||
const std::vector<GeometryCompressor::CompressedNode> &forward_geometry =
|
||||
m_geometry_compressor.GetBucketReference(e1);
|
||||
const std::vector<GeometryCompressor::CompressedNode> &reverse_geometry =
|
||||
m_geometry_compressor.GetBucketReference(e2);
|
||||
BOOST_ASSERT(forward_geometry.size() == reverse_geometry.size());
|
||||
BOOST_ASSERT(0 != forward_geometry.size());
|
||||
const unsigned geometry_size = static_cast<unsigned>(forward_geometry.size());
|
||||
BOOST_ASSERT(geometry_size > 1);
|
||||
|
||||
// reconstruct bidirectional edge with individual weights and put each into the NN index
|
||||
|
||||
std::vector<int> forward_dist_prefix_sum(forward_geometry.size(), 0);
|
||||
std::vector<int> reverse_dist_prefix_sum(reverse_geometry.size(), 0);
|
||||
|
||||
// quick'n'dirty prefix sum as std::partial_sum needs addtional casts
|
||||
// TODO: move to lambda function with C++11
|
||||
int temp_sum = 0;
|
||||
|
||||
for (const auto i : osrm::irange(0u, geometry_size))
|
||||
{
|
||||
forward_dist_prefix_sum[i] = temp_sum;
|
||||
temp_sum += forward_geometry[i].second;
|
||||
|
||||
BOOST_ASSERT(forward_data.distance >= temp_sum);
|
||||
}
|
||||
|
||||
temp_sum = 0;
|
||||
for (const auto i : osrm::irange(0u, geometry_size))
|
||||
{
|
||||
temp_sum += reverse_geometry[reverse_geometry.size() - 1 - i].second;
|
||||
reverse_dist_prefix_sum[i] = reverse_data.distance - temp_sum;
|
||||
// BOOST_ASSERT(reverse_data.distance >= temp_sum);
|
||||
}
|
||||
|
||||
NodeID current_edge_source_coordinate_id = node_u;
|
||||
|
||||
if (SPECIAL_NODEID != forward_data.edgeBasedNodeID)
|
||||
{
|
||||
max_id = std::max(forward_data.edgeBasedNodeID, max_id);
|
||||
}
|
||||
if (SPECIAL_NODEID != reverse_data.edgeBasedNodeID)
|
||||
{
|
||||
max_id = std::max(reverse_data.edgeBasedNodeID, max_id);
|
||||
}
|
||||
|
||||
// traverse arrays from start and end respectively
|
||||
for (const auto i : osrm::irange(0u, geometry_size))
|
||||
{
|
||||
BOOST_ASSERT(current_edge_source_coordinate_id ==
|
||||
reverse_geometry[geometry_size - 1 - i].first);
|
||||
const NodeID current_edge_target_coordinate_id = forward_geometry[i].first;
|
||||
BOOST_ASSERT(current_edge_target_coordinate_id != current_edge_source_coordinate_id);
|
||||
|
||||
// build edges
|
||||
m_edge_based_node_list.emplace_back(forward_data.edgeBasedNodeID,
|
||||
reverse_data.edgeBasedNodeID,
|
||||
current_edge_source_coordinate_id,
|
||||
current_edge_target_coordinate_id,
|
||||
forward_data.nameID,
|
||||
forward_geometry[i].second,
|
||||
reverse_geometry[geometry_size - 1 - i].second,
|
||||
forward_dist_prefix_sum[i],
|
||||
reverse_dist_prefix_sum[i],
|
||||
m_geometry_compressor.GetPositionForID(e1),
|
||||
i,
|
||||
belongs_to_tiny_cc,
|
||||
forward_data.travel_mode,
|
||||
reverse_data.travel_mode);
|
||||
current_edge_source_coordinate_id = current_edge_target_coordinate_id;
|
||||
|
||||
BOOST_ASSERT(m_edge_based_node_list.back().IsCompressed());
|
||||
|
||||
BOOST_ASSERT(node_u != m_edge_based_node_list.back().u ||
|
||||
node_v != m_edge_based_node_list.back().v);
|
||||
|
||||
BOOST_ASSERT(node_u != m_edge_based_node_list.back().v ||
|
||||
node_v != m_edge_based_node_list.back().u);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(current_edge_source_coordinate_id == node_v);
|
||||
BOOST_ASSERT(m_edge_based_node_list.back().IsCompressed());
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(!m_geometry_compressor.HasEntryForID(e2));
|
||||
|
||||
if (forward_data.edgeBasedNodeID != SPECIAL_NODEID)
|
||||
{
|
||||
BOOST_ASSERT(forward_data.forward);
|
||||
}
|
||||
if (reverse_data.edgeBasedNodeID != SPECIAL_NODEID)
|
||||
{
|
||||
BOOST_ASSERT(reverse_data.forward);
|
||||
}
|
||||
if (forward_data.edgeBasedNodeID == SPECIAL_NODEID)
|
||||
{
|
||||
BOOST_ASSERT(!forward_data.forward);
|
||||
}
|
||||
if (reverse_data.edgeBasedNodeID == SPECIAL_NODEID)
|
||||
{
|
||||
BOOST_ASSERT(!reverse_data.forward);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(forward_data.edgeBasedNodeID != SPECIAL_NODEID ||
|
||||
reverse_data.edgeBasedNodeID != SPECIAL_NODEID);
|
||||
|
||||
m_edge_based_node_list.emplace_back(forward_data.edgeBasedNodeID,
|
||||
reverse_data.edgeBasedNodeID,
|
||||
node_u,
|
||||
node_v,
|
||||
forward_data.nameID,
|
||||
forward_data.distance,
|
||||
reverse_data.distance,
|
||||
0,
|
||||
0,
|
||||
SPECIAL_EDGEID,
|
||||
0,
|
||||
belongs_to_tiny_cc,
|
||||
forward_data.travel_mode,
|
||||
reverse_data.travel_mode);
|
||||
BOOST_ASSERT(!m_edge_based_node_list.back().IsCompressed());
|
||||
}
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::FlushVectorToStream(
|
||||
std::ofstream &edge_data_file, std::vector<OriginalEdgeData> &original_edge_data_vector) const
|
||||
{
|
||||
if (original_edge_data_vector.empty()) {
|
||||
return;
|
||||
}
|
||||
edge_data_file.write((char *)&(original_edge_data_vector[0]),
|
||||
original_edge_data_vector.size() * sizeof(OriginalEdgeData));
|
||||
original_edge_data_vector.clear();
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename,
|
||||
const std::string &geometry_filename,
|
||||
lua_State *lua_state)
|
||||
{
|
||||
|
||||
TIMER_START(geometry);
|
||||
CompressGeometry();
|
||||
TIMER_STOP(geometry);
|
||||
|
||||
TIMER_START(renumber);
|
||||
RenumberEdges();
|
||||
TIMER_STOP(renumber);
|
||||
|
||||
TIMER_START(generate_nodes);
|
||||
GenerateEdgeExpandedNodes();
|
||||
TIMER_STOP(generate_nodes);
|
||||
|
||||
TIMER_START(generate_edges);
|
||||
GenerateEdgeExpandedEdges(original_edge_data_filename, lua_state);
|
||||
TIMER_STOP(generate_edges);
|
||||
|
||||
m_geometry_compressor.SerializeInternalVector(geometry_filename);
|
||||
|
||||
SimpleLogger().Write() << "Timing statistics for edge-expanded graph:";
|
||||
SimpleLogger().Write() << "Geometry compression: " << TIMER_SEC(geometry) << "s";
|
||||
SimpleLogger().Write() << "Renumbering edges: " << TIMER_SEC(renumber) << "s";
|
||||
SimpleLogger().Write() << "Generating nodes: " << TIMER_SEC(generate_nodes) << "s";
|
||||
SimpleLogger().Write() << "Generating edges: " << TIMER_SEC(generate_edges) << "s";
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::CompressGeometry()
|
||||
{
|
||||
SimpleLogger().Write() << "Removing graph geometry while preserving topology";
|
||||
|
||||
const unsigned original_number_of_nodes = m_node_based_graph->GetNumberOfNodes();
|
||||
const unsigned original_number_of_edges = m_node_based_graph->GetNumberOfEdges();
|
||||
|
||||
Percent progress(original_number_of_nodes);
|
||||
unsigned removed_node_count = 0;
|
||||
|
||||
for (const NodeID node_v : osrm::irange(0u, original_number_of_nodes))
|
||||
{
|
||||
progress.printStatus(node_v);
|
||||
|
||||
// only contract degree 2 vertices
|
||||
if (2 != m_node_based_graph->GetOutDegree(node_v))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't contract barrier node
|
||||
if (m_barrier_nodes.end() != m_barrier_nodes.find(node_v))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if v is a via node for a turn restriction, i.e. a 'directed' barrier node
|
||||
if (m_restriction_map->IsViaNode(node_v))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const bool reverse_edge_order =
|
||||
!(m_node_based_graph->GetEdgeData(m_node_based_graph->BeginEdges(node_v)).forward);
|
||||
const EdgeID forward_e2 = m_node_based_graph->BeginEdges(node_v) + reverse_edge_order;
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != forward_e2);
|
||||
const EdgeID reverse_e2 = m_node_based_graph->BeginEdges(node_v) + 1 - reverse_edge_order;
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != reverse_e2);
|
||||
|
||||
const EdgeData &fwd_edge_data2 = m_node_based_graph->GetEdgeData(forward_e2);
|
||||
const EdgeData &rev_edge_data2 = m_node_based_graph->GetEdgeData(reverse_e2);
|
||||
|
||||
const NodeID node_w = m_node_based_graph->GetTarget(forward_e2);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != node_w);
|
||||
BOOST_ASSERT(node_v != node_w);
|
||||
const NodeID node_u = m_node_based_graph->GetTarget(reverse_e2);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != node_u);
|
||||
BOOST_ASSERT(node_u != node_v);
|
||||
|
||||
const EdgeID forward_e1 = m_node_based_graph->FindEdge(node_u, node_v);
|
||||
BOOST_ASSERT(m_node_based_graph->EndEdges(node_u) != forward_e1);
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != forward_e1);
|
||||
BOOST_ASSERT(node_v == m_node_based_graph->GetTarget(forward_e1));
|
||||
const EdgeID reverse_e1 = m_node_based_graph->FindEdge(node_w, node_v);
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != reverse_e1);
|
||||
BOOST_ASSERT(node_v == m_node_based_graph->GetTarget(reverse_e1));
|
||||
|
||||
const EdgeData &fwd_edge_data1 = m_node_based_graph->GetEdgeData(forward_e1);
|
||||
const EdgeData &rev_edge_data1 = m_node_based_graph->GetEdgeData(reverse_e1);
|
||||
|
||||
if ((m_node_based_graph->FindEdge(node_u, node_w) != m_node_based_graph->EndEdges(node_u)) ||
|
||||
(m_node_based_graph->FindEdge(node_w, node_u) != m_node_based_graph->EndEdges(node_w)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( // TODO: rename to IsCompatibleTo
|
||||
fwd_edge_data1.IsEqualTo(fwd_edge_data2) &&
|
||||
rev_edge_data1.IsEqualTo(rev_edge_data2))
|
||||
{
|
||||
// Get distances before graph is modified
|
||||
const int forward_weight1 = m_node_based_graph->GetEdgeData(forward_e1).distance;
|
||||
const int forward_weight2 = m_node_based_graph->GetEdgeData(forward_e2).distance;
|
||||
|
||||
BOOST_ASSERT(0 != forward_weight1);
|
||||
BOOST_ASSERT(0 != forward_weight2);
|
||||
|
||||
const int reverse_weight1 = m_node_based_graph->GetEdgeData(reverse_e1).distance;
|
||||
const int reverse_weight2 = m_node_based_graph->GetEdgeData(reverse_e2).distance;
|
||||
|
||||
BOOST_ASSERT(0 != reverse_weight1);
|
||||
BOOST_ASSERT(0 != forward_weight2);
|
||||
|
||||
const bool add_traffic_signal_penalty =
|
||||
(m_traffic_lights.find(node_v) != m_traffic_lights.end());
|
||||
|
||||
// add weight of e2's to e1
|
||||
m_node_based_graph->GetEdgeData(forward_e1).distance += fwd_edge_data2.distance;
|
||||
m_node_based_graph->GetEdgeData(reverse_e1).distance += rev_edge_data2.distance;
|
||||
if (add_traffic_signal_penalty)
|
||||
{
|
||||
m_node_based_graph->GetEdgeData(forward_e1).distance +=
|
||||
speed_profile.traffic_signal_penalty;
|
||||
m_node_based_graph->GetEdgeData(reverse_e1).distance +=
|
||||
speed_profile.traffic_signal_penalty;
|
||||
}
|
||||
|
||||
// extend e1's to targets of e2's
|
||||
m_node_based_graph->SetTarget(forward_e1, node_w);
|
||||
m_node_based_graph->SetTarget(reverse_e1, node_u);
|
||||
|
||||
// remove e2's (if bidir, otherwise only one)
|
||||
m_node_based_graph->DeleteEdge(node_v, forward_e2);
|
||||
m_node_based_graph->DeleteEdge(node_v, reverse_e2);
|
||||
|
||||
// update any involved turn restrictions
|
||||
m_restriction_map->FixupStartingTurnRestriction(node_u, node_v, node_w);
|
||||
m_restriction_map->FixupArrivingTurnRestriction(node_u, node_v, node_w);
|
||||
|
||||
m_restriction_map->FixupStartingTurnRestriction(node_w, node_v, node_u);
|
||||
m_restriction_map->FixupArrivingTurnRestriction(node_w, node_v, node_u);
|
||||
|
||||
// store compressed geometry in container
|
||||
m_geometry_compressor.CompressEdge(
|
||||
forward_e1,
|
||||
forward_e2,
|
||||
node_v,
|
||||
node_w,
|
||||
forward_weight1 +
|
||||
(add_traffic_signal_penalty ? speed_profile.traffic_signal_penalty : 0),
|
||||
forward_weight2);
|
||||
m_geometry_compressor.CompressEdge(
|
||||
reverse_e1,
|
||||
reverse_e2,
|
||||
node_v,
|
||||
node_u,
|
||||
reverse_weight1,
|
||||
reverse_weight2 +
|
||||
(add_traffic_signal_penalty ? speed_profile.traffic_signal_penalty : 0));
|
||||
++removed_node_count;
|
||||
|
||||
BOOST_ASSERT(m_node_based_graph->GetEdgeData(forward_e1).nameID ==
|
||||
m_node_based_graph->GetEdgeData(reverse_e1).nameID);
|
||||
}
|
||||
}
|
||||
SimpleLogger().Write() << "removed " << removed_node_count << " nodes";
|
||||
m_geometry_compressor.PrintStatistics();
|
||||
|
||||
unsigned new_node_count = 0;
|
||||
unsigned new_edge_count = 0;
|
||||
|
||||
for(const auto i : osrm::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||
{
|
||||
if (m_node_based_graph->GetOutDegree(i) > 0)
|
||||
{
|
||||
++new_node_count;
|
||||
new_edge_count += (m_node_based_graph->EndEdges(i) - m_node_based_graph->BeginEdges(i));
|
||||
}
|
||||
}
|
||||
SimpleLogger().Write() << "new nodes: " << new_node_count << ", edges " << new_edge_count;
|
||||
SimpleLogger().Write() << "Node compression ratio: " << new_node_count /
|
||||
(double)original_number_of_nodes;
|
||||
SimpleLogger().Write() << "Edge compression ratio: " << new_edge_count /
|
||||
(double)original_number_of_edges;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the id of the edge in the edge expanded graph (into the edge in the node based graph)
|
||||
*/
|
||||
void EdgeBasedGraphFactory::RenumberEdges()
|
||||
{
|
||||
// renumber edge based node IDs
|
||||
unsigned numbered_edges_count = 0;
|
||||
for (NodeID current_node = 0; current_node < m_node_based_graph->GetNumberOfNodes();
|
||||
++current_node)
|
||||
{
|
||||
for (EdgeID current_edge : m_node_based_graph->GetAdjacentEdgeRange(current_node))
|
||||
{
|
||||
EdgeData &edge_data = m_node_based_graph->GetEdgeData(current_edge);
|
||||
if (!edge_data.forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(numbered_edges_count < m_node_based_graph->GetNumberOfEdges());
|
||||
edge_data.edgeBasedNodeID = numbered_edges_count;
|
||||
++numbered_edges_count;
|
||||
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data.edgeBasedNodeID);
|
||||
}
|
||||
}
|
||||
m_number_of_edge_based_nodes = numbered_edges_count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the nodes in the edge expanded graph from edges in the node-based graph.
|
||||
*/
|
||||
void EdgeBasedGraphFactory::GenerateEdgeExpandedNodes()
|
||||
{
|
||||
SimpleLogger().Write() << "Identifying components of the road network";
|
||||
|
||||
// Run a BFS on the undirected graph and identify small components
|
||||
BFSComponentExplorer<NodeBasedDynamicGraph> component_explorer(
|
||||
*m_node_based_graph, *m_restriction_map, m_barrier_nodes);
|
||||
|
||||
component_explorer.run();
|
||||
|
||||
SimpleLogger().Write() << "identified: " << component_explorer.GetNumberOfComponents()
|
||||
<< " many components";
|
||||
SimpleLogger().Write() << "generating edge-expanded nodes";
|
||||
|
||||
Percent progress(m_node_based_graph->GetNumberOfNodes());
|
||||
|
||||
// loop over all edges and generate new set of nodes
|
||||
for (NodeID u = 0, end = m_node_based_graph->GetNumberOfNodes(); u < end; ++u)
|
||||
{
|
||||
BOOST_ASSERT(u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(u < m_node_based_graph->GetNumberOfNodes());
|
||||
progress.printStatus(u);
|
||||
for (EdgeID e1 : m_node_based_graph->GetAdjacentEdgeRange(u))
|
||||
{
|
||||
const EdgeData &edge_data = m_node_based_graph->GetEdgeData(e1);
|
||||
BOOST_ASSERT(e1 != SPECIAL_EDGEID);
|
||||
const NodeID v = m_node_based_graph->GetTarget(e1);
|
||||
|
||||
BOOST_ASSERT(SPECIAL_NODEID != v);
|
||||
// pick only every other edge
|
||||
if (u > v)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(u < v);
|
||||
|
||||
// Note: edges that end on barrier nodes or on a turn restriction
|
||||
// may actually be in two distinct components. We choose the smallest
|
||||
const unsigned size_of_component = std::min(component_explorer.GetComponentSize(u),
|
||||
component_explorer.GetComponentSize(v));
|
||||
|
||||
const bool component_is_tiny = (size_of_component < 1000);
|
||||
if (edge_data.edgeBasedNodeID == SPECIAL_NODEID)
|
||||
{
|
||||
InsertEdgeBasedNode(v, u, component_is_tiny);
|
||||
}
|
||||
else
|
||||
{
|
||||
InsertEdgeBasedNode(u, v, component_is_tiny);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SimpleLogger().Write() << "Generated " << m_edge_based_node_list.size()
|
||||
<< " nodes in edge-expanded graph";
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually it also generates OriginalEdgeData and serializes them...
|
||||
*/
|
||||
void
|
||||
EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename,
|
||||
lua_State *lua_state)
|
||||
{
|
||||
SimpleLogger().Write() << "generating edge-expanded edges";
|
||||
|
||||
unsigned node_based_edge_counter = 0;
|
||||
unsigned original_edges_counter = 0;
|
||||
|
||||
std::ofstream edge_data_file(original_edge_data_filename.c_str(), std::ios::binary);
|
||||
|
||||
// writes a dummy value that is updated later
|
||||
edge_data_file.write((char *)&original_edges_counter, sizeof(unsigned));
|
||||
|
||||
std::vector<OriginalEdgeData> original_edge_data_vector;
|
||||
original_edge_data_vector.reserve(1024 * 1024);
|
||||
|
||||
// Loop over all turns and generate new set of edges.
|
||||
// Three nested loop look super-linear, but we are dealing with a (kind of)
|
||||
// linear number of turns only.
|
||||
unsigned restricted_turns_counter = 0;
|
||||
unsigned skipped_uturns_counter = 0;
|
||||
unsigned skipped_barrier_turns_counter = 0;
|
||||
unsigned compressed = 0;
|
||||
|
||||
Percent progress(m_node_based_graph->GetNumberOfNodes());
|
||||
|
||||
for (NodeID u = 0, end = m_node_based_graph->GetNumberOfNodes(); u < end; ++u)
|
||||
{
|
||||
progress.printStatus(u);
|
||||
for (const EdgeID e1 : m_node_based_graph->GetAdjacentEdgeRange(u))
|
||||
{
|
||||
if (!m_node_based_graph->GetEdgeData(e1).forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
++node_based_edge_counter;
|
||||
const NodeID v = m_node_based_graph->GetTarget(e1);
|
||||
const NodeID to_node_of_only_restriction =
|
||||
m_restriction_map->CheckForEmanatingIsOnlyTurn(u, v);
|
||||
const bool is_barrier_node = (m_barrier_nodes.find(v) != m_barrier_nodes.end());
|
||||
|
||||
for (EdgeID e2 : m_node_based_graph->GetAdjacentEdgeRange(v))
|
||||
{
|
||||
if (!m_node_based_graph->GetEdgeData(e2).forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID w = m_node_based_graph->GetTarget(e2);
|
||||
|
||||
if ((to_node_of_only_restriction != SPECIAL_NODEID) &&
|
||||
(w != to_node_of_only_restriction))
|
||||
{
|
||||
// We are at an only_-restriction but not at the right turn.
|
||||
++restricted_turns_counter;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_barrier_node)
|
||||
{
|
||||
if (u != w)
|
||||
{
|
||||
++skipped_barrier_turns_counter;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((u == w) && (m_node_based_graph->GetOutDegree(v) > 1))
|
||||
{
|
||||
++skipped_uturns_counter;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// only add an edge if turn is not a U-turn except when it is
|
||||
// at the end of a dead-end street
|
||||
if (m_restriction_map->CheckIfTurnIsRestricted(u, v, w) &&
|
||||
(to_node_of_only_restriction == SPECIAL_NODEID) &&
|
||||
(w != to_node_of_only_restriction))
|
||||
{
|
||||
// We are at an only_-restriction but not at the right turn.
|
||||
++restricted_turns_counter;
|
||||
continue;
|
||||
}
|
||||
|
||||
// only add an edge if turn is not prohibited
|
||||
const EdgeData &edge_data1 = m_node_based_graph->GetEdgeData(e1);
|
||||
const EdgeData &edge_data2 = m_node_based_graph->GetEdgeData(e2);
|
||||
|
||||
BOOST_ASSERT(edge_data1.edgeBasedNodeID != edge_data2.edgeBasedNodeID);
|
||||
BOOST_ASSERT(edge_data1.forward);
|
||||
BOOST_ASSERT(edge_data2.forward);
|
||||
|
||||
// the following is the core of the loop.
|
||||
unsigned distance = edge_data1.distance;
|
||||
if (m_traffic_lights.find(v) != m_traffic_lights.end())
|
||||
{
|
||||
distance += speed_profile.traffic_signal_penalty;
|
||||
}
|
||||
|
||||
// unpack last node of first segment if packed
|
||||
const auto first_coordinate = m_node_info_list[(m_geometry_compressor.HasEntryForID(e1) ?
|
||||
m_geometry_compressor.GetLastNodeIDOfBucket(e1) :
|
||||
u)];
|
||||
|
||||
// unpack first node of second segment if packed
|
||||
const auto third_coordinate = m_node_info_list[(m_geometry_compressor.HasEntryForID(e2) ?
|
||||
m_geometry_compressor.GetFirstNodeIDOfBucket(e2) :
|
||||
w)];
|
||||
|
||||
const double turn_angle = ComputeAngle::OfThreeFixedPointCoordinates(
|
||||
first_coordinate, m_node_info_list[v], third_coordinate);
|
||||
|
||||
const int turn_penalty = GetTurnPenalty(turn_angle, lua_state);
|
||||
TurnInstruction turn_instruction = AnalyzeTurn(u, v, w, turn_angle);
|
||||
if (turn_instruction == TurnInstruction::UTurn)
|
||||
{
|
||||
distance += speed_profile.u_turn_penalty;
|
||||
}
|
||||
distance += turn_penalty;
|
||||
|
||||
const bool edge_is_compressed = m_geometry_compressor.HasEntryForID(e1);
|
||||
|
||||
if (edge_is_compressed)
|
||||
{
|
||||
++compressed;
|
||||
}
|
||||
|
||||
original_edge_data_vector.emplace_back(
|
||||
(edge_is_compressed ? m_geometry_compressor.GetPositionForID(e1) : v),
|
||||
edge_data1.nameID,
|
||||
turn_instruction,
|
||||
edge_is_compressed,
|
||||
edge_data2.travel_mode);
|
||||
|
||||
++original_edges_counter;
|
||||
|
||||
if (original_edge_data_vector.size() > 1024 * 1024 * 10)
|
||||
{
|
||||
FlushVectorToStream(edge_data_file, original_edge_data_vector);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data1.edgeBasedNodeID);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edgeBasedNodeID);
|
||||
|
||||
m_edge_based_edge_list.emplace_back(EdgeBasedEdge(edge_data1.edgeBasedNodeID,
|
||||
edge_data2.edgeBasedNodeID,
|
||||
m_edge_based_edge_list.size(),
|
||||
distance,
|
||||
true,
|
||||
false));
|
||||
}
|
||||
}
|
||||
}
|
||||
FlushVectorToStream(edge_data_file, original_edge_data_vector);
|
||||
|
||||
edge_data_file.seekp(std::ios::beg);
|
||||
edge_data_file.write((char *)&original_edges_counter, sizeof(unsigned));
|
||||
edge_data_file.close();
|
||||
|
||||
SimpleLogger().Write() << "Generated " << m_edge_based_node_list.size() << " edge based nodes";
|
||||
SimpleLogger().Write() << "Node-based graph contains " << node_based_edge_counter << " edges";
|
||||
SimpleLogger().Write() << "Edge-expanded graph ...";
|
||||
SimpleLogger().Write() << " contains " << m_edge_based_edge_list.size() << " edges";
|
||||
SimpleLogger().Write() << " skips " << restricted_turns_counter << " turns, "
|
||||
"defined by "
|
||||
<< m_restriction_map->size() << " restrictions";
|
||||
SimpleLogger().Write() << " skips " << skipped_uturns_counter << " U turns";
|
||||
SimpleLogger().Write() << " skips " << skipped_barrier_turns_counter << " turns over barriers";
|
||||
}
|
||||
|
||||
int EdgeBasedGraphFactory::GetTurnPenalty(double angle, lua_State *lua_state) const
|
||||
{
|
||||
|
||||
if (speed_profile.has_turn_penalty_function)
|
||||
{
|
||||
try
|
||||
{
|
||||
// call lua profile to compute turn penalty
|
||||
return luabind::call_function<int>(lua_state, "turn_function", 180. - angle);
|
||||
}
|
||||
catch (const luabind::error &er) { SimpleLogger().Write(logWARNING) << er.what(); }
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
TurnInstruction EdgeBasedGraphFactory::AnalyzeTurn(const NodeID node_u,
|
||||
const NodeID node_v,
|
||||
const NodeID node_w,
|
||||
const double angle)
|
||||
const
|
||||
{
|
||||
if (node_u == node_w)
|
||||
{
|
||||
return TurnInstruction::UTurn;
|
||||
}
|
||||
|
||||
const EdgeID edge1 = m_node_based_graph->FindEdge(node_u, node_v);
|
||||
const EdgeID edge2 = m_node_based_graph->FindEdge(node_v, node_w);
|
||||
|
||||
const EdgeData &data1 = m_node_based_graph->GetEdgeData(edge1);
|
||||
const EdgeData &data2 = m_node_based_graph->GetEdgeData(edge2);
|
||||
|
||||
// roundabouts need to be handled explicitely
|
||||
if (data1.roundabout && data2.roundabout)
|
||||
{
|
||||
// Is a turn possible? If yes, we stay on the roundabout!
|
||||
if (1 == m_node_based_graph->GetDirectedOutDegree(node_v))
|
||||
{
|
||||
// No turn possible.
|
||||
return TurnInstruction::NoTurn;
|
||||
}
|
||||
return TurnInstruction::StayOnRoundAbout;
|
||||
}
|
||||
// Does turn start or end on roundabout?
|
||||
if (data1.roundabout || data2.roundabout)
|
||||
{
|
||||
// We are entering the roundabout
|
||||
if ((!data1.roundabout) && data2.roundabout)
|
||||
{
|
||||
return TurnInstruction::EnterRoundAbout;
|
||||
}
|
||||
// We are leaving the roundabout
|
||||
if (data1.roundabout && (!data2.roundabout))
|
||||
{
|
||||
return TurnInstruction::LeaveRoundAbout;
|
||||
}
|
||||
}
|
||||
|
||||
// If street names stay the same and if we are certain that it is not a
|
||||
// a segment of a roundabout, we skip it.
|
||||
if (data1.nameID == data2.nameID)
|
||||
{
|
||||
// TODO: Here we should also do a small graph exploration to check for
|
||||
// more complex situations
|
||||
if (0 != data1.nameID || m_node_based_graph->GetOutDegree(node_v) <= 2)
|
||||
{
|
||||
return TurnInstruction::NoTurn;
|
||||
}
|
||||
}
|
||||
|
||||
return TurnInstructionsClass::GetTurnDirectionOfInstruction(angle);
|
||||
}
|
||||
|
||||
unsigned EdgeBasedGraphFactory::GetNumberOfEdgeBasedNodes() const
|
||||
{
|
||||
return m_number_of_edge_based_nodes;
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
// This class constructs the edge-expanded routing graph
|
||||
|
||||
#ifndef EDGE_BASED_GRAPH_FACTORY_HPP_
|
||||
#define EDGE_BASED_GRAPH_FACTORY_HPP_
|
||||
|
||||
#include "geometry_compressor.hpp"
|
||||
#include "../typedefs.h"
|
||||
#include "../DataStructures/DeallocatingVector.h"
|
||||
#include "../DataStructures/EdgeBasedNode.h"
|
||||
#include "../DataStructures/OriginalEdgeData.h"
|
||||
#include "../DataStructures/QueryNode.h"
|
||||
#include "../DataStructures/TurnInstructions.h"
|
||||
#include "../DataStructures/NodeBasedGraph.h"
|
||||
#include "../DataStructures/restriction_map.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iosfwd>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
struct lua_State;
|
||||
|
||||
class EdgeBasedGraphFactory
|
||||
{
|
||||
public:
|
||||
EdgeBasedGraphFactory() = delete;
|
||||
EdgeBasedGraphFactory(const EdgeBasedGraphFactory &) = delete;
|
||||
|
||||
struct SpeedProfileProperties;
|
||||
|
||||
explicit EdgeBasedGraphFactory(const std::shared_ptr<NodeBasedDynamicGraph> &node_based_graph,
|
||||
std::unique_ptr<RestrictionMap> restricion_map,
|
||||
std::vector<NodeID> &barrier_node_list,
|
||||
std::vector<NodeID> &traffic_light_node_list,
|
||||
std::vector<QueryNode> &node_info_list,
|
||||
SpeedProfileProperties &speed_profile);
|
||||
|
||||
void Run(const std::string &original_edge_data_filename,
|
||||
const std::string &geometry_filename,
|
||||
lua_State *lua_state);
|
||||
|
||||
void GetEdgeBasedEdges(DeallocatingVector<EdgeBasedEdge> &edges);
|
||||
|
||||
void GetEdgeBasedNodes(std::vector<EdgeBasedNode> &nodes);
|
||||
|
||||
TurnInstruction AnalyzeTurn(const NodeID u, const NodeID v, const NodeID w, const double angle) const;
|
||||
|
||||
int GetTurnPenalty(double angle, lua_State *lua_state) const;
|
||||
|
||||
unsigned GetNumberOfEdgeBasedNodes() const;
|
||||
|
||||
struct SpeedProfileProperties
|
||||
{
|
||||
SpeedProfileProperties()
|
||||
: traffic_signal_penalty(0), u_turn_penalty(0), has_turn_penalty_function(false)
|
||||
{
|
||||
}
|
||||
|
||||
int traffic_signal_penalty;
|
||||
int u_turn_penalty;
|
||||
bool has_turn_penalty_function;
|
||||
} speed_profile;
|
||||
|
||||
private:
|
||||
using EdgeData = NodeBasedDynamicGraph::EdgeData;
|
||||
|
||||
unsigned m_number_of_edge_based_nodes;
|
||||
|
||||
std::vector<QueryNode> m_node_info_list;
|
||||
std::vector<EdgeBasedNode> m_edge_based_node_list;
|
||||
DeallocatingVector<EdgeBasedEdge> m_edge_based_edge_list;
|
||||
|
||||
std::shared_ptr<NodeBasedDynamicGraph> m_node_based_graph;
|
||||
std::unordered_set<NodeID> m_barrier_nodes;
|
||||
std::unordered_set<NodeID> m_traffic_lights;
|
||||
|
||||
std::unique_ptr<RestrictionMap> m_restriction_map;
|
||||
|
||||
GeometryCompressor m_geometry_compressor;
|
||||
|
||||
void CompressGeometry();
|
||||
void RenumberEdges();
|
||||
void GenerateEdgeExpandedNodes();
|
||||
void GenerateEdgeExpandedEdges(const std::string &original_edge_data_filename,
|
||||
lua_State *lua_state);
|
||||
|
||||
void InsertEdgeBasedNode(const NodeID u, const NodeID v, const bool belongsToTinyComponent);
|
||||
|
||||
void FlushVectorToStream(std::ofstream &edge_data_file,
|
||||
std::vector<OriginalEdgeData> &original_edge_data_vector) const;
|
||||
|
||||
NodeID max_id;
|
||||
};
|
||||
|
||||
#endif /* EDGE_BASED_GRAPH_FACTORY_HPP_ */
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include "geometry_compressor.hpp"
|
||||
#include "../Util/simple_logger.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
int free_list_maximum = 0;
|
||||
int UniqueNumber() { return ++free_list_maximum; }
|
||||
|
||||
GeometryCompressor::GeometryCompressor()
|
||||
{
|
||||
m_free_list.reserve(100);
|
||||
IncreaseFreeList();
|
||||
}
|
||||
|
||||
void GeometryCompressor::IncreaseFreeList()
|
||||
{
|
||||
m_compressed_geometries.resize(m_compressed_geometries.size() + 100);
|
||||
for (unsigned i = 100; i > 0; --i)
|
||||
{
|
||||
m_free_list.emplace_back(free_list_maximum);
|
||||
++free_list_maximum;
|
||||
}
|
||||
}
|
||||
|
||||
bool GeometryCompressor::HasEntryForID(const EdgeID edge_id) const
|
||||
{
|
||||
auto iter = m_edge_id_to_list_index_map.find(edge_id);
|
||||
return iter != m_edge_id_to_list_index_map.end();
|
||||
}
|
||||
|
||||
unsigned GeometryCompressor::GetPositionForID(const EdgeID edge_id) const
|
||||
{
|
||||
auto map_iterator = m_edge_id_to_list_index_map.find(edge_id);
|
||||
BOOST_ASSERT(map_iterator != m_edge_id_to_list_index_map.end());
|
||||
BOOST_ASSERT(map_iterator->second < m_compressed_geometries.size());
|
||||
return map_iterator->second;
|
||||
}
|
||||
|
||||
void GeometryCompressor::SerializeInternalVector(const std::string &path) const
|
||||
{
|
||||
|
||||
boost::filesystem::fstream geometry_out_stream(path, std::ios::binary | std::ios::out);
|
||||
const unsigned compressed_geometries = m_compressed_geometries.size() + 1;
|
||||
BOOST_ASSERT(std::numeric_limits<unsigned>::max() != compressed_geometries);
|
||||
geometry_out_stream.write((char *)&compressed_geometries, sizeof(unsigned));
|
||||
|
||||
// write indices array
|
||||
unsigned prefix_sum_of_list_indices = 0;
|
||||
for (const auto &elem : m_compressed_geometries)
|
||||
{
|
||||
geometry_out_stream.write((char *)&prefix_sum_of_list_indices, sizeof(unsigned));
|
||||
|
||||
const std::vector<CompressedNode> ¤t_vector = elem;
|
||||
const unsigned unpacked_size = current_vector.size();
|
||||
BOOST_ASSERT(std::numeric_limits<unsigned>::max() != unpacked_size);
|
||||
prefix_sum_of_list_indices += unpacked_size;
|
||||
}
|
||||
// sentinel element
|
||||
geometry_out_stream.write((char *)&prefix_sum_of_list_indices, sizeof(unsigned));
|
||||
|
||||
// number of geometry entries to follow, it is the (inclusive) prefix sum
|
||||
geometry_out_stream.write((char *)&prefix_sum_of_list_indices, sizeof(unsigned));
|
||||
|
||||
unsigned control_sum = 0;
|
||||
// write compressed geometries
|
||||
for (auto &elem : m_compressed_geometries)
|
||||
{
|
||||
const std::vector<CompressedNode> ¤t_vector = elem;
|
||||
const unsigned unpacked_size = current_vector.size();
|
||||
control_sum += unpacked_size;
|
||||
BOOST_ASSERT(std::numeric_limits<unsigned>::max() != unpacked_size);
|
||||
for (const CompressedNode current_node : current_vector)
|
||||
{
|
||||
geometry_out_stream.write((char *)&(current_node.first), sizeof(NodeID));
|
||||
}
|
||||
}
|
||||
BOOST_ASSERT(control_sum == prefix_sum_of_list_indices);
|
||||
// all done, let's close the resource
|
||||
geometry_out_stream.close();
|
||||
}
|
||||
|
||||
void GeometryCompressor::CompressEdge(const EdgeID edge_id_1,
|
||||
const EdgeID edge_id_2,
|
||||
const NodeID via_node_id,
|
||||
const NodeID target_node_id,
|
||||
const EdgeWeight weight1,
|
||||
const EdgeWeight weight2)
|
||||
{
|
||||
// remove super-trivial geometries
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != edge_id_1);
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != edge_id_2);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != via_node_id);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != target_node_id);
|
||||
BOOST_ASSERT(INVALID_EDGE_WEIGHT != weight1);
|
||||
BOOST_ASSERT(INVALID_EDGE_WEIGHT != weight2);
|
||||
|
||||
// append list of removed edge_id plus via node to surviving edge id:
|
||||
// <surv_1, .. , surv_n, via_node_id, rem_1, .. rem_n
|
||||
//
|
||||
// General scheme:
|
||||
// 1. append via node id to list of edge_id_1
|
||||
// 2. find list for edge_id_2, if yes add all elements and delete it
|
||||
|
||||
// Add via node id. List is created if it does not exist
|
||||
if (!HasEntryForID(edge_id_1))
|
||||
{
|
||||
// create a new entry in the map
|
||||
if (0 == m_free_list.size())
|
||||
{
|
||||
// make sure there is a place to put the entries
|
||||
IncreaseFreeList();
|
||||
}
|
||||
BOOST_ASSERT(!m_free_list.empty());
|
||||
m_edge_id_to_list_index_map[edge_id_1] = m_free_list.back();
|
||||
m_free_list.pop_back();
|
||||
}
|
||||
|
||||
// find bucket index
|
||||
const auto iter = m_edge_id_to_list_index_map.find(edge_id_1);
|
||||
BOOST_ASSERT(iter != m_edge_id_to_list_index_map.end());
|
||||
const unsigned edge_bucket_id1 = iter->second;
|
||||
BOOST_ASSERT(edge_bucket_id1 == GetPositionForID(edge_id_1));
|
||||
BOOST_ASSERT(edge_bucket_id1 < m_compressed_geometries.size());
|
||||
|
||||
std::vector<CompressedNode> &edge_bucket_list1 = m_compressed_geometries[edge_bucket_id1];
|
||||
|
||||
if (edge_bucket_list1.empty())
|
||||
{
|
||||
edge_bucket_list1.emplace_back(via_node_id, weight1);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(0 < edge_bucket_list1.size());
|
||||
BOOST_ASSERT(!edge_bucket_list1.empty());
|
||||
|
||||
if (HasEntryForID(edge_id_2))
|
||||
{
|
||||
// second edge is not atomic anymore
|
||||
const unsigned list_to_remove_index = GetPositionForID(edge_id_2);
|
||||
BOOST_ASSERT(list_to_remove_index < m_compressed_geometries.size());
|
||||
|
||||
std::vector<CompressedNode> &edge_bucket_list2 =
|
||||
m_compressed_geometries[list_to_remove_index];
|
||||
|
||||
// found an existing list, append it to the list of edge_id_1
|
||||
edge_bucket_list1.insert(
|
||||
edge_bucket_list1.end(), edge_bucket_list2.begin(), edge_bucket_list2.end());
|
||||
|
||||
// remove the list of edge_id_2
|
||||
m_edge_id_to_list_index_map.erase(edge_id_2);
|
||||
BOOST_ASSERT(m_edge_id_to_list_index_map.end() ==
|
||||
m_edge_id_to_list_index_map.find(edge_id_2));
|
||||
edge_bucket_list2.clear();
|
||||
BOOST_ASSERT(0 == edge_bucket_list2.size());
|
||||
m_free_list.emplace_back(list_to_remove_index);
|
||||
BOOST_ASSERT(list_to_remove_index == m_free_list.back());
|
||||
}
|
||||
else
|
||||
{
|
||||
// we are certain that the second edge is atomic.
|
||||
edge_bucket_list1.emplace_back(target_node_id, weight2);
|
||||
}
|
||||
}
|
||||
|
||||
void GeometryCompressor::PrintStatistics() const
|
||||
{
|
||||
const uint64_t compressed_edges = m_compressed_geometries.size();
|
||||
BOOST_ASSERT(0 == compressed_edges % 2);
|
||||
BOOST_ASSERT(m_compressed_geometries.size() + m_free_list.size() > 0);
|
||||
|
||||
uint64_t compressed_geometries = 0;
|
||||
uint64_t longest_chain_length = 0;
|
||||
for (const std::vector<CompressedNode> ¤t_vector : m_compressed_geometries)
|
||||
{
|
||||
compressed_geometries += current_vector.size();
|
||||
longest_chain_length = std::max(longest_chain_length, (uint64_t)current_vector.size());
|
||||
}
|
||||
|
||||
SimpleLogger().Write() << "Geometry successfully removed:"
|
||||
"\n compressed edges: " << compressed_edges
|
||||
<< "\n compressed geometries: " << compressed_geometries
|
||||
<< "\n longest chain length: " << longest_chain_length
|
||||
<< "\n cmpr ratio: "
|
||||
<< ((float)compressed_edges /
|
||||
std::max(compressed_geometries, (uint64_t)1))
|
||||
<< "\n avg chain length: "
|
||||
<< (float)compressed_geometries /
|
||||
std::max((uint64_t)1, compressed_edges);
|
||||
}
|
||||
|
||||
const std::vector<GeometryCompressor::CompressedNode> &
|
||||
GeometryCompressor::GetBucketReference(const EdgeID edge_id) const
|
||||
{
|
||||
const unsigned index = m_edge_id_to_list_index_map.at(edge_id);
|
||||
return m_compressed_geometries.at(index);
|
||||
}
|
||||
|
||||
NodeID GeometryCompressor::GetFirstNodeIDOfBucket(const EdgeID edge_id) const
|
||||
{
|
||||
const auto &bucket = GetBucketReference(edge_id);
|
||||
BOOST_ASSERT(bucket.size() >= 2);
|
||||
return bucket[1].first;
|
||||
}
|
||||
NodeID GeometryCompressor::GetLastNodeIDOfBucket(const EdgeID edge_id) const
|
||||
{
|
||||
const auto &bucket = GetBucketReference(edge_id);
|
||||
BOOST_ASSERT(bucket.size() >= 2);
|
||||
return bucket[bucket.size()-2].first;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
|
||||
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 GEOMETRY_COMPRESSOR_HPP_
|
||||
#define GEOMETRY_COMPRESSOR_HPP_
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class GeometryCompressor
|
||||
{
|
||||
public:
|
||||
using CompressedNode = std::pair<NodeID, EdgeWeight>;
|
||||
|
||||
GeometryCompressor();
|
||||
void CompressEdge(const EdgeID surviving_edge_id,
|
||||
const EdgeID removed_edge_id,
|
||||
const NodeID via_node_id,
|
||||
const NodeID target_node,
|
||||
const EdgeWeight weight1,
|
||||
const EdgeWeight weight2);
|
||||
|
||||
bool HasEntryForID(const EdgeID edge_id) const;
|
||||
void PrintStatistics() const;
|
||||
void SerializeInternalVector(const std::string &path) const;
|
||||
unsigned GetPositionForID(const EdgeID edge_id) const;
|
||||
const std::vector<GeometryCompressor::CompressedNode> &
|
||||
GetBucketReference(const EdgeID edge_id) const;
|
||||
NodeID GetFirstNodeIDOfBucket(const EdgeID edge_id) const;
|
||||
NodeID GetLastNodeIDOfBucket(const EdgeID edge_id) const;
|
||||
|
||||
private:
|
||||
void IncreaseFreeList();
|
||||
std::vector<std::vector<CompressedNode>> m_compressed_geometries;
|
||||
std::vector<unsigned> m_free_list;
|
||||
std::unordered_map<EdgeID, unsigned> m_edge_id_to_list_index_map;
|
||||
};
|
||||
|
||||
#endif // GEOMETRY_COMPRESSOR_HPP_
|
||||
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
#include "processing_chain.hpp"
|
||||
|
||||
#include "contractor.hpp"
|
||||
|
||||
#include "../algorithms/crc32_processor.hpp"
|
||||
#include "../DataStructures/DeallocatingVector.h"
|
||||
#include "../DataStructures/StaticRTree.h"
|
||||
#include "../DataStructures/restriction_map.hpp"
|
||||
|
||||
#include "../Util/GitDescription.h"
|
||||
#include "../Util/integer_range.hpp"
|
||||
#include "../Util/lua_util.hpp"
|
||||
#include "../Util/make_unique.hpp"
|
||||
#include "../Util/OSRMException.h"
|
||||
#include "../Util/simple_logger.hpp"
|
||||
#include "../Util/StringUtil.h"
|
||||
#include "../Util/TimingUtil.h"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include "../Util/GraphLoader.h"
|
||||
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
Prepare::Prepare() : requested_num_threads(1) {}
|
||||
|
||||
Prepare::~Prepare() {}
|
||||
|
||||
int Prepare::Process(int argc, char *argv[])
|
||||
{
|
||||
LogPolicy::GetInstance().Unmute();
|
||||
TIMER_START(preparing);
|
||||
TIMER_START(expansion);
|
||||
|
||||
if (!ParseArguments(argc, argv))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (!boost::filesystem::is_regular_file(input_path))
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "Input file " << input_path.string() << " not found!";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!boost::filesystem::is_regular_file(profile_path))
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "Profile " << profile_path.string() << " not found!";
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (1 > requested_num_threads)
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "Number of threads must be 1 or larger";
|
||||
return 1;
|
||||
}
|
||||
|
||||
const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads();
|
||||
|
||||
SimpleLogger().Write() << "Input file: " << input_path.filename().string();
|
||||
SimpleLogger().Write() << "Restrictions file: " << restrictions_path.filename().string();
|
||||
SimpleLogger().Write() << "Profile: " << profile_path.filename().string();
|
||||
SimpleLogger().Write() << "Threads: " << requested_num_threads;
|
||||
if (recommended_num_threads != requested_num_threads)
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "The recommended number of threads is "
|
||||
<< recommended_num_threads
|
||||
<< "! This setting may have performance side-effects.";
|
||||
}
|
||||
|
||||
tbb::task_scheduler_init init(requested_num_threads);
|
||||
|
||||
LogPolicy::GetInstance().Unmute();
|
||||
|
||||
FingerPrint fingerprint_orig;
|
||||
CheckRestrictionsFile(fingerprint_orig);
|
||||
|
||||
boost::filesystem::ifstream input_stream(input_path, std::ios::in | std::ios::binary);
|
||||
|
||||
node_filename = input_path.string() + ".nodes";
|
||||
edge_out = input_path.string() + ".edges";
|
||||
geometry_filename = input_path.string() + ".geometry";
|
||||
graph_out = input_path.string() + ".hsgr";
|
||||
rtree_nodes_path = input_path.string() + ".ramIndex";
|
||||
rtree_leafs_path = input_path.string() + ".fileIndex";
|
||||
|
||||
/*** Setup Scripting Environment ***/
|
||||
// Create a new lua state
|
||||
lua_State *lua_state = luaL_newstate();
|
||||
|
||||
// Connect LuaBind to this lua state
|
||||
luabind::open(lua_state);
|
||||
|
||||
EdgeBasedGraphFactory::SpeedProfileProperties speed_profile;
|
||||
|
||||
if (!SetupScriptingEnvironment(lua_state, speed_profile))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma message("Memory consumption on Windows can be higher due to different bit packing")
|
||||
#else
|
||||
static_assert(sizeof(ImportEdge) == 20,
|
||||
"changing ImportEdge type has influence on memory consumption!");
|
||||
#endif
|
||||
NodeID number_of_node_based_nodes =
|
||||
readBinaryOSRMGraphFromStream(input_stream,
|
||||
edge_list,
|
||||
barrier_node_list,
|
||||
traffic_light_list,
|
||||
&internal_to_external_node_map,
|
||||
restriction_list);
|
||||
input_stream.close();
|
||||
|
||||
if (edge_list.empty())
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "The input data is empty, exiting.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
SimpleLogger().Write() << restriction_list.size() << " restrictions, "
|
||||
<< barrier_node_list.size() << " bollard nodes, "
|
||||
<< traffic_light_list.size() << " traffic lights";
|
||||
|
||||
std::vector<EdgeBasedNode> node_based_edge_list;
|
||||
unsigned number_of_edge_based_nodes = 0;
|
||||
DeallocatingVector<EdgeBasedEdge> edge_based_edge_list;
|
||||
|
||||
// init node_based_edge_list, edge_based_edge_list by edgeList
|
||||
number_of_edge_based_nodes = BuildEdgeExpandedGraph(lua_state,
|
||||
number_of_node_based_nodes,
|
||||
node_based_edge_list,
|
||||
edge_based_edge_list,
|
||||
speed_profile);
|
||||
lua_close(lua_state);
|
||||
|
||||
TIMER_STOP(expansion);
|
||||
|
||||
BuildRTree(node_based_edge_list);
|
||||
|
||||
RangebasedCRC32 crc32;
|
||||
if (crc32.using_hardware())
|
||||
{
|
||||
SimpleLogger().Write() << "using hardware based CRC32 computation";
|
||||
}
|
||||
else
|
||||
{
|
||||
SimpleLogger().Write() << "using software based CRC32 computation";
|
||||
}
|
||||
|
||||
const unsigned crc32_value = crc32(node_based_edge_list);
|
||||
node_based_edge_list.clear();
|
||||
node_based_edge_list.shrink_to_fit();
|
||||
SimpleLogger().Write() << "CRC32: " << crc32_value;
|
||||
|
||||
WriteNodeMapping();
|
||||
|
||||
/***
|
||||
* Contracting the edge-expanded graph
|
||||
*/
|
||||
|
||||
SimpleLogger().Write() << "initializing contractor";
|
||||
auto contractor =
|
||||
osrm::make_unique<Contractor>(number_of_edge_based_nodes, edge_based_edge_list);
|
||||
|
||||
TIMER_START(contraction);
|
||||
contractor->Run();
|
||||
TIMER_STOP(contraction);
|
||||
|
||||
SimpleLogger().Write() << "Contraction took " << TIMER_SEC(contraction) << " sec";
|
||||
|
||||
DeallocatingVector<QueryEdge> contracted_edge_list;
|
||||
contractor->GetEdges(contracted_edge_list);
|
||||
contractor.reset();
|
||||
|
||||
/***
|
||||
* Sorting contracted edges in a way that the static query graph can read some in in-place.
|
||||
*/
|
||||
|
||||
tbb::parallel_sort(contracted_edge_list.begin(), contracted_edge_list.end());
|
||||
const unsigned contracted_edge_count = contracted_edge_list.size();
|
||||
SimpleLogger().Write() << "Serializing compacted graph of " << contracted_edge_count
|
||||
<< " edges";
|
||||
|
||||
boost::filesystem::ofstream hsgr_output_stream(graph_out, std::ios::binary);
|
||||
hsgr_output_stream.write((char *)&fingerprint_orig, sizeof(FingerPrint));
|
||||
const unsigned max_used_node_id = 1 + [&contracted_edge_list]
|
||||
{
|
||||
unsigned tmp_max = 0;
|
||||
for (const QueryEdge &edge : contracted_edge_list)
|
||||
{
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge.source);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge.target);
|
||||
tmp_max = std::max(tmp_max, edge.source);
|
||||
tmp_max = std::max(tmp_max, edge.target);
|
||||
}
|
||||
return tmp_max;
|
||||
}();
|
||||
|
||||
SimpleLogger().Write(logDEBUG) << "input graph has " << number_of_edge_based_nodes << " nodes";
|
||||
SimpleLogger().Write(logDEBUG) << "contracted graph has " << max_used_node_id << " nodes";
|
||||
|
||||
std::vector<StaticGraph<EdgeData>::NodeArrayEntry> node_array;
|
||||
node_array.resize(number_of_edge_based_nodes + 1);
|
||||
|
||||
SimpleLogger().Write() << "Building node array";
|
||||
StaticGraph<EdgeData>::EdgeIterator edge = 0;
|
||||
StaticGraph<EdgeData>::EdgeIterator position = 0;
|
||||
StaticGraph<EdgeData>::EdgeIterator last_edge = edge;
|
||||
|
||||
// initializing 'first_edge'-field of nodes:
|
||||
for (const auto node : osrm::irange(0u, max_used_node_id))
|
||||
{
|
||||
last_edge = edge;
|
||||
while ((edge < contracted_edge_count) && (contracted_edge_list[edge].source == node))
|
||||
{
|
||||
++edge;
|
||||
}
|
||||
node_array[node].first_edge = position; //=edge
|
||||
position += edge - last_edge; // remove
|
||||
}
|
||||
|
||||
for (const auto sentinel_counter : osrm::irange<unsigned>(max_used_node_id, node_array.size()))
|
||||
{
|
||||
// sentinel element, guarded against underflow
|
||||
node_array[sentinel_counter].first_edge = contracted_edge_count;
|
||||
}
|
||||
|
||||
SimpleLogger().Write() << "Serializing node array";
|
||||
|
||||
const unsigned node_array_size = node_array.size();
|
||||
// serialize crc32, aka checksum
|
||||
hsgr_output_stream.write((char *)&crc32_value, sizeof(unsigned));
|
||||
// serialize number of nodes
|
||||
hsgr_output_stream.write((char *)&node_array_size, sizeof(unsigned));
|
||||
// serialize number of edges
|
||||
hsgr_output_stream.write((char *)&contracted_edge_count, sizeof(unsigned));
|
||||
// serialize all nodes
|
||||
if (node_array_size > 0)
|
||||
{
|
||||
hsgr_output_stream.write((char *)&node_array[0],
|
||||
sizeof(StaticGraph<EdgeData>::NodeArrayEntry) * node_array_size);
|
||||
}
|
||||
// serialize all edges
|
||||
|
||||
SimpleLogger().Write() << "Building edge array";
|
||||
edge = 0;
|
||||
int number_of_used_edges = 0;
|
||||
|
||||
StaticGraph<EdgeData>::EdgeArrayEntry current_edge;
|
||||
for (const auto edge : osrm::irange<std::size_t>(0, contracted_edge_list.size()))
|
||||
{
|
||||
// no eigen loops
|
||||
BOOST_ASSERT(contracted_edge_list[edge].source != contracted_edge_list[edge].target);
|
||||
current_edge.target = contracted_edge_list[edge].target;
|
||||
current_edge.data = contracted_edge_list[edge].data;
|
||||
|
||||
// every target needs to be valid
|
||||
BOOST_ASSERT(current_edge.target < max_used_node_id);
|
||||
#ifndef NDEBUG
|
||||
if (current_edge.data.distance <= 0)
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "Edge: " << edge
|
||||
<< ",source: " << contracted_edge_list[edge].source
|
||||
<< ", target: " << contracted_edge_list[edge].target
|
||||
<< ", dist: " << current_edge.data.distance;
|
||||
|
||||
SimpleLogger().Write(logWARNING) << "Failed at adjacency list of node "
|
||||
<< contracted_edge_list[edge].source << "/"
|
||||
<< node_array.size() - 1;
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
hsgr_output_stream.write((char *)¤t_edge,
|
||||
sizeof(StaticGraph<EdgeData>::EdgeArrayEntry));
|
||||
|
||||
++number_of_used_edges;
|
||||
}
|
||||
hsgr_output_stream.close();
|
||||
|
||||
TIMER_STOP(preparing);
|
||||
|
||||
SimpleLogger().Write() << "Preprocessing : " << TIMER_SEC(preparing) << " seconds";
|
||||
SimpleLogger().Write() << "Expansion : " << (number_of_node_based_nodes / TIMER_SEC(expansion))
|
||||
<< " nodes/sec and "
|
||||
<< (number_of_edge_based_nodes / TIMER_SEC(expansion)) << " edges/sec";
|
||||
|
||||
SimpleLogger().Write() << "Contraction: "
|
||||
<< (number_of_edge_based_nodes / TIMER_SEC(contraction))
|
||||
<< " nodes/sec and " << number_of_used_edges / TIMER_SEC(contraction)
|
||||
<< " edges/sec";
|
||||
|
||||
node_array.clear();
|
||||
SimpleLogger().Write() << "finished preprocessing";
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Parses command line arguments
|
||||
\param argc count of arguments
|
||||
\param argv array of arguments
|
||||
\param result [out] value for exit return value
|
||||
\return true if everything is ok, false if need to terminate execution
|
||||
*/
|
||||
bool Prepare::ParseArguments(int argc, char *argv[])
|
||||
{
|
||||
// declare a group of options that will be allowed only on command line
|
||||
boost::program_options::options_description generic_options("Options");
|
||||
generic_options.add_options()("version,v", "Show version")("help,h", "Show this help message")(
|
||||
"config,c",
|
||||
boost::program_options::value<boost::filesystem::path>(&config_file_path)
|
||||
->default_value("contractor.ini"),
|
||||
"Path to a configuration file.");
|
||||
|
||||
// declare a group of options that will be allowed both on command line and in config file
|
||||
boost::program_options::options_description config_options("Configuration");
|
||||
config_options.add_options()(
|
||||
"restrictions,r",
|
||||
boost::program_options::value<boost::filesystem::path>(&restrictions_path),
|
||||
"Restrictions file in .osrm.restrictions format")(
|
||||
"profile,p",
|
||||
boost::program_options::value<boost::filesystem::path>(&profile_path)
|
||||
->default_value("profile.lua"),
|
||||
"Path to LUA routing profile")(
|
||||
"threads,t",
|
||||
boost::program_options::value<unsigned int>(&requested_num_threads)
|
||||
->default_value(tbb::task_scheduler_init::default_num_threads()),
|
||||
"Number of threads to use");
|
||||
|
||||
// hidden options, will be allowed both on command line and in config file, but will not be
|
||||
// shown to the user
|
||||
boost::program_options::options_description hidden_options("Hidden options");
|
||||
hidden_options.add_options()(
|
||||
"input,i",
|
||||
boost::program_options::value<boost::filesystem::path>(&input_path),
|
||||
"Input file in .osm, .osm.bz2 or .osm.pbf format");
|
||||
|
||||
// positional option
|
||||
boost::program_options::positional_options_description positional_options;
|
||||
positional_options.add("input", 1);
|
||||
|
||||
// combine above options for parsing
|
||||
boost::program_options::options_description cmdline_options;
|
||||
cmdline_options.add(generic_options).add(config_options).add(hidden_options);
|
||||
|
||||
boost::program_options::options_description config_file_options;
|
||||
config_file_options.add(config_options).add(hidden_options);
|
||||
|
||||
boost::program_options::options_description visible_options(
|
||||
"Usage: " + boost::filesystem::basename(argv[0]) + " <input.osrm> [options]");
|
||||
visible_options.add(generic_options).add(config_options);
|
||||
|
||||
// parse command line options
|
||||
boost::program_options::variables_map option_variables;
|
||||
boost::program_options::store(boost::program_options::command_line_parser(argc, argv)
|
||||
.options(cmdline_options)
|
||||
.positional(positional_options)
|
||||
.run(),
|
||||
option_variables);
|
||||
|
||||
const auto& temp_config_path = option_variables["config"].as<boost::filesystem::path>();
|
||||
if (boost::filesystem::is_regular_file(temp_config_path))
|
||||
{
|
||||
boost::program_options::store(boost::program_options::parse_config_file<char>(temp_config_path.string().c_str(), cmdline_options, true),
|
||||
option_variables);
|
||||
}
|
||||
|
||||
if (option_variables.count("version"))
|
||||
{
|
||||
SimpleLogger().Write() << g_GIT_DESCRIPTION;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (option_variables.count("help"))
|
||||
{
|
||||
SimpleLogger().Write() << "\n" << visible_options;
|
||||
return false;
|
||||
}
|
||||
|
||||
boost::program_options::notify(option_variables);
|
||||
|
||||
if (!option_variables.count("restrictions"))
|
||||
{
|
||||
restrictions_path = std::string(input_path.string() + ".restrictions");
|
||||
}
|
||||
|
||||
if (!option_variables.count("input"))
|
||||
{
|
||||
SimpleLogger().Write() << "\n" << visible_options;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Loads and checks file UUIDs
|
||||
*/
|
||||
void Prepare::CheckRestrictionsFile(FingerPrint &fingerprint_orig)
|
||||
{
|
||||
boost::filesystem::ifstream restriction_stream(restrictions_path, std::ios::binary);
|
||||
FingerPrint fingerprint_loaded;
|
||||
unsigned number_of_usable_restrictions = 0;
|
||||
restriction_stream.read((char *)&fingerprint_loaded, sizeof(FingerPrint));
|
||||
if (!fingerprint_loaded.TestPrepare(fingerprint_orig))
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << ".restrictions was prepared with different build.\n"
|
||||
"Reprocess to get rid of this warning.";
|
||||
}
|
||||
|
||||
restriction_stream.read((char *)&number_of_usable_restrictions, sizeof(unsigned));
|
||||
restriction_list.resize(number_of_usable_restrictions);
|
||||
if (number_of_usable_restrictions > 0)
|
||||
{
|
||||
restriction_stream.read((char *)&(restriction_list[0]),
|
||||
number_of_usable_restrictions * sizeof(TurnRestriction));
|
||||
}
|
||||
restriction_stream.close();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Setups scripting environment (lua-scripting)
|
||||
Also initializes speed profile.
|
||||
*/
|
||||
bool
|
||||
Prepare::SetupScriptingEnvironment(lua_State *lua_state,
|
||||
EdgeBasedGraphFactory::SpeedProfileProperties &speed_profile)
|
||||
{
|
||||
// open utility libraries string library;
|
||||
luaL_openlibs(lua_state);
|
||||
|
||||
// adjust lua load path
|
||||
luaAddScriptFolderToLoadPath(lua_state, profile_path.string().c_str());
|
||||
|
||||
// Now call our function in a lua script
|
||||
if (0 != luaL_dofile(lua_state, profile_path.string().c_str()))
|
||||
{
|
||||
std::cerr << lua_tostring(lua_state, -1) << " occured in scripting block" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (0 != luaL_dostring(lua_state, "return traffic_signal_penalty\n"))
|
||||
{
|
||||
std::cerr << lua_tostring(lua_state, -1) << " occured in scripting block" << std::endl;
|
||||
return false;
|
||||
}
|
||||
speed_profile.traffic_signal_penalty = 10 * lua_tointeger(lua_state, -1);
|
||||
SimpleLogger().Write(logDEBUG)
|
||||
<< "traffic_signal_penalty: " << speed_profile.traffic_signal_penalty;
|
||||
|
||||
if (0 != luaL_dostring(lua_state, "return u_turn_penalty\n"))
|
||||
{
|
||||
std::cerr << lua_tostring(lua_state, -1) << " occured in scripting block" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
speed_profile.u_turn_penalty = 10 * lua_tointeger(lua_state, -1);
|
||||
speed_profile.has_turn_penalty_function = lua_function_exists(lua_state, "turn_function");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Building an edge-expanded graph from node-based input and turn restrictions
|
||||
*/
|
||||
std::size_t
|
||||
Prepare::BuildEdgeExpandedGraph(lua_State *lua_state,
|
||||
NodeID number_of_node_based_nodes,
|
||||
std::vector<EdgeBasedNode> &node_based_edge_list,
|
||||
DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list,
|
||||
EdgeBasedGraphFactory::SpeedProfileProperties &speed_profile)
|
||||
{
|
||||
SimpleLogger().Write() << "Generating edge-expanded graph representation";
|
||||
std::shared_ptr<NodeBasedDynamicGraph> node_based_graph =
|
||||
NodeBasedDynamicGraphFromImportEdges(number_of_node_based_nodes, edge_list);
|
||||
std::unique_ptr<RestrictionMap> restriction_map =
|
||||
std::unique_ptr<RestrictionMap>(new RestrictionMap(node_based_graph, restriction_list));
|
||||
std::shared_ptr<EdgeBasedGraphFactory> edge_based_graph_factory =
|
||||
std::make_shared<EdgeBasedGraphFactory>(node_based_graph,
|
||||
std::move(restriction_map),
|
||||
barrier_node_list,
|
||||
traffic_light_list,
|
||||
internal_to_external_node_map,
|
||||
speed_profile);
|
||||
edge_list.clear();
|
||||
edge_list.shrink_to_fit();
|
||||
|
||||
edge_based_graph_factory->Run(edge_out, geometry_filename, lua_state);
|
||||
|
||||
restriction_list.clear();
|
||||
restriction_list.shrink_to_fit();
|
||||
barrier_node_list.clear();
|
||||
barrier_node_list.shrink_to_fit();
|
||||
traffic_light_list.clear();
|
||||
traffic_light_list.shrink_to_fit();
|
||||
|
||||
const std::size_t number_of_edge_based_nodes =
|
||||
edge_based_graph_factory->GetNumberOfEdgeBasedNodes();
|
||||
|
||||
BOOST_ASSERT(number_of_edge_based_nodes != std::numeric_limits<unsigned>::max());
|
||||
#ifndef WIN32
|
||||
static_assert(sizeof(EdgeBasedEdge) == 16,
|
||||
"changing ImportEdge type has influence on memory consumption!");
|
||||
#endif
|
||||
|
||||
edge_based_graph_factory->GetEdgeBasedEdges(edge_based_edge_list);
|
||||
edge_based_graph_factory->GetEdgeBasedNodes(node_based_edge_list);
|
||||
|
||||
edge_based_graph_factory.reset();
|
||||
node_based_graph.reset();
|
||||
|
||||
return number_of_edge_based_nodes;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Writing info on original (node-based) nodes
|
||||
*/
|
||||
void Prepare::WriteNodeMapping()
|
||||
{
|
||||
SimpleLogger().Write() << "writing node map ...";
|
||||
boost::filesystem::ofstream node_stream(node_filename, std::ios::binary);
|
||||
const unsigned size_of_mapping = internal_to_external_node_map.size();
|
||||
node_stream.write((char *)&size_of_mapping, sizeof(unsigned));
|
||||
if (size_of_mapping > 0)
|
||||
{
|
||||
node_stream.write((char *)&(internal_to_external_node_map[0]),
|
||||
size_of_mapping * sizeof(QueryNode));
|
||||
}
|
||||
node_stream.close();
|
||||
internal_to_external_node_map.clear();
|
||||
internal_to_external_node_map.shrink_to_fit();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Building rtree-based nearest-neighbor data structure
|
||||
|
||||
Saves info to files: '.ramIndex' and '.fileIndex'.
|
||||
*/
|
||||
void Prepare::BuildRTree(std::vector<EdgeBasedNode> &node_based_edge_list)
|
||||
{
|
||||
SimpleLogger().Write() << "building r-tree ...";
|
||||
StaticRTree<EdgeBasedNode>(node_based_edge_list,
|
||||
rtree_nodes_path.c_str(),
|
||||
rtree_leafs_path.c_str(),
|
||||
internal_to_external_node_map);
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef PREPARE_H
|
||||
#define PREPARE_H
|
||||
|
||||
#include "edge_based_graph_factory.hpp"
|
||||
#include "../DataStructures/QueryEdge.h"
|
||||
#include "../DataStructures/StaticGraph.h"
|
||||
class FingerPrint;
|
||||
struct EdgeBasedNode;
|
||||
struct lua_State;
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
\brief class of 'prepare' utility.
|
||||
*/
|
||||
class Prepare
|
||||
{
|
||||
public:
|
||||
using EdgeData = QueryEdge::EdgeData;
|
||||
using InputEdge = DynamicGraph<EdgeData>::InputEdge;
|
||||
using StaticEdge = StaticGraph<EdgeData>::InputEdge;
|
||||
|
||||
explicit Prepare();
|
||||
Prepare(const Prepare &) = delete;
|
||||
~Prepare();
|
||||
|
||||
int Process(int argc, char *argv[]);
|
||||
|
||||
protected:
|
||||
bool ParseArguments(int argc, char *argv[]);
|
||||
void CheckRestrictionsFile(FingerPrint &fingerprint_orig);
|
||||
bool SetupScriptingEnvironment(lua_State *myLuaState,
|
||||
EdgeBasedGraphFactory::SpeedProfileProperties &speed_profile);
|
||||
std::size_t BuildEdgeExpandedGraph(lua_State *myLuaState,
|
||||
NodeID nodeBasedNodeNumber,
|
||||
std::vector<EdgeBasedNode> &nodeBasedEdgeList,
|
||||
DeallocatingVector<EdgeBasedEdge> &edgeBasedEdgeList,
|
||||
EdgeBasedGraphFactory::SpeedProfileProperties &speed_profile);
|
||||
void WriteNodeMapping();
|
||||
void BuildRTree(std::vector<EdgeBasedNode> &node_based_edge_list);
|
||||
|
||||
private:
|
||||
std::vector<QueryNode> internal_to_external_node_map;
|
||||
std::vector<TurnRestriction> restriction_list;
|
||||
std::vector<NodeID> barrier_node_list;
|
||||
std::vector<NodeID> traffic_light_list;
|
||||
std::vector<ImportEdge> edge_list;
|
||||
|
||||
unsigned requested_num_threads;
|
||||
boost::filesystem::path config_file_path;
|
||||
boost::filesystem::path input_path;
|
||||
boost::filesystem::path restrictions_path;
|
||||
boost::filesystem::path preinfo_path;
|
||||
boost::filesystem::path profile_path;
|
||||
|
||||
std::string node_filename;
|
||||
std::string edge_out;
|
||||
std::string info_out;
|
||||
std::string geometry_filename;
|
||||
std::string graph_out;
|
||||
std::string rtree_nodes_path;
|
||||
std::string rtree_leafs_path;
|
||||
};
|
||||
|
||||
#endif // PREPARE_H
|
||||
Reference in New Issue
Block a user