#include "extractor/compressed_edge_container.hpp" #include "util/simple_logger.hpp" #include #include #include #include #include #include namespace osrm { namespace extractor { 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::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 ¤t_vector = elem; const unsigned unpacked_size = current_vector.size(); BOOST_ASSERT(std::numeric_limits::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 ¤t_vector = elem; const unsigned unpacked_size = current_vector.size(); control_sum += unpacked_size; BOOST_ASSERT(std::numeric_limits::max() != unpacked_size); for (const auto ¤t_node : current_vector) { geometry_out_stream.write((char *)&(current_node), sizeof(CompressedEdge)); } } BOOST_ASSERT(control_sum == prefix_sum_of_list_indices); } // Adds info for a compressed edge to the container. edge_id_2 // has been removed from the graph, so we have to save These edges/nodes // have already been trimmed from the graph, this function just stores // the original data for unpacking later. // // edge_id_1 edge_id_2 // ----------> via_node_id -----------> target_node_id // weight_1 weight_2 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: // second; BOOST_ASSERT(edge_bucket_id1 == GetPositionForID(edge_id_1)); BOOST_ASSERT(edge_bucket_id1 < m_compressed_geometries.size()); std::vector &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(CompressedEdge{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 &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(CompressedEdge{target_node_id, weight2}); } } void CompressedEdgeContainer::AddUncompressedEdge(const EdgeID edge_id, const NodeID target_node_id, const EdgeWeight weight) { // remove super-trivial geometries BOOST_ASSERT(SPECIAL_EDGEID != edge_id); BOOST_ASSERT(SPECIAL_NODEID != target_node_id); BOOST_ASSERT(INVALID_EDGE_WEIGHT != weight); // Add via node id. List is created if it does not exist if (!HasEntryForID(edge_id)) { // 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] = 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); BOOST_ASSERT(iter != m_edge_id_to_list_index_map.end()); const unsigned edge_bucket_id = iter->second; BOOST_ASSERT(edge_bucket_id == GetPositionForID(edge_id)); BOOST_ASSERT(edge_bucket_id < m_compressed_geometries.size()); std::vector &edge_bucket_list = m_compressed_geometries[edge_bucket_id]; // note we don't save the start coordinate: it is implicitly given by edge_id // weight is the distance to the (currently) last coordinate in the bucket // Don't re-add this if it's already in there. if (edge_bucket_list.empty()) { edge_bucket_list.emplace_back(CompressedEdge{target_node_id, weight}); } } 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 ¤t_vector : m_compressed_geometries) { compressed_geometries += current_vector.size(); longest_chain_length = std::max(longest_chain_length, (uint64_t)current_vector.size()); } util::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); } // Since all edges are technically in the compressed geometry container, // regardless of whether a compressed edge actually contains multiple // original segments, we use 'Trivial' here to describe compressed edges // that only contain one original segment bool CompressedEdgeContainer::IsTrivial(const EdgeID edge_id) const { const auto &bucket = GetBucketReference(edge_id); return bucket.size() == 1; } NodeID CompressedEdgeContainer::GetFirstEdgeTargetID(const EdgeID edge_id) const { const auto &bucket = GetBucketReference(edge_id); BOOST_ASSERT(bucket.size() >= 1); return bucket.front().node_id; } NodeID CompressedEdgeContainer::GetLastEdgeTargetID(const EdgeID edge_id) const { const auto &bucket = GetBucketReference(edge_id); BOOST_ASSERT(bucket.size() >= 1); return bucket.back().node_id; } NodeID CompressedEdgeContainer::GetLastEdgeSourceID(const EdgeID edge_id) const { const auto &bucket = GetBucketReference(edge_id); BOOST_ASSERT(bucket.size() >= 2); return bucket[bucket.size() - 2].node_id; } } }