/* Copyright (c) 2013, 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 "GeometryCompressor.h" #include "../Util/SimpleLogger.h" #include #include #include int current_free_list_maximum = 0; int UniqueNumber () { return ++current_free_list_maximum; } GeometryCompressor::GeometryCompressor() { m_free_list.resize(100); IncreaseFreeList(); } void GeometryCompressor::IncreaseFreeList() { m_compressed_geometries.resize(m_compressed_geometries.size() + 100); for(unsigned i = 100; i > 0; --i) { m_free_list.push_back(current_free_list_maximum); ++current_free_list_maximum; } } bool GeometryCompressor::HasEntryForID(const EdgeID edge_id) { return (m_edge_id_to_list_index_map.find(edge_id) != m_edge_id_to_list_index_map.end()); } unsigned GeometryCompressor::GetPositionForID(const EdgeID edge_id) { boost::unordered_map::const_iterator map_iterator; map_iterator = m_edge_id_to_list_index_map.find(edge_id); BOOST_ASSERT( map_iterator != m_edge_id_to_list_index_map.end() ); return map_iterator->second; } void GeometryCompressor::AddNodeIDToCompressedEdge( const EdgeID edge_id, const NodeID node_id ) { unsigned index = GetPositionForID(edge_id); BOOST_ASSERT( index < m_compressed_geometries.size() ); m_compressed_geometries[index].push_back( node_id ); } void GeometryCompressor::SerializeInternalVector( const std::string & path ) const { std::ofstream geometry_out_stream( path.c_str(), std::ios::binary ); const unsigned compressed_edge_count = m_compressed_geometries.size()+1; BOOST_ASSERT( UINT_MAX != compressed_edge_count ); geometry_out_stream.write( (char*)&compressed_edge_count, sizeof(unsigned) ); // write indices array unsigned prefix_sum_of_list_indices = 0; for(unsigned i = 0; i < m_compressed_geometries.size(); ++i ) { const std::vector & current_vector = m_compressed_geometries[i]; const unsigned unpacked_size = current_vector.size(); BOOST_ASSERT( UINT_MAX != unpacked_size ); geometry_out_stream.write( (char*)&prefix_sum_of_list_indices, sizeof(unsigned) ); prefix_sum_of_list_indices += unpacked_size; } // write sentinel element geometry_out_stream.write( (char*)&prefix_sum_of_list_indices, sizeof(unsigned) ); // write compressed geometries for(unsigned i = 0; i < m_compressed_geometries.size(); ++i ) { const std::vector & current_vector = m_compressed_geometries[i]; const unsigned unpacked_size = current_vector.size(); BOOST_ASSERT( UINT_MAX != unpacked_size ); for(unsigned j = 0; j < unpacked_size; ++j) { geometry_out_stream.write( (char*)&(current_vector[j]), sizeof(unsigned) ); } } // all done, let's close the resource geometry_out_stream.close(); } void GeometryCompressor::CompressEdge( const EdgeID surviving_edge_id, const EdgeID removed_edge_id, const NodeID via_node_id ) { BOOST_ASSERT( UINT_MAX != surviving_edge_id ); BOOST_ASSERT( UINT_MAX != removed_edge_id ); BOOST_ASSERT( UINT_MAX != via_node_id ); // append list of removed edge_id plus via node to surviving edge id: // & compressed_id_list = m_compressed_geometries[surving_list_id]; compressed_id_list.push_back(via_node_id); BOOST_ASSERT( 0 < compressed_id_list.size() ); // Find any existing list for removed_edge_id typename boost::unordered_map::const_iterator map_iterator; map_iterator = m_edge_id_to_list_index_map.find(removed_edge_id); if( m_edge_id_to_list_index_map.end() != map_iterator ) { const unsigned index = map_iterator->second; BOOST_ASSERT( index < m_compressed_geometries.size() ); // found an existing list, append it to the list of surviving_edge_id compressed_id_list.insert( compressed_id_list.end(), m_compressed_geometries[index].begin(), m_compressed_geometries[index].end() ); //remove the list of removed_edge_id m_edge_id_to_list_index_map.erase(map_iterator); BOOST_ASSERT( m_edge_id_to_list_index_map.end() == m_edge_id_to_list_index_map.find(removed_edge_id) ); m_compressed_geometries[index].clear(); BOOST_ASSERT( 0 == m_compressed_geometries[index].size() ); m_free_list.push_back(index); BOOST_ASSERT( index == m_free_list.back() ); } } void GeometryCompressor::PrintStatistics() const { unsigned removed_edge_count = 0; const unsigned surviving_edge_count = m_compressed_geometries.size()-m_free_list.size(); BOOST_ASSERT( m_compressed_geometries.size() + m_free_list.size() > 0 ); unsigned long longest_chain_length = 0; BOOST_FOREACH(const std::vector & current_vector, m_compressed_geometries) { removed_edge_count += current_vector.size(); longest_chain_length = std::max(longest_chain_length, current_vector.size()); } BOOST_ASSERT(0 == surviving_edge_count % 2); SimpleLogger().Write() << "surviving edges: " << surviving_edge_count << ", compressed edges: " << removed_edge_count << ", longest chain length: " << longest_chain_length << ", comp ratio: " << ((float)surviving_edge_count/std::max(removed_edge_count, 1u) ) << ", avg: chain length: " << (float)removed_edge_count/std::max(1u, surviving_edge_count); SimpleLogger().Write() << "No bytes: " << 4*surviving_edge_count + removed_edge_count*4; }