Move files in src/ include/
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
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 "compressed_edge_container.hpp"
|
||||
#include "../util/simple_logger.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
CompressedEdgeContainer::CompressedEdgeContainer()
|
||||
{
|
||||
m_free_list.reserve(100);
|
||||
IncreaseFreeList();
|
||||
}
|
||||
|
||||
void CompressedEdgeContainer::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 CompressedEdgeContainer::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 CompressedEdgeContainer::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 CompressedEdgeContainer::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 CompressedEdgeContainer::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];
|
||||
|
||||
// note we don't save the start coordinate: it is implicitly given by edge 1
|
||||
// weight1 is the distance to the (currently) last coordinate in the bucket
|
||||
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 CompressedEdgeContainer::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 CompressedEdgeContainer::EdgeBucket&
|
||||
CompressedEdgeContainer::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 CompressedEdgeContainer::GetFirstEdgeTargetID(const EdgeID edge_id) const
|
||||
{
|
||||
const auto &bucket = GetBucketReference(edge_id);
|
||||
BOOST_ASSERT(bucket.size() >= 2);
|
||||
return bucket.front().first;
|
||||
}
|
||||
NodeID CompressedEdgeContainer::GetLastEdgeSourceID(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,689 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, 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/coordinate_calculation.hpp"
|
||||
#include "../data_structures/percent.hpp"
|
||||
#include "../util/compute_angle.hpp"
|
||||
#include "../util/integer_range.hpp"
|
||||
#include "../util/lua_util.hpp"
|
||||
#include "../util/simple_logger.hpp"
|
||||
#include "../util/timing_util.hpp"
|
||||
#include "../util/osrm_exception.hpp"
|
||||
|
||||
#include "../util/debug_geometry.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <limits>
|
||||
|
||||
EdgeBasedGraphFactory::EdgeBasedGraphFactory(
|
||||
std::shared_ptr<NodeBasedDynamicGraph> node_based_graph,
|
||||
const CompressedEdgeContainer &compressed_edge_container,
|
||||
const std::unordered_set<NodeID> &barrier_nodes,
|
||||
const std::unordered_set<NodeID> &traffic_lights,
|
||||
std::shared_ptr<const RestrictionMap> restriction_map,
|
||||
const std::vector<QueryNode> &node_info_list,
|
||||
SpeedProfileProperties speed_profile)
|
||||
: m_max_edge_id(0), m_node_info_list(node_info_list), m_node_based_graph(std::move(node_based_graph)),
|
||||
m_restriction_map(std::move(restriction_map)), m_barrier_nodes(barrier_nodes),
|
||||
m_traffic_lights(traffic_lights), m_compressed_edge_container(compressed_edge_container),
|
||||
speed_profile(std::move(speed_profile))
|
||||
{
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::GetEdgeBasedEdges(DeallocatingVector<EdgeBasedEdge> &output_edge_list)
|
||||
{
|
||||
BOOST_ASSERT_MSG(0 == output_edge_list.size(), "Vector is not empty");
|
||||
using std::swap; // Koenig swap
|
||||
swap(m_edge_based_edge_list, 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
|
||||
using std::swap; // Koenig swap
|
||||
swap(nodes, m_edge_based_node_list);
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::GetStartPointMarkers(std::vector<bool> &node_is_startpoint)
|
||||
{
|
||||
using std::swap; // Koenig swap
|
||||
swap(m_edge_based_node_is_startpoint, node_is_startpoint);
|
||||
}
|
||||
|
||||
unsigned EdgeBasedGraphFactory::GetHighestEdgeID()
|
||||
{
|
||||
return m_max_edge_id;
|
||||
}
|
||||
|
||||
void EdgeBasedGraphFactory::InsertEdgeBasedNode(const NodeID node_u,
|
||||
const NodeID node_v)
|
||||
{
|
||||
// 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 edge_id_1 = m_node_based_graph->FindEdge(node_u, node_v);
|
||||
BOOST_ASSERT(edge_id_1 != SPECIAL_EDGEID);
|
||||
|
||||
const EdgeData &forward_data = m_node_based_graph->GetEdgeData(edge_id_1);
|
||||
|
||||
// find reverse edge id and
|
||||
const EdgeID edge_id_2 = m_node_based_graph->FindEdge(node_v, node_u);
|
||||
BOOST_ASSERT(edge_id_2 != SPECIAL_EDGEID);
|
||||
|
||||
const EdgeData &reverse_data = m_node_based_graph->GetEdgeData(edge_id_2);
|
||||
|
||||
if (forward_data.edge_id == SPECIAL_NODEID &&
|
||||
reverse_data.edge_id == SPECIAL_NODEID)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_1) ==
|
||||
m_compressed_edge_container.HasEntryForID(edge_id_2));
|
||||
if (m_compressed_edge_container.HasEntryForID(edge_id_1))
|
||||
{
|
||||
BOOST_ASSERT(m_compressed_edge_container.HasEntryForID(edge_id_2));
|
||||
|
||||
// reconstruct geometry and put in each individual edge with its offset
|
||||
const auto& forward_geometry = m_compressed_edge_container.GetBucketReference(edge_id_1);
|
||||
const auto& reverse_geometry = m_compressed_edge_container.GetBucketReference(edge_id_2);
|
||||
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;
|
||||
|
||||
// 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.edge_id, reverse_data.edge_id,
|
||||
current_edge_source_coordinate_id, current_edge_target_coordinate_id,
|
||||
forward_data.name_id, forward_geometry[i].second,
|
||||
reverse_geometry[geometry_size - 1 - i].second, forward_dist_prefix_sum[i],
|
||||
reverse_dist_prefix_sum[i], m_compressed_edge_container.GetPositionForID(edge_id_1),
|
||||
false, INVALID_COMPONENTID, i, forward_data.travel_mode, reverse_data.travel_mode);
|
||||
m_edge_based_node_is_startpoint.push_back(forward_data.startpoint || reverse_data.startpoint);
|
||||
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_compressed_edge_container.HasEntryForID(edge_id_2));
|
||||
|
||||
if (forward_data.edge_id != SPECIAL_NODEID)
|
||||
{
|
||||
BOOST_ASSERT(!forward_data.reversed);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(forward_data.reversed);
|
||||
}
|
||||
|
||||
if (reverse_data.edge_id != SPECIAL_NODEID)
|
||||
{
|
||||
BOOST_ASSERT(!reverse_data.reversed);
|
||||
}
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(reverse_data.reversed);
|
||||
}
|
||||
|
||||
BOOST_ASSERT(forward_data.edge_id != SPECIAL_NODEID ||
|
||||
reverse_data.edge_id != SPECIAL_NODEID);
|
||||
|
||||
m_edge_based_node_list.emplace_back(
|
||||
forward_data.edge_id, reverse_data.edge_id, node_u, node_v,
|
||||
forward_data.name_id, forward_data.distance, reverse_data.distance, 0, 0, SPECIAL_EDGEID,
|
||||
false, INVALID_COMPONENTID, 0, forward_data.travel_mode, reverse_data.travel_mode);
|
||||
m_edge_based_node_is_startpoint.push_back(forward_data.startpoint || reverse_data.startpoint);
|
||||
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();
|
||||
}
|
||||
|
||||
#ifdef DEBUG_GEOMETRY
|
||||
void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename,
|
||||
lua_State *lua_state,
|
||||
const std::string &edge_segment_lookup_filename,
|
||||
const std::string &edge_penalty_filename,
|
||||
const bool generate_edge_lookup,
|
||||
const std::string &debug_turns_path)
|
||||
#else
|
||||
void EdgeBasedGraphFactory::Run(const std::string &original_edge_data_filename,
|
||||
lua_State *lua_state,
|
||||
const std::string &edge_segment_lookup_filename,
|
||||
const std::string &edge_penalty_filename,
|
||||
const bool generate_edge_lookup)
|
||||
#endif
|
||||
{
|
||||
TIMER_START(renumber);
|
||||
m_max_edge_id = RenumberEdges() - 1;
|
||||
TIMER_STOP(renumber);
|
||||
|
||||
TIMER_START(generate_nodes);
|
||||
GenerateEdgeExpandedNodes();
|
||||
TIMER_STOP(generate_nodes);
|
||||
|
||||
TIMER_START(generate_edges);
|
||||
#ifdef DEBUG_GEOMETRY
|
||||
GenerateEdgeExpandedEdges(original_edge_data_filename, lua_state,
|
||||
edge_segment_lookup_filename,edge_penalty_filename,
|
||||
generate_edge_lookup, debug_turns_path);
|
||||
#else
|
||||
GenerateEdgeExpandedEdges(original_edge_data_filename, lua_state,
|
||||
edge_segment_lookup_filename,edge_penalty_filename,
|
||||
generate_edge_lookup);
|
||||
#endif
|
||||
|
||||
TIMER_STOP(generate_edges);
|
||||
|
||||
SimpleLogger().Write() << "Timing statistics for edge-expanded graph:";
|
||||
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";
|
||||
}
|
||||
|
||||
|
||||
/// Renumbers all _forward_ edges and sets the edge_id.
|
||||
/// A specific numbering is not important. Any unique ID will do.
|
||||
/// Returns the number of edge based nodes.
|
||||
unsigned EdgeBasedGraphFactory::RenumberEdges()
|
||||
{
|
||||
// renumber edge based node of outgoing edges
|
||||
unsigned numbered_edges_count = 0;
|
||||
for (const auto current_node : osrm::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||
{
|
||||
for (const auto current_edge : m_node_based_graph->GetAdjacentEdgeRange(current_node))
|
||||
{
|
||||
EdgeData &edge_data = m_node_based_graph->GetEdgeData(current_edge);
|
||||
|
||||
// only number incoming edges
|
||||
if (edge_data.reversed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(numbered_edges_count < m_node_based_graph->GetNumberOfEdges());
|
||||
edge_data.edge_id = numbered_edges_count;
|
||||
++numbered_edges_count;
|
||||
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data.edge_id);
|
||||
}
|
||||
}
|
||||
|
||||
return numbered_edges_count;
|
||||
}
|
||||
|
||||
/// Creates the nodes in the edge expanded graph from edges in the node-based graph.
|
||||
void EdgeBasedGraphFactory::GenerateEdgeExpandedNodes()
|
||||
{
|
||||
Percent progress(m_node_based_graph->GetNumberOfNodes());
|
||||
|
||||
// loop over all edges and generate new set of nodes
|
||||
for (const auto node_u : osrm::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||
{
|
||||
BOOST_ASSERT(node_u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(node_u < m_node_based_graph->GetNumberOfNodes());
|
||||
progress.printStatus(node_u);
|
||||
for (EdgeID e1 : m_node_based_graph->GetAdjacentEdgeRange(node_u))
|
||||
{
|
||||
const EdgeData &edge_data = m_node_based_graph->GetEdgeData(e1);
|
||||
BOOST_ASSERT(e1 != SPECIAL_EDGEID);
|
||||
const NodeID node_v = m_node_based_graph->GetTarget(e1);
|
||||
|
||||
BOOST_ASSERT(SPECIAL_NODEID != node_v);
|
||||
// pick only every other edge, since we have every edge as an outgoing
|
||||
// and incoming egde
|
||||
if (node_u > node_v)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(node_u < node_v);
|
||||
|
||||
// if we found a non-forward edge reverse and try again
|
||||
if (edge_data.edge_id == SPECIAL_NODEID)
|
||||
{
|
||||
InsertEdgeBasedNode(node_v, node_u);
|
||||
}
|
||||
else
|
||||
{
|
||||
InsertEdgeBasedNode(node_u, node_v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_ASSERT(m_edge_based_node_list.size() == m_edge_based_node_is_startpoint.size());
|
||||
|
||||
SimpleLogger().Write() << "Generated " << m_edge_based_node_list.size()
|
||||
<< " nodes in edge-expanded graph";
|
||||
}
|
||||
|
||||
/// Actually it also generates OriginalEdgeData and serializes them...
|
||||
#ifdef DEBUG_GEOMETRY
|
||||
void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
const std::string &original_edge_data_filename, lua_State *lua_state,
|
||||
const std::string &edge_segment_lookup_filename,
|
||||
const std::string &edge_fixed_penalties_filename,
|
||||
const bool generate_edge_lookup,
|
||||
const std::string &debug_turns_path)
|
||||
#else
|
||||
void EdgeBasedGraphFactory::GenerateEdgeExpandedEdges(
|
||||
const std::string &original_edge_data_filename, lua_State *lua_state,
|
||||
const std::string &edge_segment_lookup_filename,
|
||||
const std::string &edge_fixed_penalties_filename,
|
||||
const bool generate_edge_lookup)
|
||||
#endif
|
||||
{
|
||||
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);
|
||||
std::ofstream edge_segment_file;
|
||||
std::ofstream edge_penalty_file;
|
||||
|
||||
if (generate_edge_lookup)
|
||||
{
|
||||
edge_segment_file.open(edge_segment_lookup_filename.c_str(), std::ios::binary);
|
||||
edge_penalty_file.open(edge_fixed_penalties_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());
|
||||
|
||||
#ifdef DEBUG_GEOMETRY
|
||||
DEBUG_TURNS_START(debug_turns_path);
|
||||
#endif
|
||||
|
||||
for (const auto node_u : osrm::irange(0u, m_node_based_graph->GetNumberOfNodes()))
|
||||
{
|
||||
//progress.printStatus(node_u);
|
||||
for (const EdgeID e1 : m_node_based_graph->GetAdjacentEdgeRange(node_u))
|
||||
{
|
||||
if (m_node_based_graph->GetEdgeData(e1).reversed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
++node_based_edge_counter;
|
||||
const NodeID node_v = m_node_based_graph->GetTarget(e1);
|
||||
const NodeID only_restriction_to_node =
|
||||
m_restriction_map->CheckForEmanatingIsOnlyTurn(node_u, node_v);
|
||||
const bool is_barrier_node = m_barrier_nodes.find(node_v) != m_barrier_nodes.end();
|
||||
|
||||
for (const EdgeID e2 : m_node_based_graph->GetAdjacentEdgeRange(node_v))
|
||||
{
|
||||
if (m_node_based_graph->GetEdgeData(e2).reversed)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID node_w = m_node_based_graph->GetTarget(e2);
|
||||
|
||||
if ((only_restriction_to_node != SPECIAL_NODEID) &&
|
||||
(node_w != only_restriction_to_node))
|
||||
{
|
||||
// We are at an only_-restriction but not at the right turn.
|
||||
++restricted_turns_counter;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_barrier_node)
|
||||
{
|
||||
if (node_u != node_w)
|
||||
{
|
||||
++skipped_barrier_turns_counter;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((node_u == node_w) && (m_node_based_graph->GetOutDegree(node_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(node_u, node_v, node_w) &&
|
||||
(only_restriction_to_node == SPECIAL_NODEID) &&
|
||||
(node_w != only_restriction_to_node))
|
||||
{
|
||||
// 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.edge_id != edge_data2.edge_id);
|
||||
BOOST_ASSERT(!edge_data1.reversed);
|
||||
BOOST_ASSERT(!edge_data2.reversed);
|
||||
|
||||
// the following is the core of the loop.
|
||||
unsigned distance = edge_data1.distance;
|
||||
if (m_traffic_lights.find(node_v) != m_traffic_lights.end())
|
||||
{
|
||||
distance += speed_profile.traffic_signal_penalty;
|
||||
|
||||
DEBUG_SIGNAL(node_v, m_node_info_list, speed_profile.traffic_signal_penalty);
|
||||
}
|
||||
|
||||
// unpack last node of first segment if packed
|
||||
const auto first_coordinate =
|
||||
m_node_info_list[(m_compressed_edge_container.HasEntryForID(e1)
|
||||
? m_compressed_edge_container.GetLastEdgeSourceID(e1)
|
||||
: node_u)];
|
||||
|
||||
// unpack first node of second segment if packed
|
||||
const auto third_coordinate =
|
||||
m_node_info_list[(m_compressed_edge_container.HasEntryForID(e2)
|
||||
? m_compressed_edge_container.GetFirstEdgeTargetID(e2)
|
||||
: node_w)];
|
||||
|
||||
const double turn_angle = ComputeAngle::OfThreeFixedPointCoordinates(
|
||||
first_coordinate, m_node_info_list[node_v], third_coordinate);
|
||||
|
||||
const int turn_penalty = GetTurnPenalty(turn_angle, lua_state);
|
||||
TurnInstruction turn_instruction = AnalyzeTurn(node_u, node_v, node_w, turn_angle);
|
||||
if (turn_instruction == TurnInstruction::UTurn)
|
||||
{
|
||||
distance += speed_profile.u_turn_penalty;
|
||||
|
||||
DEBUG_UTURN(node_v, m_node_info_list, speed_profile.u_turn_penalty);
|
||||
}
|
||||
|
||||
DEBUG_TURN(node_v, m_node_info_list, first_coordinate, turn_angle, turn_penalty);
|
||||
|
||||
distance += turn_penalty;
|
||||
|
||||
const bool edge_is_compressed = m_compressed_edge_container.HasEntryForID(e1);
|
||||
|
||||
if (edge_is_compressed)
|
||||
{
|
||||
++compressed;
|
||||
}
|
||||
|
||||
original_edge_data_vector.emplace_back(
|
||||
(edge_is_compressed ? m_compressed_edge_container.GetPositionForID(e1) : node_v),
|
||||
edge_data1.name_id, 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.edge_id);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != edge_data2.edge_id);
|
||||
|
||||
|
||||
// NOTE: potential overflow here if we hit 2^32 routable edges
|
||||
BOOST_ASSERT(m_edge_based_edge_list.size() <= std::numeric_limits<NodeID>::max());
|
||||
m_edge_based_edge_list.emplace_back(edge_data1.edge_id, edge_data2.edge_id,
|
||||
m_edge_based_edge_list.size(), distance, true, false);
|
||||
|
||||
|
||||
// Here is where we write out the mapping between the edge-expanded edges, and
|
||||
// the node-based edges that are originally used to calculate the `distance`
|
||||
// for the edge-expanded edges. About 40 lines back, there is:
|
||||
//
|
||||
// unsigned distance = edge_data1.distance;
|
||||
//
|
||||
// This tells us that the weight for an edge-expanded-edge is based on the weight
|
||||
// of the *source* node-based edge. Therefore, we will look up the individual
|
||||
// segments of the source node-based edge, and write out a mapping between
|
||||
// those and the edge-based-edge ID.
|
||||
// External programs can then use this mapping to quickly perform
|
||||
// updates to the edge-expanded-edge based directly on its ID.
|
||||
if (generate_edge_lookup)
|
||||
{
|
||||
unsigned fixed_penalty = distance - edge_data1.distance;
|
||||
edge_penalty_file.write(reinterpret_cast<const char *>(&fixed_penalty), sizeof(fixed_penalty));
|
||||
if (edge_is_compressed)
|
||||
{
|
||||
const auto node_based_edges = m_compressed_edge_container.GetBucketReference(e1);
|
||||
NodeID previous = node_u;
|
||||
|
||||
const unsigned node_count = node_based_edges.size()+1;
|
||||
edge_segment_file.write(reinterpret_cast<const char *>(&node_count), sizeof(node_count));
|
||||
const QueryNode &first_node = m_node_info_list[previous];
|
||||
edge_segment_file.write(reinterpret_cast<const char *>(&first_node.node_id), sizeof(first_node.node_id));
|
||||
|
||||
for (auto target_node : node_based_edges)
|
||||
{
|
||||
const QueryNode &from = m_node_info_list[previous];
|
||||
const QueryNode &to = m_node_info_list[target_node.first];
|
||||
const double segment_length = coordinate_calculation::great_circle_distance(from.lat, from.lon, to.lat, to.lon);
|
||||
|
||||
edge_segment_file.write(reinterpret_cast<const char *>(&to.node_id), sizeof(to.node_id));
|
||||
edge_segment_file.write(reinterpret_cast<const char *>(&segment_length), sizeof(segment_length));
|
||||
edge_segment_file.write(reinterpret_cast<const char *>(&target_node.second), sizeof(target_node.second));
|
||||
previous = target_node.first;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
static const unsigned node_count = 2;
|
||||
const QueryNode from = m_node_info_list[node_u];
|
||||
const QueryNode to = m_node_info_list[node_v];
|
||||
const double segment_length = coordinate_calculation::great_circle_distance(from.lat, from.lon, to.lat, to.lon);
|
||||
edge_segment_file.write(reinterpret_cast<const char *>(&node_count), sizeof(node_count));
|
||||
edge_segment_file.write(reinterpret_cast<const char *>(&from.node_id), sizeof(from.node_id));
|
||||
edge_segment_file.write(reinterpret_cast<const char *>(&to.node_id), sizeof(to.node_id));
|
||||
edge_segment_file.write(reinterpret_cast<const char *>(&segment_length), sizeof(segment_length));
|
||||
edge_segment_file.write(reinterpret_cast<const char *>(&edge_data1.distance), sizeof(edge_data1.distance));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_TURNS_STOP();
|
||||
|
||||
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
|
||||
double penalty = luabind::call_function<double>(lua_state, "turn_function", 180. - angle);
|
||||
return static_cast<int>(penalty);
|
||||
}
|
||||
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.name_id == data2.name_id && data1.travel_mode == data2.travel_mode)
|
||||
{
|
||||
// TODO: Here we should also do a small graph exploration to check for
|
||||
// more complex situations
|
||||
if (0 != data1.name_id || m_node_based_graph->GetOutDegree(node_v) <= 2)
|
||||
{
|
||||
return TurnInstruction::NoTurn;
|
||||
}
|
||||
}
|
||||
|
||||
return TurnInstructionsClass::GetTurnDirectionOfInstruction(angle);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Project OSRM contributors
|
||||
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 "external_memory_node.hpp"
|
||||
#include "query_node.hpp"
|
||||
|
||||
#include <limits>
|
||||
|
||||
ExternalMemoryNode::ExternalMemoryNode(
|
||||
int lat, int lon, OSMNodeID node_id, bool barrier, bool traffic_lights)
|
||||
: QueryNode(lat, lon, node_id), barrier(barrier), traffic_lights(traffic_lights)
|
||||
{
|
||||
}
|
||||
|
||||
ExternalMemoryNode::ExternalMemoryNode() : barrier(false), traffic_lights(false) {}
|
||||
|
||||
ExternalMemoryNode ExternalMemoryNode::min_value()
|
||||
{
|
||||
return ExternalMemoryNode(0, 0, MIN_OSM_NODEID, false, false);
|
||||
}
|
||||
|
||||
ExternalMemoryNode ExternalMemoryNode::max_value()
|
||||
{
|
||||
return ExternalMemoryNode(std::numeric_limits<int>::max(), std::numeric_limits<int>::max(),
|
||||
MAX_OSM_NODEID, false, false);
|
||||
}
|
||||
|
||||
bool ExternalMemoryNodeSTXXLCompare::operator()(const ExternalMemoryNode &left,
|
||||
const ExternalMemoryNode &right) const
|
||||
{
|
||||
return left.node_id < right.node_id;
|
||||
}
|
||||
|
||||
ExternalMemoryNodeSTXXLCompare::value_type ExternalMemoryNodeSTXXLCompare::max_value()
|
||||
{
|
||||
return ExternalMemoryNode::max_value();
|
||||
}
|
||||
|
||||
ExternalMemoryNodeSTXXLCompare::value_type ExternalMemoryNodeSTXXLCompare::min_value()
|
||||
{
|
||||
return ExternalMemoryNode::min_value();
|
||||
}
|
||||
@@ -0,0 +1,739 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
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 "extraction_containers.hpp"
|
||||
#include "extraction_way.hpp"
|
||||
|
||||
#include "../algorithms/coordinate_calculation.hpp"
|
||||
#include "../data_structures/node_id.hpp"
|
||||
#include "../data_structures/range_table.hpp"
|
||||
|
||||
#include "../util/osrm_exception.hpp"
|
||||
#include "../util/simple_logger.hpp"
|
||||
#include "../util/timing_util.hpp"
|
||||
#include "../util/fingerprint.hpp"
|
||||
#include "../util/lua_util.hpp"
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/ref.hpp>
|
||||
#include <boost/numeric/conversion/cast.hpp>
|
||||
|
||||
#include <luabind/luabind.hpp>
|
||||
|
||||
#include <stxxl/sort>
|
||||
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
|
||||
static const int WRITE_BLOCK_BUFFER_SIZE = 8000;
|
||||
|
||||
ExtractionContainers::ExtractionContainers()
|
||||
{
|
||||
// Check if stxxl can be instantiated
|
||||
stxxl::vector<unsigned> dummy_vector;
|
||||
// Insert the empty string, it has no data and is zero length
|
||||
name_lengths.push_back(0);
|
||||
}
|
||||
|
||||
ExtractionContainers::~ExtractionContainers()
|
||||
{
|
||||
// FIXME isn't this done implicitly of the stxxl::vectors go out of scope?
|
||||
used_node_id_list.clear();
|
||||
all_nodes_list.clear();
|
||||
all_edges_list.clear();
|
||||
name_char_data.clear();
|
||||
name_lengths.clear();
|
||||
restrictions_list.clear();
|
||||
way_start_end_id_list.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the collected data and serializes it.
|
||||
* At this point nodes are still referenced by their OSM id.
|
||||
*
|
||||
* - map start-end nodes of ways to ways used int restrictions to compute compressed
|
||||
* trippe representation
|
||||
* - filter nodes list to nodes that are referenced by ways
|
||||
* - merge edges with nodes to include location of start/end points and serialize
|
||||
*
|
||||
*/
|
||||
void ExtractionContainers::PrepareData(const std::string &output_file_name,
|
||||
const std::string &restrictions_file_name,
|
||||
const std::string &name_file_name,
|
||||
lua_State *segment_state)
|
||||
{
|
||||
try
|
||||
{
|
||||
std::ofstream file_out_stream;
|
||||
file_out_stream.open(output_file_name.c_str(), std::ios::binary);
|
||||
const FingerPrint fingerprint = FingerPrint::GetValid();
|
||||
file_out_stream.write((char *)&fingerprint, sizeof(FingerPrint));
|
||||
|
||||
PrepareNodes();
|
||||
WriteNodes(file_out_stream);
|
||||
PrepareEdges(segment_state);
|
||||
WriteEdges(file_out_stream);
|
||||
|
||||
file_out_stream.close();
|
||||
|
||||
PrepareRestrictions();
|
||||
WriteRestrictions(restrictions_file_name);
|
||||
|
||||
WriteNames(name_file_name);
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
std::cerr << "Caught Execption:" << e.what() << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractionContainers::WriteNames(const std::string& names_file_name) const
|
||||
{
|
||||
std::cout << "[extractor] writing street name index ... " << std::flush;
|
||||
TIMER_START(write_name_index);
|
||||
boost::filesystem::ofstream name_file_stream(names_file_name, std::ios::binary);
|
||||
|
||||
unsigned total_length = 0;
|
||||
|
||||
for (const unsigned &name_length : name_lengths)
|
||||
{
|
||||
total_length += name_length;
|
||||
}
|
||||
|
||||
// builds and writes the index
|
||||
RangeTable<> name_index_range(name_lengths);
|
||||
name_file_stream << name_index_range;
|
||||
|
||||
name_file_stream.write((char *)&total_length, sizeof(unsigned));
|
||||
|
||||
|
||||
// write all chars consecutively
|
||||
char write_buffer[WRITE_BLOCK_BUFFER_SIZE];
|
||||
unsigned buffer_len = 0;
|
||||
|
||||
for (const char &c : name_char_data)
|
||||
{
|
||||
write_buffer[buffer_len++] = c;
|
||||
|
||||
if (buffer_len >= WRITE_BLOCK_BUFFER_SIZE)
|
||||
{
|
||||
name_file_stream.write(write_buffer, WRITE_BLOCK_BUFFER_SIZE);
|
||||
buffer_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
name_file_stream.write(write_buffer, buffer_len);
|
||||
|
||||
name_file_stream.close();
|
||||
TIMER_STOP(write_name_index);
|
||||
std::cout << "ok, after " << TIMER_SEC(write_name_index) << "s" << std::endl;
|
||||
}
|
||||
|
||||
void ExtractionContainers::PrepareNodes()
|
||||
{
|
||||
std::cout << "[extractor] Sorting used nodes ... " << std::flush;
|
||||
TIMER_START(sorting_used_nodes);
|
||||
stxxl::sort(used_node_id_list.begin(), used_node_id_list.end(), Cmp(), stxxl_memory);
|
||||
TIMER_STOP(sorting_used_nodes);
|
||||
std::cout << "ok, after " << TIMER_SEC(sorting_used_nodes) << "s" << std::endl;
|
||||
|
||||
std::cout << "[extractor] Erasing duplicate nodes ... " << std::flush;
|
||||
TIMER_START(erasing_dups);
|
||||
auto new_end = std::unique(used_node_id_list.begin(), used_node_id_list.end());
|
||||
used_node_id_list.resize(new_end - used_node_id_list.begin());
|
||||
TIMER_STOP(erasing_dups);
|
||||
std::cout << "ok, after " << TIMER_SEC(erasing_dups) << "s" << std::endl;
|
||||
|
||||
std::cout << "[extractor] Sorting all nodes ... " << std::flush;
|
||||
TIMER_START(sorting_nodes);
|
||||
stxxl::sort(all_nodes_list.begin(), all_nodes_list.end(), ExternalMemoryNodeSTXXLCompare(),
|
||||
stxxl_memory);
|
||||
TIMER_STOP(sorting_nodes);
|
||||
std::cout << "ok, after " << TIMER_SEC(sorting_nodes) << "s" << std::endl;
|
||||
|
||||
std::cout << "[extractor] Building node id map ... " << std::flush;
|
||||
TIMER_START(id_map);
|
||||
external_to_internal_node_id_map.reserve(used_node_id_list.size());
|
||||
auto node_iter = all_nodes_list.begin();
|
||||
auto ref_iter = used_node_id_list.begin();
|
||||
const auto all_nodes_list_end = all_nodes_list.end();
|
||||
const auto used_node_id_list_end = used_node_id_list.end();
|
||||
// Note: despite being able to handle 64 bit OSM node ids, we can't
|
||||
// handle > uint32_t actual usable nodes. This should be OK for a while
|
||||
// because we usually route on a *lot* less than 2^32 of the OSM
|
||||
// graph nodes.
|
||||
std::size_t internal_id = 0;
|
||||
|
||||
// compute the intersection of nodes that were referenced and nodes we actually have
|
||||
while (node_iter != all_nodes_list_end && ref_iter != used_node_id_list_end)
|
||||
{
|
||||
if (node_iter->node_id < *ref_iter)
|
||||
{
|
||||
node_iter++;
|
||||
continue;
|
||||
}
|
||||
if (node_iter->node_id > *ref_iter)
|
||||
{
|
||||
ref_iter++;
|
||||
continue;
|
||||
}
|
||||
BOOST_ASSERT(node_iter->node_id == *ref_iter);
|
||||
external_to_internal_node_id_map[*ref_iter] = static_cast<NodeID>(internal_id++);
|
||||
node_iter++;
|
||||
ref_iter++;
|
||||
}
|
||||
if (internal_id > std::numeric_limits<NodeID>::max())
|
||||
{
|
||||
throw osrm::exception("There are too many nodes remaining after filtering, OSRM only supports 2^32 unique nodes");
|
||||
}
|
||||
max_internal_node_id = boost::numeric_cast<NodeID>(internal_id);
|
||||
TIMER_STOP(id_map);
|
||||
std::cout << "ok, after " << TIMER_SEC(id_map) << "s" << std::endl;
|
||||
|
||||
}
|
||||
|
||||
void ExtractionContainers::PrepareEdges(lua_State *segment_state)
|
||||
{
|
||||
// Sort edges by start.
|
||||
std::cout << "[extractor] Sorting edges by start ... " << std::flush;
|
||||
TIMER_START(sort_edges_by_start);
|
||||
stxxl::sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByOSMStartID(), stxxl_memory);
|
||||
TIMER_STOP(sort_edges_by_start);
|
||||
std::cout << "ok, after " << TIMER_SEC(sort_edges_by_start) << "s" << std::endl;
|
||||
|
||||
std::cout << "[extractor] Setting start coords ... " << std::flush;
|
||||
TIMER_START(set_start_coords);
|
||||
// Traverse list of edges and nodes in parallel and set start coord
|
||||
auto node_iterator = all_nodes_list.begin();
|
||||
auto edge_iterator = all_edges_list.begin();
|
||||
|
||||
const auto all_edges_list_end = all_edges_list.end();
|
||||
const auto all_nodes_list_end = all_nodes_list.end();
|
||||
|
||||
while (edge_iterator != all_edges_list_end && node_iterator != all_nodes_list_end)
|
||||
{
|
||||
if (edge_iterator->result.osm_source_id < node_iterator->node_id)
|
||||
{
|
||||
SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference " << edge_iterator->result.source;
|
||||
edge_iterator->result.source = SPECIAL_NODEID;
|
||||
++edge_iterator;
|
||||
continue;
|
||||
}
|
||||
if (edge_iterator->result.osm_source_id > node_iterator->node_id)
|
||||
{
|
||||
node_iterator++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// remove loops
|
||||
if (edge_iterator->result.osm_source_id == edge_iterator->result.osm_target_id)
|
||||
{
|
||||
edge_iterator->result.source = SPECIAL_NODEID;
|
||||
edge_iterator->result.target = SPECIAL_NODEID;
|
||||
++edge_iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(edge_iterator->result.osm_source_id == node_iterator->node_id);
|
||||
|
||||
// assign new node id
|
||||
auto id_iter = external_to_internal_node_id_map.find(node_iterator->node_id);
|
||||
BOOST_ASSERT(id_iter != external_to_internal_node_id_map.end());
|
||||
edge_iterator->result.source = id_iter->second;
|
||||
|
||||
edge_iterator->source_coordinate.lat = node_iterator->lat;
|
||||
edge_iterator->source_coordinate.lon = node_iterator->lon;
|
||||
++edge_iterator;
|
||||
}
|
||||
|
||||
// Remove all remaining edges. They are invalid because there are no corresponding nodes for
|
||||
// them. This happens when using osmosis with bbox or polygon to extract smaller areas.
|
||||
auto markSourcesInvalid = [](InternalExtractorEdge &edge)
|
||||
{
|
||||
SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference "
|
||||
<< edge.result.source;
|
||||
edge.result.source = SPECIAL_NODEID;
|
||||
edge.result.osm_source_id = SPECIAL_OSM_NODEID;
|
||||
};
|
||||
std::for_each(edge_iterator, all_edges_list_end, markSourcesInvalid);
|
||||
TIMER_STOP(set_start_coords);
|
||||
std::cout << "ok, after " << TIMER_SEC(set_start_coords) << "s" << std::endl;
|
||||
|
||||
// Sort Edges by target
|
||||
std::cout << "[extractor] Sorting edges by target ... " << std::flush;
|
||||
TIMER_START(sort_edges_by_target);
|
||||
stxxl::sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByOSMTargetID(),
|
||||
stxxl_memory);
|
||||
TIMER_STOP(sort_edges_by_target);
|
||||
std::cout << "ok, after " << TIMER_SEC(sort_edges_by_target) << "s" << std::endl;
|
||||
|
||||
// Compute edge weights
|
||||
std::cout << "[extractor] Computing edge weights ... " << std::flush;
|
||||
TIMER_START(compute_weights);
|
||||
node_iterator = all_nodes_list.begin();
|
||||
edge_iterator = all_edges_list.begin();
|
||||
const auto all_edges_list_end_ = all_edges_list.end();
|
||||
const auto all_nodes_list_end_ = all_nodes_list.end();
|
||||
|
||||
while (edge_iterator != all_edges_list_end_ && node_iterator != all_nodes_list_end_)
|
||||
{
|
||||
// skip all invalid edges
|
||||
if (edge_iterator->result.source == SPECIAL_NODEID)
|
||||
{
|
||||
++edge_iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (edge_iterator->result.osm_target_id < node_iterator->node_id)
|
||||
{
|
||||
SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference " << OSMNodeID_to_uint64_t(edge_iterator->result.osm_target_id);
|
||||
edge_iterator->result.target = SPECIAL_NODEID;
|
||||
++edge_iterator;
|
||||
continue;
|
||||
}
|
||||
if (edge_iterator->result.osm_target_id > node_iterator->node_id)
|
||||
{
|
||||
++node_iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(edge_iterator->result.osm_target_id == node_iterator->node_id);
|
||||
BOOST_ASSERT(edge_iterator->weight_data.speed >= 0);
|
||||
BOOST_ASSERT(edge_iterator->source_coordinate.lat != std::numeric_limits<int>::min());
|
||||
BOOST_ASSERT(edge_iterator->source_coordinate.lon != std::numeric_limits<int>::min());
|
||||
|
||||
const double distance = coordinate_calculation::great_circle_distance(
|
||||
edge_iterator->source_coordinate.lat, edge_iterator->source_coordinate.lon,
|
||||
node_iterator->lat, node_iterator->lon);
|
||||
|
||||
if (lua_function_exists(segment_state, "segment_function"))
|
||||
{
|
||||
luabind::call_function<void>(
|
||||
segment_state, "segment_function",
|
||||
boost::cref(edge_iterator->source_coordinate),
|
||||
boost::cref(*node_iterator),
|
||||
distance,
|
||||
boost::ref(edge_iterator->weight_data));
|
||||
}
|
||||
|
||||
const double weight = [distance](const InternalExtractorEdge::WeightData& data) {
|
||||
switch (data.type)
|
||||
{
|
||||
case InternalExtractorEdge::WeightType::EDGE_DURATION:
|
||||
case InternalExtractorEdge::WeightType::WAY_DURATION:
|
||||
return data.duration * 10.;
|
||||
break;
|
||||
case InternalExtractorEdge::WeightType::SPEED:
|
||||
return (distance * 10.) / (data.speed / 3.6);
|
||||
break;
|
||||
case InternalExtractorEdge::WeightType::INVALID:
|
||||
osrm::exception("invalid weight type");
|
||||
}
|
||||
return -1.0;
|
||||
}(edge_iterator->weight_data);
|
||||
|
||||
auto& edge = edge_iterator->result;
|
||||
edge.weight = std::max(1, static_cast<int>(std::floor(weight + .5)));
|
||||
|
||||
// assign new node id
|
||||
auto id_iter = external_to_internal_node_id_map.find(node_iterator->node_id);
|
||||
BOOST_ASSERT(id_iter != external_to_internal_node_id_map.end());
|
||||
edge.target = id_iter->second;
|
||||
|
||||
// orient edges consistently: source id < target id
|
||||
// important for multi-edge removal
|
||||
if (edge.source > edge.target)
|
||||
{
|
||||
std::swap(edge.source, edge.target);
|
||||
|
||||
// std::swap does not work with bit-fields
|
||||
bool temp = edge.forward;
|
||||
edge.forward = edge.backward;
|
||||
edge.backward = temp;
|
||||
}
|
||||
++edge_iterator;
|
||||
}
|
||||
|
||||
// Remove all remaining edges. They are invalid because there are no corresponding nodes for
|
||||
// them. This happens when using osmosis with bbox or polygon to extract smaller areas.
|
||||
auto markTargetsInvalid = [](InternalExtractorEdge &edge)
|
||||
{
|
||||
SimpleLogger().Write(LogLevel::logWARNING) << "Found invalid node reference "
|
||||
<< edge.result.target;
|
||||
edge.result.target = SPECIAL_NODEID;
|
||||
};
|
||||
std::for_each(edge_iterator, all_edges_list_end_, markTargetsInvalid);
|
||||
TIMER_STOP(compute_weights);
|
||||
std::cout << "ok, after " << TIMER_SEC(compute_weights) << "s" << std::endl;
|
||||
|
||||
// Sort edges by start.
|
||||
std::cout << "[extractor] Sorting edges by renumbered start ... " << std::flush;
|
||||
TIMER_START(sort_edges_by_renumbered_start);
|
||||
stxxl::sort(all_edges_list.begin(), all_edges_list.end(), CmpEdgeByInternalStartThenInternalTargetID(), stxxl_memory);
|
||||
TIMER_STOP(sort_edges_by_renumbered_start);
|
||||
std::cout << "ok, after " << TIMER_SEC(sort_edges_by_renumbered_start) << "s" << std::endl;
|
||||
|
||||
BOOST_ASSERT(all_edges_list.size() > 0);
|
||||
for (unsigned i = 0; i < all_edges_list.size();)
|
||||
{
|
||||
// only invalid edges left
|
||||
if (all_edges_list[i].result.source == SPECIAL_NODEID)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// skip invalid edges
|
||||
if (all_edges_list[i].result.target == SPECIAL_NODEID)
|
||||
{
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned start_idx = i;
|
||||
NodeID source = all_edges_list[i].result.source;
|
||||
NodeID target = all_edges_list[i].result.target;
|
||||
|
||||
int min_forward_weight = std::numeric_limits<int>::max();
|
||||
int min_backward_weight = std::numeric_limits<int>::max();
|
||||
unsigned min_forward_idx = std::numeric_limits<unsigned>::max();
|
||||
unsigned min_backward_idx = std::numeric_limits<unsigned>::max();
|
||||
|
||||
// find minimal edge in both directions
|
||||
while (all_edges_list[i].result.source == source &&
|
||||
all_edges_list[i].result.target == target)
|
||||
{
|
||||
if (all_edges_list[i].result.forward && all_edges_list[i].result.weight < min_forward_weight)
|
||||
{
|
||||
min_forward_idx = i;
|
||||
}
|
||||
if (all_edges_list[i].result.backward && all_edges_list[i].result.weight < min_backward_weight)
|
||||
{
|
||||
min_backward_idx = i;
|
||||
}
|
||||
|
||||
// this also increments the outer loop counter!
|
||||
i++;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(min_forward_idx == std::numeric_limits<unsigned>::max() || min_forward_idx < i);
|
||||
BOOST_ASSERT(min_backward_idx == std::numeric_limits<unsigned>::max() || min_backward_idx < i);
|
||||
BOOST_ASSERT(min_backward_idx != std::numeric_limits<unsigned>::max() ||
|
||||
min_forward_idx != std::numeric_limits<unsigned>::max());
|
||||
|
||||
if (min_backward_idx == min_forward_idx)
|
||||
{
|
||||
all_edges_list[min_forward_idx].result.is_split = false;
|
||||
all_edges_list[min_forward_idx].result.forward = true;
|
||||
all_edges_list[min_forward_idx].result.backward = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bool has_forward = min_forward_idx != std::numeric_limits<unsigned>::max();
|
||||
bool has_backward = min_backward_idx != std::numeric_limits<unsigned>::max();
|
||||
if (has_forward)
|
||||
{
|
||||
all_edges_list[min_forward_idx].result.forward = true;
|
||||
all_edges_list[min_forward_idx].result.backward = false;
|
||||
all_edges_list[min_forward_idx].result.is_split = has_backward;
|
||||
}
|
||||
if (has_backward)
|
||||
{
|
||||
std::swap(all_edges_list[min_backward_idx].result.source,
|
||||
all_edges_list[min_backward_idx].result.target);
|
||||
all_edges_list[min_backward_idx].result.forward = true;
|
||||
all_edges_list[min_backward_idx].result.backward = false;
|
||||
all_edges_list[min_backward_idx].result.is_split = has_forward;
|
||||
}
|
||||
}
|
||||
|
||||
// invalidate all unused edges
|
||||
for (unsigned j = start_idx; j < i; j++)
|
||||
{
|
||||
if (j == min_forward_idx || j == min_backward_idx)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
all_edges_list[j].result.source = SPECIAL_NODEID;
|
||||
all_edges_list[j].result.target = SPECIAL_NODEID;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExtractionContainers::WriteEdges(std::ofstream& file_out_stream) const
|
||||
{
|
||||
std::cout << "[extractor] Writing used edges ... " << std::flush;
|
||||
TIMER_START(write_edges);
|
||||
// Traverse list of edges and nodes in parallel and set target coord
|
||||
std::size_t used_edges_counter = 0;
|
||||
unsigned used_edges_counter_buffer = 0;
|
||||
|
||||
auto start_position = file_out_stream.tellp();
|
||||
file_out_stream.write((char *)&used_edges_counter_buffer, sizeof(unsigned));
|
||||
|
||||
for (const auto& edge : all_edges_list)
|
||||
{
|
||||
if (edge.result.source == SPECIAL_NODEID || edge.result.target == SPECIAL_NODEID)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// IMPORTANT: here, we're using slicing to only write the data from the base
|
||||
// class of NodeBasedEdgeWithOSM
|
||||
NodeBasedEdge tmp = edge.result;
|
||||
file_out_stream.write((char*) &tmp, sizeof(NodeBasedEdge));
|
||||
used_edges_counter++;
|
||||
}
|
||||
|
||||
if (used_edges_counter > std::numeric_limits<unsigned>::max())
|
||||
{
|
||||
throw osrm::exception("There are too many edges, OSRM only supports 2^32");
|
||||
}
|
||||
TIMER_STOP(write_edges);
|
||||
std::cout << "ok, after " << TIMER_SEC(write_edges) << "s" << std::endl;
|
||||
|
||||
std::cout << "[extractor] setting number of edges ... " << std::flush;
|
||||
|
||||
used_edges_counter_buffer = boost::numeric_cast<unsigned>(used_edges_counter);
|
||||
|
||||
file_out_stream.seekp(start_position);
|
||||
file_out_stream.write((char *)&used_edges_counter_buffer, sizeof(unsigned));
|
||||
std::cout << "ok" << std::endl;
|
||||
|
||||
SimpleLogger().Write() << "Processed " << used_edges_counter << " edges";
|
||||
}
|
||||
|
||||
void ExtractionContainers::WriteNodes(std::ofstream& file_out_stream) const
|
||||
{
|
||||
// write dummy value, will be overwritten later
|
||||
std::cout << "[extractor] setting number of nodes ... " << std::flush;
|
||||
file_out_stream.write((char *)&max_internal_node_id, sizeof(unsigned));
|
||||
std::cout << "ok" << std::endl;
|
||||
|
||||
std::cout << "[extractor] Confirming/Writing used nodes ... " << std::flush;
|
||||
TIMER_START(write_nodes);
|
||||
// identify all used nodes by a merging step of two sorted lists
|
||||
auto node_iterator = all_nodes_list.begin();
|
||||
auto node_id_iterator = used_node_id_list.begin();
|
||||
const auto used_node_id_list_end = used_node_id_list.end();
|
||||
const auto all_nodes_list_end = all_nodes_list.end();
|
||||
|
||||
while (node_id_iterator != used_node_id_list_end && node_iterator != all_nodes_list_end)
|
||||
{
|
||||
if (*node_id_iterator < node_iterator->node_id)
|
||||
{
|
||||
++node_id_iterator;
|
||||
continue;
|
||||
}
|
||||
if (*node_id_iterator > node_iterator->node_id)
|
||||
{
|
||||
++node_iterator;
|
||||
continue;
|
||||
}
|
||||
BOOST_ASSERT(*node_id_iterator == node_iterator->node_id);
|
||||
|
||||
file_out_stream.write((char *)&(*node_iterator), sizeof(ExternalMemoryNode));
|
||||
|
||||
++node_id_iterator;
|
||||
++node_iterator;
|
||||
}
|
||||
TIMER_STOP(write_nodes);
|
||||
std::cout << "ok, after " << TIMER_SEC(write_nodes) << "s" << std::endl;
|
||||
|
||||
|
||||
SimpleLogger().Write() << "Processed " << max_internal_node_id << " nodes";
|
||||
}
|
||||
|
||||
void ExtractionContainers::WriteRestrictions(const std::string& path) const
|
||||
{
|
||||
// serialize restrictions
|
||||
std::ofstream restrictions_out_stream;
|
||||
unsigned written_restriction_count = 0;
|
||||
restrictions_out_stream.open(path.c_str(), std::ios::binary);
|
||||
const FingerPrint fingerprint = FingerPrint::GetValid();
|
||||
restrictions_out_stream.write((char *)&fingerprint, sizeof(FingerPrint));
|
||||
const auto count_position = restrictions_out_stream.tellp();
|
||||
restrictions_out_stream.write((char *)&written_restriction_count, sizeof(unsigned));
|
||||
|
||||
for (const auto &restriction_container : restrictions_list)
|
||||
{
|
||||
if (SPECIAL_NODEID != restriction_container.restriction.from.node &&
|
||||
SPECIAL_NODEID != restriction_container.restriction.via.node &&
|
||||
SPECIAL_NODEID != restriction_container.restriction.to.node)
|
||||
{
|
||||
restrictions_out_stream.write((char *)&(restriction_container.restriction),
|
||||
sizeof(TurnRestriction));
|
||||
++written_restriction_count;
|
||||
}
|
||||
}
|
||||
restrictions_out_stream.seekp(count_position);
|
||||
restrictions_out_stream.write((char *)&written_restriction_count, sizeof(unsigned));
|
||||
restrictions_out_stream.close();
|
||||
SimpleLogger().Write() << "usable restrictions: " << written_restriction_count;
|
||||
}
|
||||
|
||||
void ExtractionContainers::PrepareRestrictions()
|
||||
{
|
||||
std::cout << "[extractor] Sorting used ways ... " << std::flush;
|
||||
TIMER_START(sort_ways);
|
||||
stxxl::sort(way_start_end_id_list.begin(), way_start_end_id_list.end(),
|
||||
FirstAndLastSegmentOfWayStxxlCompare(), stxxl_memory);
|
||||
TIMER_STOP(sort_ways);
|
||||
std::cout << "ok, after " << TIMER_SEC(sort_ways) << "s" << std::endl;
|
||||
|
||||
std::cout << "[extractor] Sorting " << restrictions_list.size()
|
||||
<< " restriction. by from... " << std::flush;
|
||||
TIMER_START(sort_restrictions);
|
||||
stxxl::sort(restrictions_list.begin(), restrictions_list.end(),
|
||||
CmpRestrictionContainerByFrom(), stxxl_memory);
|
||||
TIMER_STOP(sort_restrictions);
|
||||
std::cout << "ok, after " << TIMER_SEC(sort_restrictions) << "s" << std::endl;
|
||||
|
||||
std::cout << "[extractor] Fixing restriction starts ... " << std::flush;
|
||||
TIMER_START(fix_restriction_starts);
|
||||
auto restrictions_iterator = restrictions_list.begin();
|
||||
auto way_start_and_end_iterator = way_start_end_id_list.cbegin();
|
||||
const auto restrictions_list_end = restrictions_list.end();
|
||||
const auto way_start_end_id_list_end = way_start_end_id_list.cend();
|
||||
|
||||
while (way_start_and_end_iterator != way_start_end_id_list_end &&
|
||||
restrictions_iterator != restrictions_list_end)
|
||||
{
|
||||
if (way_start_and_end_iterator->way_id < OSMWayID(restrictions_iterator->restriction.from.way))
|
||||
{
|
||||
++way_start_and_end_iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (way_start_and_end_iterator->way_id > OSMWayID(restrictions_iterator->restriction.from.way))
|
||||
{
|
||||
SimpleLogger().Write(LogLevel::logDEBUG) << "Restriction references invalid way: " << restrictions_iterator->restriction.from.way;
|
||||
restrictions_iterator->restriction.from.node = SPECIAL_NODEID;
|
||||
++restrictions_iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
BOOST_ASSERT(way_start_and_end_iterator->way_id ==
|
||||
OSMWayID(restrictions_iterator->restriction.from.way));
|
||||
// we do not remap the via id yet, since we will need it for the to node as well
|
||||
const OSMNodeID via_node_id = OSMNodeID(restrictions_iterator->restriction.via.node);
|
||||
|
||||
// check if via is actually valid, if not invalidate
|
||||
auto via_id_iter = external_to_internal_node_id_map.find(via_node_id);
|
||||
if(via_id_iter == external_to_internal_node_id_map.end())
|
||||
{
|
||||
SimpleLogger().Write(LogLevel::logDEBUG) << "Restriction references invalid node: " << restrictions_iterator->restriction.via.node;
|
||||
restrictions_iterator->restriction.via.node = SPECIAL_NODEID;
|
||||
++restrictions_iterator;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (OSMNodeID(way_start_and_end_iterator->first_segment_source_id) == via_node_id)
|
||||
{
|
||||
// assign new from node id
|
||||
auto id_iter = external_to_internal_node_id_map.find(
|
||||
OSMNodeID(way_start_and_end_iterator->first_segment_target_id));
|
||||
BOOST_ASSERT(id_iter != external_to_internal_node_id_map.end());
|
||||
restrictions_iterator->restriction.from.node = id_iter->second;
|
||||
}
|
||||
else if (OSMNodeID(way_start_and_end_iterator->last_segment_target_id) == via_node_id)
|
||||
{
|
||||
// assign new from node id
|
||||
auto id_iter = external_to_internal_node_id_map.find(
|
||||
OSMNodeID(way_start_and_end_iterator->last_segment_source_id));
|
||||
BOOST_ASSERT(id_iter != external_to_internal_node_id_map.end());
|
||||
restrictions_iterator->restriction.from.node = id_iter->second;
|
||||
}
|
||||
++restrictions_iterator;
|
||||
}
|
||||
|
||||
TIMER_STOP(fix_restriction_starts);
|
||||
std::cout << "ok, after " << TIMER_SEC(fix_restriction_starts) << "s" << std::endl;
|
||||
|
||||
std::cout << "[extractor] Sorting restrictions. by to ... " << std::flush;
|
||||
TIMER_START(sort_restrictions_to);
|
||||
stxxl::sort(restrictions_list.begin(), restrictions_list.end(),
|
||||
CmpRestrictionContainerByTo(), stxxl_memory);
|
||||
TIMER_STOP(sort_restrictions_to);
|
||||
std::cout << "ok, after " << TIMER_SEC(sort_restrictions_to) << "s" << std::endl;
|
||||
|
||||
std::cout << "[extractor] Fixing restriction ends ... " << std::flush;
|
||||
TIMER_START(fix_restriction_ends);
|
||||
restrictions_iterator = restrictions_list.begin();
|
||||
way_start_and_end_iterator = way_start_end_id_list.cbegin();
|
||||
const auto way_start_end_id_list_end_ = way_start_end_id_list.cend();
|
||||
const auto restrictions_list_end_ = restrictions_list.end();
|
||||
|
||||
while (way_start_and_end_iterator != way_start_end_id_list_end_ &&
|
||||
restrictions_iterator != restrictions_list_end_)
|
||||
{
|
||||
if (way_start_and_end_iterator->way_id < OSMWayID(restrictions_iterator->restriction.to.way))
|
||||
{
|
||||
++way_start_and_end_iterator;
|
||||
continue;
|
||||
}
|
||||
if (restrictions_iterator->restriction.from.node == SPECIAL_NODEID ||
|
||||
restrictions_iterator->restriction.via.node == SPECIAL_NODEID)
|
||||
{
|
||||
++restrictions_iterator;
|
||||
continue;
|
||||
}
|
||||
if (way_start_and_end_iterator->way_id > OSMWayID(restrictions_iterator->restriction.to.way))
|
||||
{
|
||||
SimpleLogger().Write(LogLevel::logDEBUG) << "Restriction references invalid way: " << restrictions_iterator->restriction.to.way;
|
||||
restrictions_iterator->restriction.to.way = SPECIAL_NODEID;
|
||||
++restrictions_iterator;
|
||||
continue;
|
||||
}
|
||||
BOOST_ASSERT(way_start_and_end_iterator->way_id ==
|
||||
OSMWayID(restrictions_iterator->restriction.to.way));
|
||||
const OSMNodeID via_node_id = OSMNodeID(restrictions_iterator->restriction.via.node);
|
||||
|
||||
// assign new via node id
|
||||
auto via_id_iter = external_to_internal_node_id_map.find(via_node_id);
|
||||
BOOST_ASSERT(via_id_iter != external_to_internal_node_id_map.end());
|
||||
restrictions_iterator->restriction.via.node = via_id_iter->second;
|
||||
|
||||
if (OSMNodeID(way_start_and_end_iterator->first_segment_source_id) == via_node_id)
|
||||
{
|
||||
auto to_id_iter = external_to_internal_node_id_map.find(
|
||||
OSMNodeID(way_start_and_end_iterator->first_segment_target_id));
|
||||
BOOST_ASSERT(to_id_iter != external_to_internal_node_id_map.end());
|
||||
restrictions_iterator->restriction.to.node = to_id_iter->second;
|
||||
}
|
||||
else if (OSMNodeID(way_start_and_end_iterator->last_segment_target_id) == via_node_id)
|
||||
{
|
||||
auto to_id_iter = external_to_internal_node_id_map.find(
|
||||
OSMNodeID(way_start_and_end_iterator->last_segment_source_id));
|
||||
BOOST_ASSERT(to_id_iter != external_to_internal_node_id_map.end());
|
||||
restrictions_iterator->restriction.to.node = to_id_iter->second;
|
||||
}
|
||||
++restrictions_iterator;
|
||||
}
|
||||
TIMER_STOP(fix_restriction_ends);
|
||||
std::cout << "ok, after " << TIMER_SEC(fix_restriction_ends) << "s" << std::endl;
|
||||
}
|
||||
@@ -0,0 +1,638 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
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 "extractor.hpp"
|
||||
|
||||
#include "extraction_containers.hpp"
|
||||
#include "extraction_node.hpp"
|
||||
#include "extraction_way.hpp"
|
||||
#include "extractor_callbacks.hpp"
|
||||
#include "restriction_parser.hpp"
|
||||
#include "scripting_environment.hpp"
|
||||
|
||||
#include "../data_structures/raster_source.hpp"
|
||||
#include "../util/make_unique.hpp"
|
||||
#include "../util/simple_logger.hpp"
|
||||
#include "../util/timing_util.hpp"
|
||||
#include "../util/lua_util.hpp"
|
||||
#include "../util/graph_loader.hpp"
|
||||
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include "../data_structures/static_graph.hpp"
|
||||
#include "../data_structures/static_rtree.hpp"
|
||||
#include "../data_structures/restriction_map.hpp"
|
||||
#include "../data_structures/compressed_edge_container.hpp"
|
||||
|
||||
#include "../algorithms/tarjan_scc.hpp"
|
||||
#include "../algorithms/crc32_processor.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/fstream.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
#include <luabind/luabind.hpp>
|
||||
|
||||
#include <osmium/io/any_input.hpp>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* TODO: Refactor this function into smaller functions for better readability.
|
||||
*
|
||||
* This function is the entry point for the whole extraction process. The goal of the extraction
|
||||
* step is to filter and convert the OSM geometry to something more fitting for routing.
|
||||
* That includes:
|
||||
* - extracting turn restrictions
|
||||
* - splitting ways into (directional!) edge segments
|
||||
* - checking if nodes are barriers or traffic signal
|
||||
* - discarding all tag information: All relevant type information for nodes/ways
|
||||
* is extracted at this point.
|
||||
*
|
||||
* The result of this process are the following files:
|
||||
* .names : Names of all streets, stored as long consecutive string with prefix sum based index
|
||||
* .osrm : Nodes and edges in a intermediate format that easy to digest for osrm-prepare
|
||||
* .restrictions : Turn restrictions that are used my osrm-prepare to construct the edge-expanded
|
||||
* graph
|
||||
*
|
||||
*/
|
||||
int extractor::run()
|
||||
{
|
||||
try
|
||||
{
|
||||
LogPolicy::GetInstance().Unmute();
|
||||
TIMER_START(extracting);
|
||||
|
||||
const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads();
|
||||
const auto number_of_threads =
|
||||
std::min(recommended_num_threads, config.requested_num_threads);
|
||||
tbb::task_scheduler_init init(number_of_threads);
|
||||
|
||||
SimpleLogger().Write() << "Input file: " << config.input_path.filename().string();
|
||||
SimpleLogger().Write() << "Profile: " << config.profile_path.filename().string();
|
||||
SimpleLogger().Write() << "Threads: " << number_of_threads;
|
||||
|
||||
// setup scripting environment
|
||||
ScriptingEnvironment scripting_environment(config.profile_path.string().c_str());
|
||||
|
||||
ExtractionContainers extraction_containers;
|
||||
auto extractor_callbacks = osrm::make_unique<ExtractorCallbacks>(extraction_containers);
|
||||
|
||||
const osmium::io::File input_file(config.input_path.string());
|
||||
osmium::io::Reader reader(input_file);
|
||||
const osmium::io::Header header = reader.header();
|
||||
|
||||
std::atomic<unsigned> number_of_nodes{0};
|
||||
std::atomic<unsigned> number_of_ways{0};
|
||||
std::atomic<unsigned> number_of_relations{0};
|
||||
std::atomic<unsigned> number_of_others{0};
|
||||
|
||||
SimpleLogger().Write() << "Parsing in progress..";
|
||||
TIMER_START(parsing);
|
||||
|
||||
lua_State *segment_state = scripting_environment.get_lua_state();
|
||||
|
||||
if (lua_function_exists(segment_state, "source_function"))
|
||||
{
|
||||
// bind a single instance of SourceContainer class to relevant lua state
|
||||
SourceContainer sources;
|
||||
luabind::globals(segment_state)["sources"] = sources;
|
||||
|
||||
luabind::call_function<void>(segment_state, "source_function");
|
||||
}
|
||||
|
||||
std::string generator = header.get("generator");
|
||||
if (generator.empty())
|
||||
{
|
||||
generator = "unknown tool";
|
||||
}
|
||||
SimpleLogger().Write() << "input file generated by " << generator;
|
||||
|
||||
// write .timestamp data file
|
||||
std::string timestamp = header.get("osmosis_replication_timestamp");
|
||||
if (timestamp.empty())
|
||||
{
|
||||
timestamp = "n/a";
|
||||
}
|
||||
SimpleLogger().Write() << "timestamp: " << timestamp;
|
||||
|
||||
boost::filesystem::ofstream timestamp_out(config.timestamp_file_name);
|
||||
timestamp_out.write(timestamp.c_str(), timestamp.length());
|
||||
timestamp_out.close();
|
||||
|
||||
// initialize vectors holding parsed objects
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionNode>> resulting_nodes;
|
||||
tbb::concurrent_vector<std::pair<std::size_t, ExtractionWay>> resulting_ways;
|
||||
tbb::concurrent_vector<boost::optional<InputRestrictionContainer>> resulting_restrictions;
|
||||
|
||||
// setup restriction parser
|
||||
const RestrictionParser restriction_parser(scripting_environment.get_lua_state());
|
||||
|
||||
while (const osmium::memory::Buffer buffer = reader.read())
|
||||
{
|
||||
// create a vector of iterators into the buffer
|
||||
std::vector<osmium::memory::Buffer::const_iterator> osm_elements;
|
||||
for (auto iter = std::begin(buffer), end = std::end(buffer); iter != end; ++iter)
|
||||
{
|
||||
osm_elements.push_back(iter);
|
||||
}
|
||||
|
||||
// clear resulting vectors
|
||||
resulting_nodes.clear();
|
||||
resulting_ways.clear();
|
||||
resulting_restrictions.clear();
|
||||
|
||||
// parse OSM entities in parallel, store in resulting vectors
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<std::size_t>(0, osm_elements.size()),
|
||||
[&](const tbb::blocked_range<std::size_t> &range)
|
||||
{
|
||||
ExtractionNode result_node;
|
||||
ExtractionWay result_way;
|
||||
lua_State *local_state = scripting_environment.get_lua_state();
|
||||
|
||||
for (auto x = range.begin(), end = range.end(); x != end; ++x)
|
||||
{
|
||||
const auto entity = osm_elements[x];
|
||||
|
||||
switch (entity->type())
|
||||
{
|
||||
case osmium::item_type::node:
|
||||
result_node.clear();
|
||||
++number_of_nodes;
|
||||
luabind::call_function<void>(
|
||||
local_state, "node_function",
|
||||
boost::cref(static_cast<const osmium::Node &>(*entity)),
|
||||
boost::ref(result_node));
|
||||
resulting_nodes.push_back(std::make_pair(x, result_node));
|
||||
break;
|
||||
case osmium::item_type::way:
|
||||
result_way.clear();
|
||||
++number_of_ways;
|
||||
luabind::call_function<void>(
|
||||
local_state, "way_function",
|
||||
boost::cref(static_cast<const osmium::Way &>(*entity)),
|
||||
boost::ref(result_way));
|
||||
resulting_ways.push_back(std::make_pair(x, result_way));
|
||||
break;
|
||||
case osmium::item_type::relation:
|
||||
++number_of_relations;
|
||||
resulting_restrictions.push_back(restriction_parser.TryParse(
|
||||
static_cast<const osmium::Relation &>(*entity)));
|
||||
break;
|
||||
default:
|
||||
++number_of_others;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// put parsed objects thru extractor callbacks
|
||||
for (const auto &result : resulting_nodes)
|
||||
{
|
||||
extractor_callbacks->ProcessNode(
|
||||
static_cast<const osmium::Node &>(*(osm_elements[result.first])),
|
||||
result.second);
|
||||
}
|
||||
for (const auto &result : resulting_ways)
|
||||
{
|
||||
extractor_callbacks->ProcessWay(
|
||||
static_cast<const osmium::Way &>(*(osm_elements[result.first])), result.second);
|
||||
}
|
||||
for (const auto &result : resulting_restrictions)
|
||||
{
|
||||
extractor_callbacks->ProcessRestriction(result);
|
||||
}
|
||||
}
|
||||
TIMER_STOP(parsing);
|
||||
SimpleLogger().Write() << "Parsing finished after " << TIMER_SEC(parsing) << " seconds";
|
||||
|
||||
SimpleLogger().Write() << "Raw input contains " << number_of_nodes.load() << " nodes, "
|
||||
<< number_of_ways.load() << " ways, and "
|
||||
<< number_of_relations.load() << " relations, and "
|
||||
<< number_of_others.load() << " unknown entities";
|
||||
|
||||
extractor_callbacks.reset();
|
||||
|
||||
if (extraction_containers.all_edges_list.empty())
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "The input data is empty, exiting.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
extraction_containers.PrepareData(config.output_file_name, config.restriction_file_name,
|
||||
config.names_file_name, segment_state);
|
||||
|
||||
TIMER_STOP(extracting);
|
||||
SimpleLogger().Write() << "extraction finished after " << TIMER_SEC(extracting) << "s";
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << e.what();
|
||||
return 1;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// Transform the node-based graph that OSM is based on into an edge-based graph
|
||||
// that is better for routing. Every edge becomes a node, and every valid
|
||||
// movement (e.g. turn from A->B, and B->A) becomes an edge
|
||||
//
|
||||
//
|
||||
// // Create a new lua state
|
||||
|
||||
SimpleLogger().Write() << "Generating edge-expanded graph representation";
|
||||
|
||||
TIMER_START(expansion);
|
||||
|
||||
std::vector<EdgeBasedNode> node_based_edge_list;
|
||||
DeallocatingVector<EdgeBasedEdge> edge_based_edge_list;
|
||||
std::vector<bool> node_is_startpoint;
|
||||
std::vector<QueryNode> internal_to_external_node_map;
|
||||
auto graph_size =
|
||||
BuildEdgeExpandedGraph(internal_to_external_node_map, node_based_edge_list,
|
||||
node_is_startpoint, edge_based_edge_list);
|
||||
|
||||
auto number_of_node_based_nodes = graph_size.first;
|
||||
auto max_edge_id = graph_size.second;
|
||||
|
||||
TIMER_STOP(expansion);
|
||||
|
||||
SimpleLogger().Write() << "building r-tree ...";
|
||||
TIMER_START(rtree);
|
||||
|
||||
FindComponents(max_edge_id, edge_based_edge_list, node_based_edge_list);
|
||||
|
||||
BuildRTree(std::move(node_based_edge_list), std::move(node_is_startpoint),
|
||||
internal_to_external_node_map);
|
||||
|
||||
TIMER_STOP(rtree);
|
||||
|
||||
SimpleLogger().Write() << "writing node map ...";
|
||||
WriteNodeMapping(internal_to_external_node_map);
|
||||
|
||||
WriteEdgeBasedGraph(config.edge_graph_output_path, max_edge_id, edge_based_edge_list);
|
||||
|
||||
SimpleLogger().Write() << "Expansion : "
|
||||
<< (number_of_node_based_nodes / TIMER_SEC(expansion))
|
||||
<< " nodes/sec and " << ((max_edge_id + 1) / TIMER_SEC(expansion))
|
||||
<< " edges/sec";
|
||||
SimpleLogger().Write() << "To prepare the data for routing, run: "
|
||||
<< "./osrm-prepare " << config.output_file_name << std::endl;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << e.what();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Setups scripting environment (lua-scripting)
|
||||
Also initializes speed profile.
|
||||
*/
|
||||
void extractor::SetupScriptingEnvironment(lua_State *lua_state,
|
||||
SpeedProfileProperties &speed_profile)
|
||||
{
|
||||
// open utility libraries string library;
|
||||
luaL_openlibs(lua_state);
|
||||
|
||||
// adjust lua load path
|
||||
luaAddScriptFolderToLoadPath(lua_state, config.profile_path.string().c_str());
|
||||
|
||||
// Now call our function in a lua script
|
||||
if (0 != luaL_dofile(lua_state, config.profile_path.string().c_str()))
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << lua_tostring(lua_state, -1) << " occurred in scripting block";
|
||||
throw osrm::exception(msg.str());
|
||||
}
|
||||
|
||||
if (0 != luaL_dostring(lua_state, "return traffic_signal_penalty\n"))
|
||||
{
|
||||
std::stringstream msg;
|
||||
msg << lua_tostring(lua_state, -1) << " occurred in scripting block";
|
||||
throw osrm::exception(msg.str());
|
||||
}
|
||||
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::stringstream msg;
|
||||
msg << lua_tostring(lua_state, -1) << " occurred in scripting block";
|
||||
throw osrm::exception(msg.str());
|
||||
}
|
||||
|
||||
speed_profile.u_turn_penalty = 10 * lua_tointeger(lua_state, -1);
|
||||
speed_profile.has_turn_penalty_function = lua_function_exists(lua_state, "turn_function");
|
||||
}
|
||||
|
||||
void extractor::FindComponents(unsigned max_edge_id,
|
||||
const DeallocatingVector<EdgeBasedEdge> &input_edge_list,
|
||||
std::vector<EdgeBasedNode> &input_nodes) const
|
||||
{
|
||||
struct UncontractedEdgeData
|
||||
{
|
||||
};
|
||||
struct InputEdge
|
||||
{
|
||||
unsigned source;
|
||||
unsigned target;
|
||||
UncontractedEdgeData data;
|
||||
|
||||
bool operator<(const InputEdge &rhs) const
|
||||
{
|
||||
return source < rhs.source || (source == rhs.source && target < rhs.target);
|
||||
}
|
||||
|
||||
bool operator==(const InputEdge &rhs) const
|
||||
{
|
||||
return source == rhs.source && target == rhs.target;
|
||||
}
|
||||
};
|
||||
using UncontractedGraph = StaticGraph<UncontractedEdgeData>;
|
||||
std::vector<InputEdge> edges;
|
||||
edges.reserve(input_edge_list.size() * 2);
|
||||
|
||||
for (const auto &edge : input_edge_list)
|
||||
{
|
||||
BOOST_ASSERT_MSG(static_cast<unsigned int>(std::max(edge.weight, 1)) > 0,
|
||||
"edge distance < 1");
|
||||
if (edge.forward)
|
||||
{
|
||||
edges.push_back({edge.source, edge.target, {}});
|
||||
}
|
||||
|
||||
if (edge.backward)
|
||||
{
|
||||
edges.push_back({edge.target, edge.source, {}});
|
||||
}
|
||||
}
|
||||
|
||||
// connect forward and backward nodes of each edge
|
||||
for (const auto &node : input_nodes)
|
||||
{
|
||||
if (node.reverse_edge_based_node_id != SPECIAL_NODEID)
|
||||
{
|
||||
edges.push_back({node.forward_edge_based_node_id, node.reverse_edge_based_node_id, {}});
|
||||
edges.push_back({node.reverse_edge_based_node_id, node.forward_edge_based_node_id, {}});
|
||||
}
|
||||
}
|
||||
|
||||
tbb::parallel_sort(edges.begin(), edges.end());
|
||||
auto new_end = std::unique(edges.begin(), edges.end());
|
||||
edges.resize(new_end - edges.begin());
|
||||
|
||||
auto uncontractor_graph = std::make_shared<UncontractedGraph>(max_edge_id + 1, edges);
|
||||
|
||||
TarjanSCC<UncontractedGraph> component_search(
|
||||
std::const_pointer_cast<const UncontractedGraph>(uncontractor_graph));
|
||||
component_search.run();
|
||||
|
||||
for (auto &node : input_nodes)
|
||||
{
|
||||
auto forward_component = component_search.get_component_id(node.forward_edge_based_node_id);
|
||||
BOOST_ASSERT(node.reverse_edge_based_node_id == SPECIAL_EDGEID ||
|
||||
forward_component ==
|
||||
component_search.get_component_id(node.reverse_edge_based_node_id));
|
||||
|
||||
const unsigned component_size = component_search.get_component_size(forward_component);
|
||||
node.component.is_tiny = component_size < config.small_component_size;
|
||||
node.component.id = 1 + forward_component;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Build load restrictions from .restriction file
|
||||
*/
|
||||
std::shared_ptr<RestrictionMap> extractor::LoadRestrictionMap()
|
||||
{
|
||||
boost::filesystem::ifstream input_stream(config.restriction_file_name,
|
||||
std::ios::in | std::ios::binary);
|
||||
|
||||
std::vector<TurnRestriction> restriction_list;
|
||||
loadRestrictionsFromFile(input_stream, restriction_list);
|
||||
|
||||
SimpleLogger().Write() << " - " << restriction_list.size() << " restrictions.";
|
||||
|
||||
return std::make_shared<RestrictionMap>(restriction_list);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Load node based graph from .osrm file
|
||||
*/
|
||||
std::shared_ptr<NodeBasedDynamicGraph>
|
||||
extractor::LoadNodeBasedGraph(std::unordered_set<NodeID> &barrier_nodes,
|
||||
std::unordered_set<NodeID> &traffic_lights,
|
||||
std::vector<QueryNode> &internal_to_external_node_map)
|
||||
{
|
||||
std::vector<NodeBasedEdge> edge_list;
|
||||
|
||||
boost::filesystem::ifstream input_stream(config.output_file_name,
|
||||
std::ios::in | std::ios::binary);
|
||||
|
||||
std::vector<NodeID> barrier_list;
|
||||
std::vector<NodeID> traffic_light_list;
|
||||
NodeID number_of_node_based_nodes = loadNodesFromFile(
|
||||
input_stream, barrier_list, traffic_light_list, internal_to_external_node_map);
|
||||
|
||||
SimpleLogger().Write() << " - " << barrier_list.size() << " bollard nodes, "
|
||||
<< traffic_light_list.size() << " traffic lights";
|
||||
|
||||
// insert into unordered sets for fast lookup
|
||||
barrier_nodes.insert(barrier_list.begin(), barrier_list.end());
|
||||
traffic_lights.insert(traffic_light_list.begin(), traffic_light_list.end());
|
||||
|
||||
barrier_list.clear();
|
||||
barrier_list.shrink_to_fit();
|
||||
traffic_light_list.clear();
|
||||
traffic_light_list.shrink_to_fit();
|
||||
|
||||
loadEdgesFromFile(input_stream, edge_list);
|
||||
|
||||
if (edge_list.empty())
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << "The input data is empty, exiting.";
|
||||
return std::shared_ptr<NodeBasedDynamicGraph>();
|
||||
}
|
||||
|
||||
return NodeBasedDynamicGraphFromEdges(number_of_node_based_nodes, edge_list);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Building an edge-expanded graph from node-based input and turn restrictions
|
||||
*/
|
||||
std::pair<std::size_t, std::size_t>
|
||||
extractor::BuildEdgeExpandedGraph(std::vector<QueryNode> &internal_to_external_node_map,
|
||||
std::vector<EdgeBasedNode> &node_based_edge_list,
|
||||
std::vector<bool> &node_is_startpoint,
|
||||
DeallocatingVector<EdgeBasedEdge> &edge_based_edge_list)
|
||||
{
|
||||
lua_State *lua_state = luaL_newstate();
|
||||
luabind::open(lua_state);
|
||||
|
||||
SpeedProfileProperties speed_profile;
|
||||
SetupScriptingEnvironment(lua_state, speed_profile);
|
||||
|
||||
std::unordered_set<NodeID> barrier_nodes;
|
||||
std::unordered_set<NodeID> traffic_lights;
|
||||
|
||||
auto restriction_map = LoadRestrictionMap();
|
||||
auto node_based_graph =
|
||||
LoadNodeBasedGraph(barrier_nodes, traffic_lights, internal_to_external_node_map);
|
||||
|
||||
CompressedEdgeContainer compressed_edge_container;
|
||||
GraphCompressor graph_compressor(speed_profile);
|
||||
graph_compressor.Compress(barrier_nodes, traffic_lights, *restriction_map, *node_based_graph,
|
||||
compressed_edge_container);
|
||||
|
||||
EdgeBasedGraphFactory edge_based_graph_factory(
|
||||
node_based_graph, compressed_edge_container, barrier_nodes, traffic_lights,
|
||||
std::const_pointer_cast<RestrictionMap const>(restriction_map),
|
||||
internal_to_external_node_map, speed_profile);
|
||||
|
||||
compressed_edge_container.SerializeInternalVector(config.geometry_output_path);
|
||||
|
||||
edge_based_graph_factory.Run(config.edge_output_path, lua_state,
|
||||
config.edge_segment_lookup_path, config.edge_penalty_path,
|
||||
config.generate_edge_lookup
|
||||
#ifdef DEBUG_GEOMETRY
|
||||
,
|
||||
config.debug_turns_path
|
||||
#endif
|
||||
);
|
||||
lua_close(lua_state);
|
||||
|
||||
edge_based_graph_factory.GetEdgeBasedEdges(edge_based_edge_list);
|
||||
edge_based_graph_factory.GetEdgeBasedNodes(node_based_edge_list);
|
||||
edge_based_graph_factory.GetStartPointMarkers(node_is_startpoint);
|
||||
auto max_edge_id = edge_based_graph_factory.GetHighestEdgeID();
|
||||
|
||||
const std::size_t number_of_node_based_nodes = node_based_graph->GetNumberOfNodes();
|
||||
return std::make_pair(number_of_node_based_nodes, max_edge_id);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Writing info on original (node-based) nodes
|
||||
*/
|
||||
void extractor::WriteNodeMapping(const std::vector<QueryNode> &internal_to_external_node_map)
|
||||
{
|
||||
boost::filesystem::ofstream node_stream(config.node_output_path, 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.data(),
|
||||
size_of_mapping * sizeof(QueryNode));
|
||||
}
|
||||
node_stream.close();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Building rtree-based nearest-neighbor data structure
|
||||
|
||||
Saves tree into '.ramIndex' and leaves into '.fileIndex'.
|
||||
*/
|
||||
void extractor::BuildRTree(std::vector<EdgeBasedNode> node_based_edge_list,
|
||||
std::vector<bool> node_is_startpoint,
|
||||
const std::vector<QueryNode> &internal_to_external_node_map)
|
||||
{
|
||||
SimpleLogger().Write() << "constructing r-tree of " << node_based_edge_list.size()
|
||||
<< " edge elements build on-top of "
|
||||
<< internal_to_external_node_map.size() << " coordinates";
|
||||
|
||||
BOOST_ASSERT(node_is_startpoint.size() == node_based_edge_list.size());
|
||||
|
||||
// Filter node based edges based on startpoint
|
||||
auto out_iter = node_based_edge_list.begin();
|
||||
auto in_iter = node_based_edge_list.begin();
|
||||
for (auto index : osrm::irange<std::size_t>(0, node_is_startpoint.size()))
|
||||
{
|
||||
BOOST_ASSERT(in_iter != node_based_edge_list.end());
|
||||
if (node_is_startpoint[index])
|
||||
{
|
||||
*out_iter = *in_iter;
|
||||
out_iter++;
|
||||
}
|
||||
in_iter++;
|
||||
}
|
||||
auto new_size = out_iter - node_based_edge_list.begin();
|
||||
node_based_edge_list.resize(new_size);
|
||||
|
||||
TIMER_START(construction);
|
||||
StaticRTree<EdgeBasedNode>(node_based_edge_list, config.rtree_nodes_output_path,
|
||||
config.rtree_leafs_output_path, internal_to_external_node_map);
|
||||
|
||||
TIMER_STOP(construction);
|
||||
SimpleLogger().Write() << "finished r-tree construction in " << TIMER_SEC(construction)
|
||||
<< " seconds";
|
||||
}
|
||||
|
||||
void extractor::WriteEdgeBasedGraph(std::string const &output_file_filename,
|
||||
size_t const max_edge_id,
|
||||
DeallocatingVector<EdgeBasedEdge> const &edge_based_edge_list)
|
||||
{
|
||||
|
||||
std::ofstream file_out_stream;
|
||||
file_out_stream.open(output_file_filename.c_str(), std::ios::binary);
|
||||
const FingerPrint fingerprint = FingerPrint::GetValid();
|
||||
file_out_stream.write((char *)&fingerprint, sizeof(FingerPrint));
|
||||
|
||||
std::cout << "[extractor] Writing edge-based-graph egdes ... " << std::flush;
|
||||
TIMER_START(write_edges);
|
||||
|
||||
size_t number_of_used_edges = edge_based_edge_list.size();
|
||||
file_out_stream.write((char *)&number_of_used_edges, sizeof(size_t));
|
||||
file_out_stream.write((char *)&max_edge_id, sizeof(size_t));
|
||||
|
||||
for (const auto &edge : edge_based_edge_list)
|
||||
{
|
||||
file_out_stream.write((char *)&edge, sizeof(EdgeBasedEdge));
|
||||
}
|
||||
|
||||
TIMER_STOP(write_edges);
|
||||
std::cout << "ok, after " << TIMER_SEC(write_edges) << "s" << std::endl;
|
||||
|
||||
SimpleLogger().Write() << "Processed " << number_of_used_edges << " edges";
|
||||
file_out_stream.close();
|
||||
}
|
||||
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
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 "extractor_callbacks.hpp"
|
||||
#include "extraction_containers.hpp"
|
||||
#include "extraction_node.hpp"
|
||||
#include "extraction_way.hpp"
|
||||
|
||||
#include "../data_structures/external_memory_node.hpp"
|
||||
#include "../data_structures/restriction.hpp"
|
||||
#include "../util/container.hpp"
|
||||
#include "../util/simple_logger.hpp"
|
||||
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
#include <osmium/osm.hpp>
|
||||
|
||||
#include <osrm/coordinate.hpp>
|
||||
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
ExtractorCallbacks::ExtractorCallbacks(ExtractionContainers &extraction_containers)
|
||||
: external_memory(extraction_containers)
|
||||
{
|
||||
string_map[""] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes the node position from osmium and the filtered properties from the lua
|
||||
* profile and saves them to external memory.
|
||||
*
|
||||
* warning: caller needs to take care of synchronization!
|
||||
*/
|
||||
void ExtractorCallbacks::ProcessNode(const osmium::Node &input_node,
|
||||
const ExtractionNode &result_node)
|
||||
{
|
||||
external_memory.all_nodes_list.push_back(
|
||||
{static_cast<int>(input_node.location().lat() * COORDINATE_PRECISION),
|
||||
static_cast<int>(input_node.location().lon() * COORDINATE_PRECISION),
|
||||
OSMNodeID(input_node.id()),
|
||||
result_node.barrier,
|
||||
result_node.traffic_lights});
|
||||
}
|
||||
|
||||
void ExtractorCallbacks::ProcessRestriction(
|
||||
const boost::optional<InputRestrictionContainer> &restriction)
|
||||
{
|
||||
if (restriction)
|
||||
{
|
||||
external_memory.restrictions_list.push_back(restriction.get());
|
||||
// SimpleLogger().Write() << "from: " << restriction.get().restriction.from.node <<
|
||||
// ",via: " << restriction.get().restriction.via.node <<
|
||||
// ", to: " << restriction.get().restriction.to.node <<
|
||||
// ", only: " << (restriction.get().restriction.flags.is_only ?
|
||||
// "y" : "n");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Takes the geometry contained in the ```input_way``` and the tags computed
|
||||
* by the lua profile inside ```parsed_way``` and computes all edge segments.
|
||||
*
|
||||
* Depending on the forward/backwards weights the edges are split into forward
|
||||
* and backward edges.
|
||||
*
|
||||
* warning: caller needs to take care of synchronization!
|
||||
*/
|
||||
void ExtractorCallbacks::ProcessWay(const osmium::Way &input_way, const ExtractionWay &parsed_way)
|
||||
{
|
||||
if (((0 >= parsed_way.forward_speed) ||
|
||||
(TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode)) &&
|
||||
((0 >= parsed_way.backward_speed) ||
|
||||
(TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode)) &&
|
||||
(0 >= parsed_way.duration))
|
||||
{ // Only true if the way is specified by the speed profile
|
||||
return;
|
||||
}
|
||||
|
||||
if (input_way.nodes().size() <= 1)
|
||||
{ // safe-guard against broken data
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::numeric_limits<decltype(input_way.id())>::max() == input_way.id())
|
||||
{
|
||||
SimpleLogger().Write(logDEBUG) << "found bogus way with id: " << input_way.id()
|
||||
<< " of size " << input_way.nodes().size();
|
||||
return;
|
||||
}
|
||||
|
||||
InternalExtractorEdge::WeightData forward_weight_data;
|
||||
InternalExtractorEdge::WeightData backward_weight_data;
|
||||
|
||||
if (0 < parsed_way.duration)
|
||||
{
|
||||
const unsigned num_edges = (input_way.nodes().size() - 1);
|
||||
// FIXME We devide by the numer of nodes here, but should rather consider
|
||||
// the length of each segment. We would eigther have to compute the length
|
||||
// of the whole way here (we can't: no node coordinates) or push that back
|
||||
// to the container and keep a reference to the way.
|
||||
forward_weight_data.duration = parsed_way.duration / num_edges;
|
||||
forward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION;
|
||||
backward_weight_data.duration = parsed_way.duration / num_edges;
|
||||
backward_weight_data.type = InternalExtractorEdge::WeightType::WAY_DURATION;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (parsed_way.forward_speed > 0 &&
|
||||
parsed_way.forward_travel_mode != TRAVEL_MODE_INACCESSIBLE)
|
||||
{
|
||||
forward_weight_data.speed = parsed_way.forward_speed;
|
||||
forward_weight_data.type = InternalExtractorEdge::WeightType::SPEED;
|
||||
}
|
||||
if (parsed_way.backward_speed > 0 &&
|
||||
parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE)
|
||||
{
|
||||
backward_weight_data.speed = parsed_way.backward_speed;
|
||||
backward_weight_data.type = InternalExtractorEdge::WeightType::SPEED;
|
||||
}
|
||||
}
|
||||
|
||||
if (forward_weight_data.type == InternalExtractorEdge::WeightType::INVALID &&
|
||||
backward_weight_data.type == InternalExtractorEdge::WeightType::INVALID)
|
||||
{
|
||||
SimpleLogger().Write(logDEBUG) << "found way with bogus speed, id: " << input_way.id();
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the unique identifier for the street name
|
||||
const auto &string_map_iterator = string_map.find(parsed_way.name);
|
||||
unsigned name_id = external_memory.name_lengths.size();
|
||||
if (string_map.end() == string_map_iterator)
|
||||
{
|
||||
auto name_length = std::min<unsigned>(255u, parsed_way.name.size());
|
||||
std::copy(parsed_way.name.c_str(), parsed_way.name.c_str() + name_length, std::back_inserter(external_memory.name_char_data));
|
||||
external_memory.name_lengths.push_back(name_length);
|
||||
string_map.insert(std::make_pair(parsed_way.name, name_id));
|
||||
}
|
||||
else
|
||||
{
|
||||
name_id = string_map_iterator->second;
|
||||
}
|
||||
|
||||
const bool split_edge = (parsed_way.forward_speed > 0) &&
|
||||
(TRAVEL_MODE_INACCESSIBLE != parsed_way.forward_travel_mode) &&
|
||||
(parsed_way.backward_speed > 0) &&
|
||||
(TRAVEL_MODE_INACCESSIBLE != parsed_way.backward_travel_mode) &&
|
||||
((parsed_way.forward_speed != parsed_way.backward_speed) ||
|
||||
(parsed_way.forward_travel_mode != parsed_way.backward_travel_mode));
|
||||
|
||||
std::transform(input_way.nodes().begin(), input_way.nodes().end(),
|
||||
std::back_inserter(external_memory.used_node_id_list),
|
||||
[](const osmium::NodeRef &ref)
|
||||
{
|
||||
return OSMNodeID(ref.ref());
|
||||
});
|
||||
|
||||
const bool is_opposite_way = TRAVEL_MODE_INACCESSIBLE == parsed_way.forward_travel_mode;
|
||||
|
||||
// traverse way in reverse in this case
|
||||
if (is_opposite_way)
|
||||
{
|
||||
BOOST_ASSERT(split_edge == false);
|
||||
BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE);
|
||||
osrm::for_each_pair(input_way.nodes().crbegin(), input_way.nodes().crend(),
|
||||
[&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node)
|
||||
{
|
||||
external_memory.all_edges_list.push_back(InternalExtractorEdge(
|
||||
OSMNodeID(first_node.ref()), OSMNodeID(last_node.ref()), name_id,
|
||||
backward_weight_data, true, false, parsed_way.roundabout,
|
||||
parsed_way.is_access_restricted, parsed_way.is_startpoint,
|
||||
parsed_way.backward_travel_mode, false));
|
||||
});
|
||||
|
||||
external_memory.way_start_end_id_list.push_back(
|
||||
{OSMWayID(input_way.id()),
|
||||
OSMNodeID(input_way.nodes().back().ref()),
|
||||
OSMNodeID(input_way.nodes()[input_way.nodes().size() - 2].ref()),
|
||||
OSMNodeID(input_way.nodes()[1].ref()),
|
||||
OSMNodeID(input_way.nodes()[0].ref())});
|
||||
}
|
||||
else
|
||||
{
|
||||
const bool forward_only =
|
||||
split_edge || TRAVEL_MODE_INACCESSIBLE == parsed_way.backward_travel_mode;
|
||||
osrm::for_each_pair(input_way.nodes().cbegin(), input_way.nodes().cend(),
|
||||
[&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node)
|
||||
{
|
||||
external_memory.all_edges_list.push_back(InternalExtractorEdge(
|
||||
OSMNodeID(first_node.ref()), OSMNodeID(last_node.ref()), name_id, forward_weight_data,
|
||||
true, !forward_only, parsed_way.roundabout,
|
||||
parsed_way.is_access_restricted, parsed_way.is_startpoint, parsed_way.forward_travel_mode,
|
||||
split_edge));
|
||||
});
|
||||
if (split_edge)
|
||||
{
|
||||
BOOST_ASSERT(parsed_way.backward_travel_mode != TRAVEL_MODE_INACCESSIBLE);
|
||||
osrm::for_each_pair(
|
||||
input_way.nodes().cbegin(), input_way.nodes().cend(),
|
||||
[&](const osmium::NodeRef &first_node, const osmium::NodeRef &last_node)
|
||||
{
|
||||
external_memory.all_edges_list.push_back(InternalExtractorEdge(
|
||||
OSMNodeID(first_node.ref()), OSMNodeID(last_node.ref()), name_id, backward_weight_data, false,
|
||||
true, parsed_way.roundabout, parsed_way.is_access_restricted,
|
||||
parsed_way.is_startpoint, parsed_way.backward_travel_mode, true));
|
||||
});
|
||||
}
|
||||
|
||||
external_memory.way_start_end_id_list.push_back(
|
||||
{OSMWayID(input_way.id()),
|
||||
OSMNodeID(input_way.nodes().back().ref()),
|
||||
OSMNodeID(input_way.nodes()[input_way.nodes().size() - 2].ref()),
|
||||
OSMNodeID(input_way.nodes()[1].ref()),
|
||||
OSMNodeID(input_way.nodes()[0].ref())});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
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 "extractor_options.hpp"
|
||||
|
||||
#include "util/version.hpp"
|
||||
#include "../util/ini_file.hpp"
|
||||
#include "../util/simple_logger.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
|
||||
return_code
|
||||
ExtractorOptions::ParseArguments(int argc, char *argv[], ExtractorConfig &extractor_config)
|
||||
{
|
||||
// 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")(
|
||||
/*
|
||||
* TODO: re-enable this
|
||||
"restrictions,r",
|
||||
boost::program_options::value<boost::filesystem::path>(&extractor_config.restrictions_path),
|
||||
"Restrictions file in .osrm.restrictions format")(
|
||||
*/
|
||||
"config,c", boost::program_options::value<boost::filesystem::path>(
|
||||
&extractor_config.config_file_path)->default_value("extractor.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()("profile,p",
|
||||
boost::program_options::value<boost::filesystem::path>(
|
||||
&extractor_config.profile_path)->default_value("profile.lua"),
|
||||
"Path to LUA routing profile")(
|
||||
"threads,t",
|
||||
boost::program_options::value<unsigned int>(&extractor_config.requested_num_threads)
|
||||
->default_value(tbb::task_scheduler_init::default_num_threads()),
|
||||
"Number of threads to use")(
|
||||
"generate-edge-lookup",boost::program_options::value<bool>(
|
||||
&extractor_config.generate_edge_lookup)->implicit_value(true)->default_value(false),
|
||||
"Generate a lookup table for internal edge-expanded-edge IDs to OSM node pairs")(
|
||||
"small-component-size",
|
||||
boost::program_options::value<unsigned int>(&extractor_config.small_component_size)
|
||||
->default_value(1000),
|
||||
"Number of nodes required before a strongly-connected-componennt is considered big (affects nearest neighbor snapping)");
|
||||
|
||||
#ifdef DEBUG_GEOMETRY
|
||||
config_options.add_options()("debug-turns",
|
||||
boost::program_options::value<std::string>(&extractor_config.debug_turns_path),
|
||||
"Write out GeoJSON with turn penalty data");
|
||||
#endif // DEBUG_GEOMETRY
|
||||
|
||||
// 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>(
|
||||
&extractor_config.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(
|
||||
boost::filesystem::basename(argv[0]) + " <input.osm/.osm.bz2/.osm.pbf> [options]");
|
||||
visible_options.add(generic_options).add(config_options);
|
||||
|
||||
// parse command line options
|
||||
try
|
||||
{
|
||||
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);
|
||||
if (option_variables.count("version"))
|
||||
{
|
||||
SimpleLogger().Write() << OSRM_VERSION;
|
||||
return return_code::exit;
|
||||
}
|
||||
|
||||
if (option_variables.count("help"))
|
||||
{
|
||||
SimpleLogger().Write() << visible_options;
|
||||
return return_code::exit;
|
||||
}
|
||||
|
||||
boost::program_options::notify(option_variables);
|
||||
|
||||
// parse config file
|
||||
if (boost::filesystem::is_regular_file(extractor_config.config_file_path))
|
||||
{
|
||||
SimpleLogger().Write()
|
||||
<< "Reading options from: " << extractor_config.config_file_path.string();
|
||||
std::string ini_file_contents =
|
||||
read_file_lower_content(extractor_config.config_file_path);
|
||||
std::stringstream config_stream(ini_file_contents);
|
||||
boost::program_options::store(parse_config_file(config_stream, config_file_options),
|
||||
option_variables);
|
||||
boost::program_options::notify(option_variables);
|
||||
}
|
||||
|
||||
if (!option_variables.count("input"))
|
||||
{
|
||||
SimpleLogger().Write() << visible_options;
|
||||
return return_code::exit;
|
||||
}
|
||||
}
|
||||
catch (std::exception &e)
|
||||
{
|
||||
SimpleLogger().Write(logWARNING) << e.what();
|
||||
return return_code::fail;
|
||||
}
|
||||
|
||||
return return_code::ok;
|
||||
}
|
||||
|
||||
void ExtractorOptions::GenerateOutputFilesNames(ExtractorConfig &extractor_config)
|
||||
{
|
||||
boost::filesystem::path &input_path = extractor_config.input_path;
|
||||
extractor_config.output_file_name = input_path.string();
|
||||
extractor_config.restriction_file_name = input_path.string();
|
||||
extractor_config.names_file_name = input_path.string();
|
||||
extractor_config.timestamp_file_name = input_path.string();
|
||||
extractor_config.geometry_output_path = input_path.string();
|
||||
extractor_config.edge_output_path = input_path.string();
|
||||
extractor_config.edge_graph_output_path = input_path.string();
|
||||
extractor_config.node_output_path = input_path.string();
|
||||
extractor_config.rtree_nodes_output_path = input_path.string();
|
||||
extractor_config.rtree_leafs_output_path = input_path.string();
|
||||
extractor_config.edge_segment_lookup_path = input_path.string();
|
||||
extractor_config.edge_penalty_path = input_path.string();
|
||||
std::string::size_type pos = extractor_config.output_file_name.find(".osm.bz2");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
pos = extractor_config.output_file_name.find(".osm.pbf");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
pos = extractor_config.output_file_name.find(".osm.xml");
|
||||
}
|
||||
}
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
pos = extractor_config.output_file_name.find(".pbf");
|
||||
}
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
pos = extractor_config.output_file_name.find(".osm");
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
extractor_config.output_file_name.append(".osrm");
|
||||
extractor_config.restriction_file_name.append(".osrm.restrictions");
|
||||
extractor_config.names_file_name.append(".osrm.names");
|
||||
extractor_config.timestamp_file_name.append(".osrm.timestamp");
|
||||
extractor_config.geometry_output_path.append(".osrm.geometry");
|
||||
extractor_config.node_output_path.append(".osrm.nodes");
|
||||
extractor_config.edge_output_path.append(".osrm.edges");
|
||||
extractor_config.edge_graph_output_path.append(".osrm.ebg");
|
||||
extractor_config.rtree_nodes_output_path.append(".osrm.ramIndex");
|
||||
extractor_config.rtree_leafs_output_path.append(".osrm.fileIndex");
|
||||
extractor_config.edge_segment_lookup_path.append(".osrm.edge_segment_lookup");
|
||||
extractor_config.edge_penalty_path.append(".osrm.edge_penalties");
|
||||
}
|
||||
else
|
||||
{
|
||||
extractor_config.output_file_name.replace(pos, 5, ".osrm");
|
||||
extractor_config.restriction_file_name.replace(pos, 5, ".osrm.restrictions");
|
||||
extractor_config.names_file_name.replace(pos, 5, ".osrm.names");
|
||||
extractor_config.timestamp_file_name.replace(pos, 5, ".osrm.timestamp");
|
||||
extractor_config.geometry_output_path.replace(pos, 5, ".osrm.geometry");
|
||||
extractor_config.node_output_path.replace(pos, 5, ".osrm.nodes");
|
||||
extractor_config.edge_output_path.replace(pos, 5, ".osrm.edges");
|
||||
extractor_config.edge_graph_output_path.replace(pos, 5, ".osrm.ebg");
|
||||
extractor_config.rtree_nodes_output_path.replace(pos, 5, ".osrm.ramIndex");
|
||||
extractor_config.rtree_leafs_output_path.replace(pos, 5, ".osrm.fileIndex");
|
||||
extractor_config.edge_segment_lookup_path.replace(pos,5, ".osrm.edge_segment_lookup");
|
||||
extractor_config.edge_penalty_path.replace(pos,5, ".osrm.edge_penalties");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
extractor_config.output_file_name.replace(pos, 8, ".osrm");
|
||||
extractor_config.restriction_file_name.replace(pos, 8, ".osrm.restrictions");
|
||||
extractor_config.names_file_name.replace(pos, 8, ".osrm.names");
|
||||
extractor_config.timestamp_file_name.replace(pos, 8, ".osrm.timestamp");
|
||||
extractor_config.geometry_output_path.replace(pos, 8, ".osrm.geometry");
|
||||
extractor_config.node_output_path.replace(pos, 8, ".osrm.nodes");
|
||||
extractor_config.edge_output_path.replace(pos, 8, ".osrm.edges");
|
||||
extractor_config.edge_graph_output_path.replace(pos, 8, ".osrm.ebg");
|
||||
extractor_config.rtree_nodes_output_path.replace(pos, 8, ".osrm.ramIndex");
|
||||
extractor_config.rtree_leafs_output_path.replace(pos, 8, ".osrm.fileIndex");
|
||||
extractor_config.edge_segment_lookup_path.replace(pos,8, ".osrm.edge_segment_lookup");
|
||||
extractor_config.edge_penalty_path.replace(pos,8, ".osrm.edge_penalties");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
#include "graph_compressor.hpp"
|
||||
|
||||
#include "../data_structures/compressed_edge_container.hpp"
|
||||
#include "../data_structures/dynamic_graph.hpp"
|
||||
#include "../data_structures/node_based_graph.hpp"
|
||||
#include "../data_structures/restriction_map.hpp"
|
||||
#include "../data_structures/percent.hpp"
|
||||
|
||||
#include "../util/simple_logger.hpp"
|
||||
|
||||
GraphCompressor::GraphCompressor(SpeedProfileProperties speed_profile)
|
||||
: speed_profile(std::move(speed_profile))
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void GraphCompressor::Compress(const std::unordered_set<NodeID>& barrier_nodes,
|
||||
const std::unordered_set<NodeID>& traffic_lights,
|
||||
RestrictionMap& restriction_map,
|
||||
NodeBasedDynamicGraph& graph,
|
||||
CompressedEdgeContainer& geometry_compressor)
|
||||
{
|
||||
const unsigned original_number_of_nodes = graph.GetNumberOfNodes();
|
||||
const unsigned original_number_of_edges = graph.GetNumberOfEdges();
|
||||
|
||||
Percent progress(original_number_of_nodes);
|
||||
|
||||
for (const NodeID node_v : osrm::irange(0u, original_number_of_nodes))
|
||||
{
|
||||
progress.printStatus(node_v);
|
||||
|
||||
// only contract degree 2 vertices
|
||||
if (2 != graph.GetOutDegree(node_v))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// don't contract barrier node
|
||||
if (barrier_nodes.end() != barrier_nodes.find(node_v))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// check if v is a via node for a turn restriction, i.e. a 'directed' barrier node
|
||||
if (restriction_map.IsViaNode(node_v))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// reverse_e2 forward_e2
|
||||
// u <---------- v -----------> w
|
||||
// ----------> <-----------
|
||||
// forward_e1 reverse_e1
|
||||
//
|
||||
// Will be compressed to:
|
||||
//
|
||||
// reverse_e1
|
||||
// u <---------- w
|
||||
// ---------->
|
||||
// forward_e1
|
||||
//
|
||||
// If the edges are compatible.
|
||||
|
||||
const bool reverse_edge_order = graph.GetEdgeData(graph.BeginEdges(node_v)).reversed;
|
||||
const EdgeID forward_e2 = graph.BeginEdges(node_v) + reverse_edge_order;
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != forward_e2);
|
||||
BOOST_ASSERT(forward_e2 >= graph.BeginEdges(node_v) &&
|
||||
forward_e2 < graph.EndEdges(node_v));
|
||||
const EdgeID reverse_e2 = graph.BeginEdges(node_v) + 1 - reverse_edge_order;
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != reverse_e2);
|
||||
BOOST_ASSERT(reverse_e2 >= graph.BeginEdges(node_v) &&
|
||||
reverse_e2 < graph.EndEdges(node_v));
|
||||
|
||||
const EdgeData &fwd_edge_data2 = graph.GetEdgeData(forward_e2);
|
||||
const EdgeData &rev_edge_data2 = graph.GetEdgeData(reverse_e2);
|
||||
|
||||
const NodeID node_w = graph.GetTarget(forward_e2);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != node_w);
|
||||
BOOST_ASSERT(node_v != node_w);
|
||||
const NodeID node_u = graph.GetTarget(reverse_e2);
|
||||
BOOST_ASSERT(SPECIAL_NODEID != node_u);
|
||||
BOOST_ASSERT(node_u != node_v);
|
||||
|
||||
const EdgeID forward_e1 = graph.FindEdge(node_u, node_v);
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != forward_e1);
|
||||
BOOST_ASSERT(node_v == graph.GetTarget(forward_e1));
|
||||
const EdgeID reverse_e1 = graph.FindEdge(node_w, node_v);
|
||||
BOOST_ASSERT(SPECIAL_EDGEID != reverse_e1);
|
||||
BOOST_ASSERT(node_v == graph.GetTarget(reverse_e1));
|
||||
|
||||
const EdgeData &fwd_edge_data1 = graph.GetEdgeData(forward_e1);
|
||||
const EdgeData &rev_edge_data1 = graph.GetEdgeData(reverse_e1);
|
||||
|
||||
if (graph.FindEdgeInEitherDirection(node_u, node_w) != SPECIAL_EDGEID)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// this case can happen if two ways with different names overlap
|
||||
if (fwd_edge_data1.name_id != rev_edge_data1.name_id ||
|
||||
fwd_edge_data2.name_id != rev_edge_data2.name_id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (fwd_edge_data1.IsCompatibleTo(fwd_edge_data2) && rev_edge_data1.IsCompatibleTo(rev_edge_data2))
|
||||
{
|
||||
BOOST_ASSERT(graph.GetEdgeData(forward_e1).name_id ==
|
||||
graph.GetEdgeData(reverse_e1).name_id);
|
||||
BOOST_ASSERT(graph.GetEdgeData(forward_e2).name_id ==
|
||||
graph.GetEdgeData(reverse_e2).name_id);
|
||||
|
||||
// Get distances before graph is modified
|
||||
const int forward_weight1 = graph.GetEdgeData(forward_e1).distance;
|
||||
const int forward_weight2 = graph.GetEdgeData(forward_e2).distance;
|
||||
|
||||
BOOST_ASSERT(0 != forward_weight1);
|
||||
BOOST_ASSERT(0 != forward_weight2);
|
||||
|
||||
const int reverse_weight1 = graph.GetEdgeData(reverse_e1).distance;
|
||||
const int reverse_weight2 = graph.GetEdgeData(reverse_e2).distance;
|
||||
|
||||
BOOST_ASSERT(0 != reverse_weight1);
|
||||
BOOST_ASSERT(0 != reverse_weight2);
|
||||
|
||||
const bool has_node_penalty = traffic_lights.find(node_v) != traffic_lights.end();
|
||||
|
||||
// add weight of e2's to e1
|
||||
graph.GetEdgeData(forward_e1).distance += fwd_edge_data2.distance;
|
||||
graph.GetEdgeData(reverse_e1).distance += rev_edge_data2.distance;
|
||||
if (has_node_penalty)
|
||||
{
|
||||
graph.GetEdgeData(forward_e1).distance +=
|
||||
speed_profile.traffic_signal_penalty;
|
||||
graph.GetEdgeData(reverse_e1).distance +=
|
||||
speed_profile.traffic_signal_penalty;
|
||||
}
|
||||
|
||||
// extend e1's to targets of e2's
|
||||
graph.SetTarget(forward_e1, node_w);
|
||||
graph.SetTarget(reverse_e1, node_u);
|
||||
|
||||
// remove e2's (if bidir, otherwise only one)
|
||||
graph.DeleteEdge(node_v, forward_e2);
|
||||
graph.DeleteEdge(node_v, reverse_e2);
|
||||
|
||||
// update any involved turn restrictions
|
||||
restriction_map.FixupStartingTurnRestriction(node_u, node_v, node_w);
|
||||
restriction_map.FixupArrivingTurnRestriction(node_u, node_v, node_w, graph);
|
||||
|
||||
restriction_map.FixupStartingTurnRestriction(node_w, node_v, node_u);
|
||||
restriction_map.FixupArrivingTurnRestriction(node_w, node_v, node_u, graph);
|
||||
|
||||
// store compressed geometry in container
|
||||
geometry_compressor.CompressEdge(
|
||||
forward_e1, forward_e2, node_v, node_w,
|
||||
forward_weight1 + (has_node_penalty ? speed_profile.traffic_signal_penalty : 0),
|
||||
forward_weight2);
|
||||
geometry_compressor.CompressEdge(
|
||||
reverse_e1, reverse_e2, node_v, node_u, reverse_weight1,
|
||||
reverse_weight2 + (has_node_penalty ? speed_profile.traffic_signal_penalty : 0));
|
||||
}
|
||||
}
|
||||
|
||||
PrintStatistics(original_number_of_nodes, original_number_of_edges, graph);
|
||||
}
|
||||
|
||||
void GraphCompressor::PrintStatistics(unsigned original_number_of_nodes,
|
||||
unsigned original_number_of_edges,
|
||||
const NodeBasedDynamicGraph& graph) const
|
||||
{
|
||||
|
||||
unsigned new_node_count = 0;
|
||||
unsigned new_edge_count = 0;
|
||||
|
||||
for (const auto i : osrm::irange(0u, graph.GetNumberOfNodes()))
|
||||
{
|
||||
if (graph.GetOutDegree(i) > 0)
|
||||
{
|
||||
++new_node_count;
|
||||
new_edge_count += (graph.EndEdges(i) - graph.BeginEdges(i));
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2014, Project OSRM contributors
|
||||
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 "import_edge.hpp"
|
||||
|
||||
#include "travel_mode.hpp"
|
||||
#include "../typedefs.h"
|
||||
|
||||
bool NodeBasedEdge::operator<(const NodeBasedEdge &other) const
|
||||
{
|
||||
if (source == other.source)
|
||||
{
|
||||
if (target == other.target)
|
||||
{
|
||||
if (weight == other.weight)
|
||||
{
|
||||
return forward && backward && ((!other.forward) || (!other.backward));
|
||||
}
|
||||
return weight < other.weight;
|
||||
}
|
||||
return target < other.target;
|
||||
}
|
||||
return source < other.source;
|
||||
}
|
||||
|
||||
NodeBasedEdge::NodeBasedEdge()
|
||||
: source(SPECIAL_NODEID), target(SPECIAL_NODEID), name_id(0), weight(0), forward(false),
|
||||
backward(false), roundabout(false),
|
||||
access_restricted(false), startpoint(true), is_split(false), travel_mode(false)
|
||||
{
|
||||
}
|
||||
|
||||
NodeBasedEdge::NodeBasedEdge(NodeID source,
|
||||
NodeID target,
|
||||
NodeID name_id,
|
||||
EdgeWeight weight,
|
||||
bool forward,
|
||||
bool backward,
|
||||
bool roundabout,
|
||||
bool access_restricted,
|
||||
bool startpoint,
|
||||
TravelMode travel_mode,
|
||||
bool is_split)
|
||||
: source(source), target(target), name_id(name_id), weight(weight), forward(forward),
|
||||
backward(backward), roundabout(roundabout),
|
||||
access_restricted(access_restricted), startpoint(startpoint), is_split(is_split), travel_mode(travel_mode)
|
||||
{
|
||||
}
|
||||
|
||||
bool EdgeBasedEdge::operator<(const EdgeBasedEdge &other) const
|
||||
{
|
||||
if (source == other.source)
|
||||
{
|
||||
if (target == other.target)
|
||||
{
|
||||
if (weight == other.weight)
|
||||
{
|
||||
return forward && backward && ((!other.forward) || (!other.backward));
|
||||
}
|
||||
return weight < other.weight;
|
||||
}
|
||||
return target < other.target;
|
||||
}
|
||||
return source < other.source;
|
||||
}
|
||||
|
||||
template <class EdgeT>
|
||||
EdgeBasedEdge::EdgeBasedEdge(const EdgeT &other)
|
||||
: source(other.source), target(other.target), edge_id(other.data.via),
|
||||
weight(other.data.distance), forward(other.data.forward), backward(other.data.backward)
|
||||
{
|
||||
}
|
||||
|
||||
/** Default constructor. target and weight are set to 0.*/
|
||||
EdgeBasedEdge::EdgeBasedEdge()
|
||||
: source(0), target(0), edge_id(0), weight(0), forward(false), backward(false)
|
||||
{
|
||||
}
|
||||
|
||||
EdgeBasedEdge::EdgeBasedEdge(const NodeID source,
|
||||
const NodeID target,
|
||||
const NodeID edge_id,
|
||||
const EdgeWeight weight,
|
||||
const bool forward,
|
||||
const bool backward)
|
||||
: source(source), target(target), edge_id(edge_id), weight(weight), forward(forward),
|
||||
backward(backward)
|
||||
{
|
||||
}
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
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 "raster_source.hpp"
|
||||
|
||||
#include "../util/simple_logger.hpp"
|
||||
#include "../util/timing_util.hpp"
|
||||
|
||||
#include <osrm/coordinate.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
RasterSource::RasterSource(RasterGrid _raster_data,
|
||||
std::size_t _width,
|
||||
std::size_t _height,
|
||||
int _xmin,
|
||||
int _xmax,
|
||||
int _ymin,
|
||||
int _ymax)
|
||||
: xstep(calcSize(_xmin, _xmax, _width)), ystep(calcSize(_ymin, _ymax, _height)),
|
||||
raster_data(std::move(_raster_data)), width(_width), height(_height), xmin(_xmin),
|
||||
xmax(_xmax), ymin(_ymin), ymax(_ymax)
|
||||
{
|
||||
BOOST_ASSERT(xstep != 0);
|
||||
BOOST_ASSERT(ystep != 0);
|
||||
}
|
||||
|
||||
float RasterSource::calcSize(int min, int max, std::size_t count) const
|
||||
{
|
||||
BOOST_ASSERT(count > 0);
|
||||
return (max - min) / (static_cast<float>(count) - 1);
|
||||
}
|
||||
|
||||
// Query raster source for nearest data point
|
||||
RasterDatum RasterSource::getRasterData(const int lon, const int lat) const
|
||||
{
|
||||
if (lon < xmin || lon > xmax || lat < ymin || lat > ymax)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const std::size_t xth = static_cast<std::size_t>(round((lon - xmin) / xstep));
|
||||
const std::size_t yth = static_cast<std::size_t>(round((ymax - lat) / ystep));
|
||||
|
||||
return {raster_data(xth, yth)};
|
||||
}
|
||||
|
||||
// Query raster source using bilinear interpolation
|
||||
RasterDatum RasterSource::getRasterInterpolate(const int lon, const int lat) const
|
||||
{
|
||||
if (lon < xmin || lon > xmax || lat < ymin || lat > ymax)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
const auto xthP = (lon - xmin) / xstep;
|
||||
const auto ythP = (ymax - lat) / ystep;
|
||||
|
||||
const std::size_t top = static_cast<std::size_t>(fmax(floor(ythP), 0));
|
||||
const std::size_t bottom = static_cast<std::size_t>(fmin(ceil(ythP), height - 1));
|
||||
const std::size_t left = static_cast<std::size_t>(fmax(floor(xthP), 0));
|
||||
const std::size_t right = static_cast<std::size_t>(fmin(ceil(xthP), width - 1));
|
||||
|
||||
// Calculate distances from corners for bilinear interpolation
|
||||
const float fromLeft = (lon - left * xstep + xmin) / xstep;
|
||||
const float fromTop = (ymax - top * ystep - lat) / ystep;
|
||||
const float fromRight = 1 - fromLeft;
|
||||
const float fromBottom = 1 - fromTop;
|
||||
|
||||
return {static_cast<std::int32_t>(raster_data(left, top) * (fromRight * fromBottom) +
|
||||
raster_data(right, top) * (fromLeft * fromBottom) +
|
||||
raster_data(left, bottom) * (fromRight * fromTop) +
|
||||
raster_data(right, bottom) * (fromLeft * fromTop))};
|
||||
}
|
||||
|
||||
// Load raster source into memory
|
||||
int SourceContainer::loadRasterSource(const std::string &path_string,
|
||||
double xmin,
|
||||
double xmax,
|
||||
double ymin,
|
||||
double ymax,
|
||||
std::size_t nrows,
|
||||
std::size_t ncols)
|
||||
{
|
||||
const auto _xmin = static_cast<int>(xmin * COORDINATE_PRECISION);
|
||||
const auto _xmax = static_cast<int>(xmax * COORDINATE_PRECISION);
|
||||
const auto _ymin = static_cast<int>(ymin * COORDINATE_PRECISION);
|
||||
const auto _ymax = static_cast<int>(ymax * COORDINATE_PRECISION);
|
||||
|
||||
const auto itr = LoadedSourcePaths.find(path_string);
|
||||
if (itr != LoadedSourcePaths.end())
|
||||
{
|
||||
SimpleLogger().Write() << "[source loader] Already loaded source '" << path_string
|
||||
<< "' at source_id " << itr->second;
|
||||
return itr->second;
|
||||
}
|
||||
|
||||
int source_id = static_cast<int>(LoadedSources.size());
|
||||
|
||||
SimpleLogger().Write() << "[source loader] Loading from " << path_string << " ... ";
|
||||
TIMER_START(loading_source);
|
||||
|
||||
boost::filesystem::path filepath(path_string);
|
||||
if (!boost::filesystem::exists(filepath))
|
||||
{
|
||||
throw osrm::exception("error reading: no such path");
|
||||
}
|
||||
|
||||
RasterGrid rasterData{filepath, ncols, nrows};
|
||||
|
||||
RasterSource source{std::move(rasterData), ncols, nrows, _xmin, _xmax, _ymin, _ymax};
|
||||
TIMER_STOP(loading_source);
|
||||
LoadedSourcePaths.emplace(path_string, source_id);
|
||||
LoadedSources.push_back(std::move(source));
|
||||
|
||||
SimpleLogger().Write() << "[source loader] ok, after " << TIMER_SEC(loading_source) << "s";
|
||||
|
||||
return source_id;
|
||||
}
|
||||
|
||||
// External function for looking up nearest data point from a specified source
|
||||
RasterDatum SourceContainer::getRasterDataFromSource(unsigned int source_id, int lon, int lat)
|
||||
{
|
||||
if (LoadedSources.size() < source_id + 1)
|
||||
{
|
||||
throw osrm::exception("error reading: no such loaded source");
|
||||
}
|
||||
|
||||
BOOST_ASSERT(lat < (90 * COORDINATE_PRECISION));
|
||||
BOOST_ASSERT(lat > (-90 * COORDINATE_PRECISION));
|
||||
BOOST_ASSERT(lon < (180 * COORDINATE_PRECISION));
|
||||
BOOST_ASSERT(lon > (-180 * COORDINATE_PRECISION));
|
||||
|
||||
const auto &found = LoadedSources[source_id];
|
||||
return found.getRasterData(lon, lat);
|
||||
}
|
||||
|
||||
// External function for looking up interpolated data from a specified source
|
||||
RasterDatum
|
||||
SourceContainer::getRasterInterpolateFromSource(unsigned int source_id, int lon, int lat)
|
||||
{
|
||||
if (LoadedSources.size() < source_id + 1)
|
||||
{
|
||||
throw osrm::exception("error reading: no such loaded source");
|
||||
}
|
||||
|
||||
BOOST_ASSERT(lat < (90 * COORDINATE_PRECISION));
|
||||
BOOST_ASSERT(lat > (-90 * COORDINATE_PRECISION));
|
||||
BOOST_ASSERT(lon < (180 * COORDINATE_PRECISION));
|
||||
BOOST_ASSERT(lon > (-180 * COORDINATE_PRECISION));
|
||||
|
||||
const auto &found = LoadedSources[source_id];
|
||||
return found.getRasterInterpolate(lon, lat);
|
||||
}
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
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 "restriction_map.hpp"
|
||||
|
||||
RestrictionMap::RestrictionMap(const std::vector<TurnRestriction> &restriction_list) : m_count(0)
|
||||
{
|
||||
// decompose restriction consisting of a start, via and end node into a
|
||||
// a pair of starting edge and a list of all end nodes
|
||||
for (auto &restriction : restriction_list)
|
||||
{
|
||||
// This downcasting is OK because when this is called, the node IDs have been
|
||||
// renumbered into internal values, which should be well under 2^32
|
||||
// This will be a problem if we have more than 2^32 actual restrictions
|
||||
BOOST_ASSERT(restriction.from.node < std::numeric_limits<NodeID>::max());
|
||||
BOOST_ASSERT(restriction.via.node < std::numeric_limits<NodeID>::max());
|
||||
m_restriction_start_nodes.insert(restriction.from.node);
|
||||
m_no_turn_via_node_set.insert(restriction.via.node);
|
||||
|
||||
// This explicit downcasting is also OK for the same reason.
|
||||
RestrictionSource restriction_source = {static_cast<NodeID>(restriction.from.node), static_cast<NodeID>(restriction.via.node)};
|
||||
|
||||
std::size_t index;
|
||||
auto restriction_iter = m_restriction_map.find(restriction_source);
|
||||
if (restriction_iter == m_restriction_map.end())
|
||||
{
|
||||
index = m_restriction_bucket_list.size();
|
||||
m_restriction_bucket_list.resize(index + 1);
|
||||
m_restriction_map.emplace(restriction_source, index);
|
||||
}
|
||||
else
|
||||
{
|
||||
index = restriction_iter->second;
|
||||
// Map already contains an is_only_*-restriction
|
||||
if (m_restriction_bucket_list.at(index).begin()->is_only)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (restriction.flags.is_only)
|
||||
{
|
||||
// We are going to insert an is_only_*-restriction. There can be only one.
|
||||
m_count -= m_restriction_bucket_list.at(index).size();
|
||||
m_restriction_bucket_list.at(index).clear();
|
||||
}
|
||||
}
|
||||
++m_count;
|
||||
BOOST_ASSERT(restriction.to.node < std::numeric_limits<NodeID>::max());
|
||||
m_restriction_bucket_list.at(index)
|
||||
.emplace_back(restriction.to.node, restriction.flags.is_only);
|
||||
}
|
||||
}
|
||||
|
||||
bool RestrictionMap::IsViaNode(const NodeID node) const
|
||||
{
|
||||
return m_no_turn_via_node_set.find(node) != m_no_turn_via_node_set.end();
|
||||
}
|
||||
|
||||
// Replaces start edge (v, w) with (u, w). Only start node changes.
|
||||
void RestrictionMap::FixupStartingTurnRestriction(const NodeID node_u,
|
||||
const NodeID node_v,
|
||||
const NodeID node_w)
|
||||
{
|
||||
BOOST_ASSERT(node_u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(node_v != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(node_w != SPECIAL_NODEID);
|
||||
|
||||
if (!IsSourceNode(node_v))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const auto restriction_iterator = m_restriction_map.find({node_v, node_w});
|
||||
if (restriction_iterator != m_restriction_map.end())
|
||||
{
|
||||
const unsigned index = restriction_iterator->second;
|
||||
// remove old restriction start (v,w)
|
||||
m_restriction_map.erase(restriction_iterator);
|
||||
m_restriction_start_nodes.emplace(node_u);
|
||||
// insert new restriction start (u,w) (pointing to index)
|
||||
RestrictionSource new_source = {node_u, node_w};
|
||||
m_restriction_map.emplace(new_source, index);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if edge (u, v) is the start of any turn restriction.
|
||||
// If so returns id of first target node.
|
||||
NodeID RestrictionMap::CheckForEmanatingIsOnlyTurn(const NodeID node_u, const NodeID node_v) const
|
||||
{
|
||||
BOOST_ASSERT(node_u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(node_v != SPECIAL_NODEID);
|
||||
|
||||
if (!IsSourceNode(node_u))
|
||||
{
|
||||
return SPECIAL_NODEID;
|
||||
}
|
||||
|
||||
const auto restriction_iter = m_restriction_map.find({node_u, node_v});
|
||||
if (restriction_iter != m_restriction_map.end())
|
||||
{
|
||||
const unsigned index = restriction_iter->second;
|
||||
const auto &bucket = m_restriction_bucket_list.at(index);
|
||||
for (const RestrictionTarget &restriction_target : bucket)
|
||||
{
|
||||
if (restriction_target.is_only)
|
||||
{
|
||||
return restriction_target.target_node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return SPECIAL_NODEID;
|
||||
}
|
||||
|
||||
// Checks if turn <u,v,w> is actually a turn restriction.
|
||||
bool RestrictionMap::CheckIfTurnIsRestricted(const NodeID node_u,
|
||||
const NodeID node_v,
|
||||
const NodeID node_w) const
|
||||
{
|
||||
BOOST_ASSERT(node_u != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(node_v != SPECIAL_NODEID);
|
||||
BOOST_ASSERT(node_w != SPECIAL_NODEID);
|
||||
|
||||
if (!IsSourceNode(node_u))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto restriction_iter = m_restriction_map.find({node_u, node_v});
|
||||
if (restriction_iter == m_restriction_map.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned index = restriction_iter->second;
|
||||
const auto &bucket = m_restriction_bucket_list.at(index);
|
||||
|
||||
for (const RestrictionTarget &restriction_target : bucket)
|
||||
{
|
||||
if (node_w == restriction_target.target_node && // target found
|
||||
!restriction_target.is_only) // and not an only_-restr.
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (node_w != restriction_target.target_node && // target not found
|
||||
restriction_target.is_only) // and is an only restriction
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// check of node is the start of any restriction
|
||||
bool RestrictionMap::IsSourceNode(const NodeID node) const
|
||||
{
|
||||
if (m_restriction_start_nodes.find(node) == m_restriction_start_nodes.end())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
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 "restriction_parser.hpp"
|
||||
#include "extraction_way.hpp"
|
||||
|
||||
#include "../data_structures/external_memory_node.hpp"
|
||||
#include "../util/lua_util.hpp"
|
||||
#include "../util/osrm_exception.hpp"
|
||||
#include "../util/simple_logger.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/algorithm/string/regex.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/ref.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
||||
#include <osmium/osm.hpp>
|
||||
#include <osmium/tags/regex_filter.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
namespace
|
||||
{
|
||||
int lua_error_callback(lua_State *lua_state)
|
||||
{
|
||||
std::string error_msg = lua_tostring(lua_state, -1);
|
||||
throw osrm::exception("ERROR occurred in profile script:\n" + error_msg);
|
||||
}
|
||||
}
|
||||
|
||||
RestrictionParser::RestrictionParser(lua_State *lua_state) : use_turn_restrictions(true)
|
||||
{
|
||||
ReadUseRestrictionsSetting(lua_state);
|
||||
|
||||
if (use_turn_restrictions)
|
||||
{
|
||||
ReadRestrictionExceptions(lua_state);
|
||||
}
|
||||
}
|
||||
|
||||
void RestrictionParser::ReadUseRestrictionsSetting(lua_State *lua_state)
|
||||
{
|
||||
if (0 == luaL_dostring(lua_state, "return use_turn_restrictions\n") &&
|
||||
lua_isboolean(lua_state, -1))
|
||||
{
|
||||
use_turn_restrictions = lua_toboolean(lua_state, -1);
|
||||
}
|
||||
|
||||
if (use_turn_restrictions)
|
||||
{
|
||||
SimpleLogger().Write() << "Using turn restrictions";
|
||||
}
|
||||
else
|
||||
{
|
||||
SimpleLogger().Write() << "Ignoring turn restrictions";
|
||||
}
|
||||
}
|
||||
|
||||
void RestrictionParser::ReadRestrictionExceptions(lua_State *lua_state)
|
||||
{
|
||||
if (lua_function_exists(lua_state, "get_exceptions"))
|
||||
{
|
||||
luabind::set_pcall_callback(&lua_error_callback);
|
||||
// get list of turn restriction exceptions
|
||||
luabind::call_function<void>(lua_state, "get_exceptions",
|
||||
boost::ref(restriction_exceptions));
|
||||
const unsigned exception_count = restriction_exceptions.size();
|
||||
SimpleLogger().Write() << "Found " << exception_count
|
||||
<< " exceptions to turn restrictions:";
|
||||
for (const std::string &str : restriction_exceptions)
|
||||
{
|
||||
SimpleLogger().Write() << " " << str;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
SimpleLogger().Write() << "Found no exceptions to turn restrictions";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to parse an relation as turn restriction. This can fail for a number of
|
||||
* reasons, this the return type is a boost::optional<T>.
|
||||
*
|
||||
* Some restrictions can also be ignored: See the ```get_exceptions``` function
|
||||
* in the corresponding profile.
|
||||
*/
|
||||
boost::optional<InputRestrictionContainer>
|
||||
RestrictionParser::TryParse(const osmium::Relation &relation) const
|
||||
{
|
||||
// return if turn restrictions should be ignored
|
||||
if (!use_turn_restrictions)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
osmium::tags::KeyPrefixFilter filter(false);
|
||||
filter.add(true, "restriction");
|
||||
|
||||
const osmium::TagList &tag_list = relation.tags();
|
||||
|
||||
osmium::tags::KeyPrefixFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end());
|
||||
osmium::tags::KeyPrefixFilter::iterator fi_end(filter, tag_list.end(), tag_list.end());
|
||||
|
||||
// if it's a restriction, continue;
|
||||
if (std::distance(fi_begin, fi_end) == 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// check if the restriction should be ignored
|
||||
const char *except = relation.get_value_by_key("except");
|
||||
if (except != nullptr && ShouldIgnoreRestriction(except))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
bool is_only_restriction = false;
|
||||
|
||||
for (; fi_begin != fi_end; ++fi_begin)
|
||||
{
|
||||
const std::string key(fi_begin->key());
|
||||
const std::string value(fi_begin->value());
|
||||
|
||||
if (value.find("only_") == 0)
|
||||
{
|
||||
is_only_restriction = true;
|
||||
}
|
||||
|
||||
// if the "restriction*" key is longer than 11 chars, it is a conditional exception (i.e.
|
||||
// "restriction:<transportation_type>")
|
||||
if (key.size() > 11)
|
||||
{
|
||||
const auto ex_suffix = [&](const std::string &exception)
|
||||
{
|
||||
return boost::algorithm::ends_with(key, exception);
|
||||
};
|
||||
bool is_actually_restricted =
|
||||
std::any_of(begin(restriction_exceptions), end(restriction_exceptions), ex_suffix);
|
||||
|
||||
if (!is_actually_restricted)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
InputRestrictionContainer restriction_container(is_only_restriction);
|
||||
|
||||
for (const auto &member : relation.members())
|
||||
{
|
||||
const char *role = member.role();
|
||||
if (strcmp("from", role) != 0 && strcmp("to", role) != 0 && strcmp("via", role) != 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (member.type())
|
||||
{
|
||||
case osmium::item_type::node:
|
||||
// Make sure nodes appear only in the role if a via node
|
||||
if (0 == strcmp("from", role) || 0 == strcmp("to", role))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
BOOST_ASSERT(0 == strcmp("via", role));
|
||||
|
||||
// set via node id
|
||||
restriction_container.restriction.via.node = member.ref();
|
||||
break;
|
||||
|
||||
case osmium::item_type::way:
|
||||
BOOST_ASSERT(0 == strcmp("from", role) || 0 == strcmp("to", role) ||
|
||||
0 == strcmp("via", role));
|
||||
if (0 == strcmp("from", role))
|
||||
{
|
||||
restriction_container.restriction.from.way = member.ref();
|
||||
}
|
||||
else if (0 == strcmp("to", role))
|
||||
{
|
||||
restriction_container.restriction.to.way = member.ref();
|
||||
}
|
||||
// else if (0 == strcmp("via", role))
|
||||
// {
|
||||
// not yet suppported
|
||||
// restriction_container.restriction.via.way = member.ref();
|
||||
// }
|
||||
break;
|
||||
case osmium::item_type::relation:
|
||||
// not yet supported, but who knows what the future holds...
|
||||
break;
|
||||
default:
|
||||
// shouldn't ever happen
|
||||
break;
|
||||
}
|
||||
}
|
||||
return boost::make_optional(std::move(restriction_container));
|
||||
}
|
||||
|
||||
bool RestrictionParser::ShouldIgnoreRestriction(const std::string &except_tag_string) const
|
||||
{
|
||||
// should this restriction be ignored? yes if there's an overlap between:
|
||||
// a) the list of modes in the except tag of the restriction
|
||||
// (except_tag_string), eg: except=bus;bicycle
|
||||
// b) the lua profile defines a hierachy of modes,
|
||||
// eg: [access, vehicle, bicycle]
|
||||
|
||||
if (except_tag_string.empty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Be warned, this is quadratic work here, but we assume that
|
||||
// only a few exceptions are actually defined.
|
||||
std::vector<std::string> exceptions;
|
||||
boost::algorithm::split_regex(exceptions, except_tag_string, boost::regex("[;][ ]*"));
|
||||
|
||||
return std::any_of(std::begin(exceptions), std::end(exceptions),
|
||||
[&](const std::string ¤t_string)
|
||||
{
|
||||
if (std::end(restriction_exceptions) !=
|
||||
std::find(std::begin(restriction_exceptions),
|
||||
std::end(restriction_exceptions), current_string))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
|
||||
Copyright (c) 2015, Project OSRM contributors
|
||||
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 "scripting_environment.hpp"
|
||||
|
||||
#include "extraction_helper_functions.hpp"
|
||||
#include "extraction_node.hpp"
|
||||
#include "extraction_way.hpp"
|
||||
#include "internal_extractor_edge.hpp"
|
||||
#include "../data_structures/external_memory_node.hpp"
|
||||
#include "../data_structures/raster_source.hpp"
|
||||
#include "../util/lua_util.hpp"
|
||||
#include "../util/osrm_exception.hpp"
|
||||
#include "../util/simple_logger.hpp"
|
||||
#include "../typedefs.h"
|
||||
|
||||
#include <luabind/tag_function.hpp>
|
||||
#include <luabind/operator.hpp>
|
||||
|
||||
#include <osmium/osm.hpp>
|
||||
|
||||
#include <sstream>
|
||||
namespace
|
||||
{
|
||||
// wrapper method as luabind doesn't automatically overload funcs w/ default parameters
|
||||
template <class T>
|
||||
auto get_value_by_key(T const &object, const char *key) -> decltype(object.get_value_by_key(key))
|
||||
{
|
||||
return object.get_value_by_key(key, "");
|
||||
}
|
||||
|
||||
int lua_error_callback(lua_State *L) // This is so I can use my own function as an
|
||||
// exception handler, pcall_log()
|
||||
{
|
||||
std::string error_msg = lua_tostring(L, -1);
|
||||
std::ostringstream error_stream;
|
||||
error_stream << error_msg;
|
||||
throw osrm::exception("ERROR occurred in profile script:\n" + error_stream.str());
|
||||
}
|
||||
}
|
||||
|
||||
ScriptingEnvironment::ScriptingEnvironment(const std::string &file_name) : file_name(file_name)
|
||||
{
|
||||
SimpleLogger().Write() << "Using script " << file_name;
|
||||
}
|
||||
|
||||
void ScriptingEnvironment::init_lua_state(lua_State *lua_state)
|
||||
{
|
||||
typedef double (osmium::Location::*location_member_ptr_type)() const;
|
||||
|
||||
luabind::open(lua_state);
|
||||
// open utility libraries string library;
|
||||
luaL_openlibs(lua_state);
|
||||
|
||||
luaAddScriptFolderToLoadPath(lua_state, file_name.c_str());
|
||||
|
||||
// Add our function to the state's global scope
|
||||
luabind::module(lua_state)[
|
||||
luabind::def("print", LUA_print<std::string>),
|
||||
luabind::def("durationIsValid", durationIsValid),
|
||||
luabind::def("parseDuration", parseDuration),
|
||||
luabind::class_<SourceContainer>("sources")
|
||||
.def(luabind::constructor<>())
|
||||
.def("load", &SourceContainer::loadRasterSource)
|
||||
.def("query", &SourceContainer::getRasterDataFromSource)
|
||||
.def("interpolate", &SourceContainer::getRasterInterpolateFromSource),
|
||||
luabind::class_<const float>("constants")
|
||||
.enum_("enums")[luabind::value("precision", COORDINATE_PRECISION)],
|
||||
|
||||
luabind::class_<std::vector<std::string>>("vector")
|
||||
.def("Add", static_cast<void (std::vector<std::string>::*)(const std::string &)>(
|
||||
&std::vector<std::string>::push_back)),
|
||||
|
||||
luabind::class_<osmium::Location>("Location")
|
||||
.def<location_member_ptr_type>("lat", &osmium::Location::lat)
|
||||
.def<location_member_ptr_type>("lon", &osmium::Location::lon),
|
||||
|
||||
luabind::class_<osmium::Node>("Node")
|
||||
// .def<node_member_ptr_type>("tags", &osmium::Node::tags)
|
||||
.def("location", &osmium::Node::location)
|
||||
.def("get_value_by_key", &osmium::Node::get_value_by_key)
|
||||
.def("get_value_by_key", &get_value_by_key<osmium::Node>)
|
||||
.def("id", &osmium::Node::id),
|
||||
|
||||
luabind::class_<ExtractionNode>("ResultNode")
|
||||
.def_readwrite("traffic_lights", &ExtractionNode::traffic_lights)
|
||||
.def_readwrite("barrier", &ExtractionNode::barrier),
|
||||
|
||||
luabind::class_<ExtractionWay>("ResultWay")
|
||||
// .def(luabind::constructor<>())
|
||||
.def_readwrite("forward_speed", &ExtractionWay::forward_speed)
|
||||
.def_readwrite("backward_speed", &ExtractionWay::backward_speed)
|
||||
.def_readwrite("name", &ExtractionWay::name)
|
||||
.def_readwrite("roundabout", &ExtractionWay::roundabout)
|
||||
.def_readwrite("is_access_restricted", &ExtractionWay::is_access_restricted)
|
||||
.def_readwrite("is_startpoint", &ExtractionWay::is_startpoint)
|
||||
.def_readwrite("duration", &ExtractionWay::duration)
|
||||
.property("forward_mode", &ExtractionWay::get_forward_mode,
|
||||
&ExtractionWay::set_forward_mode)
|
||||
.property("backward_mode", &ExtractionWay::get_backward_mode,
|
||||
&ExtractionWay::set_backward_mode)
|
||||
.enum_("constants")[
|
||||
luabind::value("notSure", 0),
|
||||
luabind::value("oneway", 1),
|
||||
luabind::value("bidirectional", 2),
|
||||
luabind::value("opposite", 3)
|
||||
],
|
||||
luabind::class_<osmium::Way>("Way")
|
||||
.def("get_value_by_key", &osmium::Way::get_value_by_key)
|
||||
.def("get_value_by_key", &get_value_by_key<osmium::Way>)
|
||||
.def("id", &osmium::Way::id),
|
||||
luabind::class_<InternalExtractorEdge>("EdgeSource")
|
||||
.property("source_coordinate", &InternalExtractorEdge::source_coordinate)
|
||||
.property("weight_data", &InternalExtractorEdge::weight_data),
|
||||
luabind::class_<InternalExtractorEdge::WeightData>("WeightData")
|
||||
.def_readwrite("speed", &InternalExtractorEdge::WeightData::speed),
|
||||
luabind::class_<ExternalMemoryNode>("EdgeTarget")
|
||||
.property("lat", &ExternalMemoryNode::lat)
|
||||
.property("lon", &ExternalMemoryNode::lon),
|
||||
luabind::class_<FixedPointCoordinate>("Coordinate")
|
||||
.property("lat", &FixedPointCoordinate::lat)
|
||||
.property("lon", &FixedPointCoordinate::lon),
|
||||
luabind::class_<RasterDatum>("RasterDatum")
|
||||
.property("datum", &RasterDatum::datum)
|
||||
.def("invalid_data", &RasterDatum::get_invalid)
|
||||
];
|
||||
|
||||
if (0 != luaL_dofile(lua_state, file_name.c_str()))
|
||||
{
|
||||
luabind::object error_msg(luabind::from_stack(lua_state, -1));
|
||||
std::ostringstream error_stream;
|
||||
error_stream << error_msg;
|
||||
throw osrm::exception("ERROR occurred in profile script:\n" + error_stream.str());
|
||||
}
|
||||
}
|
||||
|
||||
lua_State *ScriptingEnvironment::get_lua_state()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(init_mutex);
|
||||
bool initialized = false;
|
||||
auto &ref = script_contexts.local(initialized);
|
||||
if (!initialized)
|
||||
{
|
||||
std::shared_ptr<lua_State> state(luaL_newstate(), lua_close);
|
||||
ref = state;
|
||||
init_lua_state(ref.get());
|
||||
}
|
||||
luabind::set_pcall_callback(&lua_error_callback);
|
||||
|
||||
return ref.get();
|
||||
}
|
||||
Reference in New Issue
Block a user