├── .gitignore ├── CMakeLists.txt ├── Makefile ├── README.rst ├── cmake ├── modules │ └── Findosmpbf.cmake └── osmpbfreaderConfig.cmake.in ├── example_counter.cc ├── example_routing.cc └── osmpbfreader.h /.gitignore: -------------------------------------------------------------------------------- 1 | example_counter 2 | example_routing 3 | *~ 4 | *.o 5 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8 FATAL_ERROR) 2 | project(osmpbfreader LANGUAGES CXX) 3 | 4 | include(GNUInstallDirs) 5 | set(PACKAGE_NAME osmpbfreader) 6 | set(INSTALL_CONFIGDIR ${CMAKE_INSTALL_LIBDIR}/${PACKAGE_NAME}/cmake) 7 | 8 | set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${CMAKE_SOURCE_DIR}/cmake/modules") 9 | 10 | 11 | # Library target 12 | find_package(osmpbf REQUIRED) 13 | find_package(Protobuf REQUIRED) 14 | add_library(osmpbfreader INTERFACE) 15 | target_include_directories(osmpbfreader 16 | INTERFACE 17 | ${osmpbf_INCLUDE_DIRS} 18 | ${Protobuf_INCLUDE_DIRS} 19 | $ 20 | $ 21 | ) 22 | target_compile_features(osmpbfreader INTERFACE cxx_std_11) 23 | target_link_libraries(osmpbfreader INTERFACE 24 | ${Protobuf_LIBRARIES} 25 | z 26 | ${osmpbf_LIBRARIES} 27 | ) 28 | install( 29 | TARGETS osmpbfreader 30 | EXPORT install_targets 31 | ) 32 | 33 | 34 | # Examples 35 | add_executable(example_counter example_counter.cc) 36 | target_link_libraries(example_counter PRIVATE osmpbfreader) 37 | set_target_properties(example_counter PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 38 | 39 | add_executable(example_routing example_routing.cc) 40 | target_link_libraries(example_routing PRIVATE osmpbfreader) 41 | set_target_properties(example_routing PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 42 | 43 | 44 | # Install headers 45 | install(FILES 46 | "${PROJECT_SOURCE_DIR}/osmpbfreader.h" 47 | DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) 48 | 49 | 50 | # Install targets file 51 | install(EXPORT install_targets 52 | FILE 53 | ${PACKAGE_NAME}Targets.cmake 54 | NAMESPACE 55 | ${PACKAGE_NAME}:: 56 | DESTINATION 57 | ${INSTALL_CONFIGDIR} 58 | ) 59 | 60 | 61 | # Install osmpbfreaderConfig.cmake 62 | include(CMakePackageConfigHelpers) 63 | configure_package_config_file( 64 | ${CMAKE_CURRENT_SOURCE_DIR}/cmake/${PACKAGE_NAME}Config.cmake.in 65 | ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}Config.cmake 66 | INSTALL_DESTINATION ${INSTALL_CONFIGDIR} 67 | ) 68 | install(FILES 69 | ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}Config.cmake 70 | DESTINATION ${INSTALL_CONFIGDIR} 71 | ) 72 | 73 | 74 | # Install find modules 75 | install(DIRECTORY cmake/modules/ DESTINATION ${INSTALL_CONFIGDIR}/modules) 76 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Thanks for writing this makefile Waitman Gobble 2 | CXX = g++ 3 | 4 | CXXFLAGS = -O3 -std=c++0x -Wall -Wextra 5 | 6 | LDFLAGS = -lprotobuf-lite -losmpbf -lz 7 | 8 | PROGRAMS = \ 9 | example_routing \ 10 | example_counter 11 | 12 | all: $(PROGRAMS) 13 | 14 | example_routing: example_routing.cc osmpbfreader.h 15 | $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) 16 | 17 | example_counter: example_counter.cc osmpbfreader.h 18 | $(CXX) $(CXXFLAGS) -o $@ $< $(LDFLAGS) 19 | 20 | clean: 21 | rm -f *.o core $(PROGRAMS) 22 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | libosmpbfreader 2 | =============== 3 | 4 | A simple C++ library to read OpenStreetMap binary files. 5 | 6 | It is released under the BSD licence (to be precise, the 3-clause license aka "New BSD License" aka "Modified BSD License"). 7 | 8 | Goal 9 | **** 10 | 11 | We hope that this library will help people to parse osm.pbf files in C++. 12 | This library only provides a scaffold to read the data. It does no transformation. 13 | 14 | We like a lot http://dev.omniscale.net/imposm.parser/ but wanted the same thing in C++. 15 | 16 | Compared to https://github.com/joto/osmium libosmpbfreader is way less ambitious. Where Osmium 17 | provides a whole framework for parsing PBF files, we only provide a scaffold for reading those 18 | files. Our main focus was to provide a library that can be used in 10 minutes. 19 | 20 | Install 21 | ******* 22 | 23 | The only dependency is libosmpbf. On debian/ubuntu just run:: 24 | 25 | sudo apt-get install libosmpbf-dev 26 | 27 | The library is a single header. No need to install or build anything. 28 | 29 | There is a Makefile provides that compiles the examples. 30 | 31 | Using it 32 | ******** 33 | 34 | The easiest is probably to look at the examples. 35 | 36 | As it is a header only library, juste include the header file:: 37 | 38 | #include "osmpbfreader.h" 39 | using namespace osmpbfreader; 40 | 41 | To use the library you need to implement a struct having the following signature:: 42 | 43 | struct Visitor { 44 | void node_callback(uint64_t osmid, double lon, double lat, const Tags &tags){} 45 | void way_callback(uint64_t osmid, const Tags &tags, const std::vector &refs){} 46 | void relation_callback(uint64_t osmid, const Tags &tags, const References &refs){} 47 | }; 48 | 49 | Tags and References are just typedefs:: 50 | 51 | typedef std::map Tags; 52 | typedef std::vector > References; 53 | 54 | The functions are called every time a node, a way or a relation is encountered while reading the file. 55 | 56 | *Warning:* Don't expect the elements to be read in any specific order. For example the nodes of a way might not be parsed already. 57 | 58 | Call the main function with the visitor you created:: 59 | 60 | Visitor v; 61 | read_osm_pbf("your_file.osm.pbf", v); 62 | 63 | Performances 64 | ************ 65 | 66 | Just reading (and doing nothing with the data) a europe extract (8Gb) requires 10 minutes on recent desktop computer. 67 | 68 | You must be very careful about the memory consumption if you plan to keep the nodes and ways on large files. 69 | Roughly count on 1Gb memory usage for an 100Mb .osm.pbf file. 70 | If you want to parse large files (like a whole continent or the planet), consider storing them on disk, e.g. using 71 | http://fallabs.com/kyotocabinet/ 72 | 73 | Future work 74 | *********** 75 | 76 | It will depend on your feedback ;) 77 | 78 | We might add some concurrency to speed up the parsing. However it will require more dependencies and will 79 | yield some synchronization problems in the visitor. Is it worth it? 80 | -------------------------------------------------------------------------------- /cmake/modules/Findosmpbf.cmake: -------------------------------------------------------------------------------- 1 | find_path(osmpbf_INCLUDE_DIRS 2 | NAMES osmpbf/osmpbf.h 3 | ) 4 | mark_as_advanced(osmpbf_INCLUDE_DIRS) 5 | 6 | find_library(osmpbf_LIBRARIES 7 | NAMES osmpbf 8 | ) 9 | mark_as_advanced(osmpbf_LIBRARIES) 10 | 11 | 12 | if(NOT osmpbf_INCLUDE_DIRS) 13 | message(STATUS "Could NOT find osmpbf/osmpbf.h") 14 | endif() 15 | if(NOT osmpbf_LIBRARIES) 16 | message(STATUS "Could NOT find osmpbf library") 17 | endif() 18 | 19 | include(FindPackageHandleStandardArgs) 20 | find_package_handle_standard_args(osmpbf DEFAULT_MSG osmpbf_INCLUDE_DIRS osmpbf_LIBRARIES) 21 | -------------------------------------------------------------------------------- /cmake/osmpbfreaderConfig.cmake.in: -------------------------------------------------------------------------------- 1 | get_filename_component(osmpbfreader_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) 2 | 3 | if(NOT TARGET osmpbfreader::osmpbfreader) 4 | set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH};${osmpbfreader_CMAKE_DIR}/modules") 5 | find_package(osmpbf REQUIRED) 6 | find_package(Protobuf REQUIRED) 7 | include("${osmpbfreader_CMAKE_DIR}/osmpbfreaderTargets.cmake") 8 | endif() 9 | -------------------------------------------------------------------------------- /example_counter.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2001-2022, Hove 3 | This is an example file, do whatever you want with it! (for example if you are in Paris, invite us for a beer) 4 | 5 | This shows the simplest way to use the osm.pbf reader. It just counts the number of objects in the file. 6 | 7 | To build this file : 8 | g++ -O2 -o counter example_counter.cc -losmpbf -lprotobuf 9 | 10 | To run it: 11 | ./counter path_to_your_data.osm.pbf 12 | */ 13 | 14 | #include "osmpbfreader.h" 15 | 16 | using namespace osmpbfreader; 17 | 18 | // We need to define a visitor with three methods that will be called while the file is read 19 | struct Counter { 20 | // Three integers count how many times each object type occurs 21 | int nodes; 22 | int ways; 23 | int relations; 24 | 25 | Counter() : nodes(0), ways(0), relations(0) {} 26 | 27 | // This method is called every time a Node is read 28 | void node_callback(uint64_t /*osmid*/, double /*lon*/, double /*lat*/, const Tags &/*tags*/){ 29 | ++nodes; 30 | } 31 | 32 | // This method is called every time a Way is read 33 | // refs is a vector that contains the reference to the nodes that compose the way 34 | void way_callback(uint64_t /*osmid*/, const Tags &/*tags*/, const std::vector &/*refs*/){ 35 | ++ways; 36 | } 37 | 38 | // This method is called every time a Relation is read 39 | // refs is a vector of pair corresponding of the relation type (Node, Way, Relation) and the reference to the object 40 | void relation_callback(uint64_t /*osmid*/, const Tags &/*tags*/, const References & /*refs*/){ 41 | ++relations; 42 | } 43 | }; 44 | 45 | int main(int argc, char** argv) { 46 | if(argc != 2) { 47 | std::cout << "Usage: " << argv[0] << " file_to_read.osm.pbf" << std::endl; 48 | return 1; 49 | } 50 | 51 | // Let's read that file ! 52 | Counter counter; 53 | read_osm_pbf(argv[1], counter); 54 | std::cout << "We read " << counter.nodes << " nodes, " << counter.ways << " ways and " << counter.relations << " relations" << std::endl; 55 | return 0; 56 | } 57 | -------------------------------------------------------------------------------- /example_routing.cc: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2001-2022, Hove 3 | This is an example file, do whatever you want with it! (for example if you are in Paris, invite us for a beer) 4 | 5 | A slightly more advanced example : we want to use OSM data to extract a graph that represents the road network 6 | 7 | Build the example with (we're to lazy to make it compatible with older standards): 8 | g++ example_routing.cc -O2 -losmpbf -lprotobuf -std=c++0x -o routing 9 | To run it: 10 | ./routing path_to_your_data.osm.pbf 11 | */ 12 | 13 | #include 14 | #include "osmpbfreader.h" 15 | using namespace osmpbfreader; 16 | 17 | // We keep every node and the how many times it is used in order to detect crossings 18 | struct Node { 19 | public: 20 | Node(double lon = 0, double lat = 0) : uses(0), lon_m(lon), lat_m(lat){} 21 | 22 | int32_t uses; 23 | double lon_m; 24 | double lat_m; 25 | }; 26 | 27 | struct Routing { 28 | // Map that stores all the nodes read 29 | std::unordered_map nodes; 30 | 31 | // Stores all the nodes of all the ways that are part of the road network 32 | std::vector< std::vector > ways; 33 | 34 | // This method is called every time a Node is read 35 | void node_callback(uint64_t osmid, double lon, double lat, const Tags &/*tags*/){ 36 | this->nodes[osmid] = Node(lon, lat); 37 | } 38 | 39 | // This method is called every time a Way is read 40 | void way_callback(uint64_t /*osmid*/, const Tags &tags, const std::vector &refs){ 41 | // If the way is part of the road network we keep it 42 | // There are other tags that correspond to the street network, however for simplicity, we don't manage them 43 | // Homework: read more properties like oneways, bicycle lanes… 44 | if(tags.find("highway") != tags.end()){ 45 | ways.push_back(refs); 46 | } 47 | } 48 | 49 | // Once all the ways and nodes are read, we count how many times a node is used to detect intersections 50 | void count_nodes_uses() { 51 | for(std::vector refs : ways){ 52 | for(uint64_t ref : refs){ 53 | nodes.at(ref).uses++; 54 | } 55 | // make sure that the last node is considered as an extremity 56 | nodes.at(refs.back()).uses++; 57 | } 58 | } 59 | 60 | // Returns the source and target node of the edges 61 | std::vector< std::pair > edges(){ 62 | std::vector< std::pair > result; 63 | 64 | for(std::vector refs : ways){ 65 | if(refs.size() > 0){ 66 | uint64_t source = refs[0]; 67 | for(size_t i = 1; i < refs.size(); ++i){ 68 | uint64_t current_ref = refs[i]; 69 | // If a node is used more than once, it is an intersection, hence it's a node of the road network graph 70 | if(nodes.at(current_ref).uses > 1){ 71 | // Homework: measure the length of the edge 72 | uint64_t target = current_ref; 73 | result.push_back(std::make_pair(source, target)); 74 | source = target; 75 | } 76 | } 77 | } 78 | } 79 | return result; 80 | } 81 | 82 | // We don't care about relations 83 | void relation_callback(uint64_t /*osmid*/, const Tags &/*tags*/, const References & /*refs*/){} 84 | }; 85 | 86 | int main(int argc, char** argv) { 87 | if(argc != 2) { 88 | std::cout << "Usage: " << argv[0] << " file_to_read.osm.pbf" << std::endl; 89 | return 1; 90 | } 91 | 92 | // Let's read that file ! 93 | Routing routing; 94 | read_osm_pbf(argv[1], routing); 95 | std::cout << "We read " << routing.nodes.size() << " nodes and " << routing.ways.size() << " ways" << std::endl; 96 | routing.count_nodes_uses(); 97 | std::cout << "The routing graph has " << routing.edges().size() << " edges" << std::endl; 98 | return 0; 99 | } 100 | 101 | -------------------------------------------------------------------------------- /osmpbfreader.h: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2001-2022, Hove 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without 6 | modification, are permitted provided that the following conditions are met: 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | * Neither the name of the Hove nor the 13 | names of its contributors may be used to endorse or promote products 14 | derived from this software without specific prior written permission. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL HOVE BE LIABLE FOR ANY 20 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 23 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | */ 27 | #pragma once 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | 36 | // this describes the low-level blob storage 37 | #include 38 | // this describes the high-level OSM objects 39 | #include 40 | // the maximum size of a blob header in bytes 41 | const int max_blob_header_size = 64 * 1024; // 64 kB 42 | // the maximum size of an uncompressed blob in bytes 43 | const int max_uncompressed_blob_size = 32 * 1024 * 1024; // 32 MB 44 | // resolution for longitude/latitude used for conversion 45 | // between representation as double and as int 46 | const int lonlat_resolution = 1000 * 1000 * 1000; 47 | 48 | namespace osmpbfreader { 49 | 50 | // Represents the key/values of an object 51 | typedef std::map Tags; 52 | 53 | // References of a relation 54 | struct Reference { 55 | OSMPBF::Relation::MemberType member_type; // type de la relation 56 | uint64_t member_id; // OSMID 57 | std::string role; // le role 58 | 59 | Reference() {} 60 | Reference(OSMPBF::Relation::MemberType member_type, uint64_t member_id, std::string role) : 61 | member_type(member_type), member_id(member_id), role(role) 62 | {} 63 | }; 64 | 65 | typedef std::vector References; 66 | 67 | // Main function 68 | template 69 | void read_osm_pbf(const std::string & filename, Visitor & visitor); 70 | 71 | struct warn { 72 | warn() {std::cout << "\033[33m[WARN] ";} 73 | templatewarn & operator<<(const T & t){ std::cout << t; return *this;} 74 | ~warn() {std::cout << "\033[0m" << std::endl;} 75 | }; 76 | 77 | struct info { 78 | info() {std::cout << "\033[32m[INFO] ";} 79 | templateinfo & operator<<(const T & t){ std::cout << t; return *this;} 80 | ~info() {std::cout << "\033[0m" << std::endl;} 81 | }; 82 | 83 | struct fatal { 84 | fatal() {std::cout << "\033[31m[FATAL] ";} 85 | templatefatal & operator<<(const T & t){ std::cout << t; return *this;} 86 | ~fatal() {std::cout << "\033[0m" << std::endl; exit(1);} 87 | }; 88 | 89 | 90 | 91 | template 92 | Tags get_tags(const T& object, const OSMPBF::PrimitiveBlock &primblock){ 93 | Tags result; 94 | for(int i = 0; i < object.keys_size(); ++i){ 95 | uint64_t key = object.keys(i); 96 | uint64_t val = object.vals(i); 97 | std::string key_string = primblock.stringtable().s(key); 98 | std::string val_string = primblock.stringtable().s(val); 99 | result[key_string] = val_string; 100 | } 101 | return result; 102 | } 103 | 104 | template 105 | struct Parser { 106 | 107 | void parse(){ 108 | while(!this->file.eof() && !finished) { 109 | OSMPBF::BlobHeader header = this->read_header(); 110 | if(!this->finished){ 111 | int32_t sz = this->read_blob(header); 112 | if(header.type() == "OSMData") { 113 | this->parse_primitiveblock(sz); 114 | } 115 | else if(header.type() == "OSMHeader"){ 116 | } 117 | else { 118 | warn() << " unknown blob type: " << header.type(); 119 | } 120 | } 121 | } 122 | } 123 | 124 | Parser(const std::string & filename, Visitor & visitor) 125 | : visitor(visitor), file(filename.c_str(), std::ios::binary ), finished(false) 126 | { 127 | if(!file.is_open()) 128 | fatal() << "Unable to open the file " << filename; 129 | buffer = new char[max_uncompressed_blob_size]; 130 | unpack_buffer = new char[max_uncompressed_blob_size]; 131 | info() << "Reading the file " << filename; 132 | } 133 | 134 | ~Parser(){ 135 | delete[] buffer; 136 | delete[] unpack_buffer; 137 | } 138 | 139 | private: 140 | Visitor & visitor; 141 | std::ifstream file; 142 | char* buffer; 143 | char* unpack_buffer; 144 | bool finished; 145 | 146 | OSMPBF::BlobHeader read_header(){ 147 | int32_t sz; 148 | OSMPBF::BlobHeader result; 149 | 150 | // read the first 4 bytes of the file, this is the size of the blob-header 151 | if( !file.read((char*)&sz, 4) ){ 152 | info() << "We finished reading the file"; 153 | this->finished = true; 154 | return result; 155 | } 156 | 157 | sz = ntohl(sz);// convert the size from network byte-order to host byte-order 158 | 159 | if(sz > max_blob_header_size) 160 | fatal() << "blob-header-size is bigger then allowed " << sz << " > " << max_blob_header_size; 161 | 162 | this->file.read(this->buffer, sz); 163 | if(!this->file.good()) 164 | fatal() << "unable to read blob-header from file"; 165 | 166 | // parse the blob-header from the read-buffer 167 | if(!result.ParseFromArray(this->buffer, sz)) 168 | fatal() << "unable to parse blob header"; 169 | return result; 170 | } 171 | 172 | int32_t read_blob(const OSMPBF::BlobHeader & header){ 173 | OSMPBF::Blob blob; 174 | // size of the following blob 175 | int32_t sz = header.datasize(); 176 | 177 | if(sz > max_uncompressed_blob_size) 178 | fatal() << "blob-size is bigger then allowed"; 179 | 180 | if(!this->file.read(buffer, sz)) 181 | fatal() << "unable to read blob from file"; 182 | if(!blob.ParseFromArray(this->buffer, sz)) 183 | fatal() << "unable to parse blob"; 184 | 185 | // if the blob has uncompressed data 186 | if(blob.has_raw()) { 187 | // size of the blob-data 188 | sz = blob.raw().size(); 189 | 190 | // check that raw_size is set correctly 191 | if(sz != blob.raw_size()) 192 | warn() << " reports wrong raw_size: " << blob.raw_size() << " bytes"; 193 | 194 | memcpy(unpack_buffer, blob.raw().c_str(), sz); 195 | return sz; 196 | } 197 | 198 | 199 | if(blob.has_zlib_data()) { 200 | sz = blob.zlib_data().size(); 201 | 202 | z_stream z; 203 | z.next_in = (unsigned char*) blob.zlib_data().c_str(); 204 | z.avail_in = sz; 205 | z.next_out = (unsigned char*) unpack_buffer; 206 | z.avail_out = blob.raw_size(); 207 | z.zalloc = Z_NULL; 208 | z.zfree = Z_NULL; 209 | z.opaque = Z_NULL; 210 | 211 | if(inflateInit(&z) != Z_OK) { 212 | fatal() << "failed to init zlib stream"; 213 | } 214 | if(inflate(&z, Z_FINISH) != Z_STREAM_END) { 215 | fatal() << "failed to inflate zlib stream"; 216 | } 217 | if(inflateEnd(&z) != Z_OK) { 218 | fatal() << "failed to deinit zlib stream"; 219 | } 220 | return z.total_out; 221 | } 222 | 223 | if(blob.has_lzma_data()) { 224 | fatal() << "lzma-decompression is not supported"; 225 | } 226 | return 0; 227 | } 228 | 229 | void parse_primitiveblock(int32_t sz) { 230 | OSMPBF::PrimitiveBlock primblock; 231 | if(!primblock.ParseFromArray(this->unpack_buffer, sz)) 232 | fatal() << "unable to parse primitive block"; 233 | 234 | for(int i = 0, l = primblock.primitivegroup_size(); i < l; i++) { 235 | const OSMPBF::PrimitiveGroup& pg = primblock.primitivegroup(i); 236 | 237 | // Simple Nodes 238 | for(int i = 0; i < pg.nodes_size(); ++i) { 239 | const OSMPBF::Node& n = pg.nodes(i); 240 | 241 | double lon = 0.000000001 * (primblock.lon_offset() + (primblock.granularity() * n.lon())) ; 242 | double lat = 0.000000001 * (primblock.lat_offset() + (primblock.granularity() * n.lat())) ; 243 | visitor.node_callback(n.id(), lon, lat, get_tags(n, primblock)); 244 | } 245 | 246 | // Dense Nodes 247 | if(pg.has_dense()) { 248 | const OSMPBF::DenseNodes& dn = pg.dense(); 249 | uint64_t id = 0; 250 | double lon = 0; 251 | double lat = 0; 252 | 253 | int current_kv = 0; 254 | 255 | for(int i = 0; i < dn.id_size(); ++i) { 256 | id += dn.id(i); 257 | lon += 0.000000001 * (primblock.lon_offset() + (primblock.granularity() * dn.lon(i))); 258 | lat += 0.000000001 * (primblock.lat_offset() + (primblock.granularity() * dn.lat(i))); 259 | 260 | Tags tags; 261 | while (current_kv < dn.keys_vals_size() && dn.keys_vals(current_kv) != 0){ 262 | uint64_t key = dn.keys_vals(current_kv); 263 | uint64_t val = dn.keys_vals(current_kv + 1); 264 | std::string key_string = primblock.stringtable().s(key); 265 | std::string val_string = primblock.stringtable().s(val); 266 | current_kv += 2; 267 | tags[key_string] = val_string; 268 | } 269 | ++current_kv; 270 | visitor.node_callback(id, lon, lat, tags); 271 | } 272 | } 273 | 274 | for(int i = 0; i < pg.ways_size(); ++i) { 275 | const OSMPBF::Way& w = pg.ways(i); 276 | 277 | uint64_t ref = 0; 278 | std::vector refs; 279 | for(int j = 0; j < w.refs_size(); ++j){ 280 | ref += w.refs(j); 281 | refs.push_back(ref); 282 | } 283 | uint64_t id = w.id(); 284 | visitor.way_callback(id, get_tags(w, primblock), refs); 285 | } 286 | 287 | 288 | for(int i=0; i < pg.relations_size(); ++i){ 289 | const OSMPBF::Relation& rel = pg.relations(i); 290 | uint64_t id = 0; 291 | References refs; 292 | 293 | for(int l = 0; l < rel.memids_size(); ++l){ 294 | id += rel.memids(l); 295 | refs.push_back(Reference(rel.types(l), id, primblock.stringtable().s(rel.roles_sid(l)))); 296 | } 297 | 298 | visitor.relation_callback(rel.id(), get_tags(rel, primblock), refs); 299 | } 300 | } 301 | } 302 | }; 303 | 304 | template 305 | void read_osm_pbf(const std::string & filename, Visitor & visitor){ 306 | Parser p(filename, visitor); 307 | p.parse(); 308 | } 309 | 310 | } 311 | --------------------------------------------------------------------------------