Osmium
0.1
|
00001 #ifndef OSMIUM_OUTPUT_PBF_HPP 00002 #define OSMIUM_OUTPUT_PBF_HPP 00003 00004 /* 00005 00006 Copyright 2011 Jochen Topf <jochen@topf.org> and others (see README). 00007 00008 This file is part of Osmium (https://github.com/joto/osmium). 00009 00010 Osmium is free software: you can redistribute it and/or modify it under the 00011 terms of the GNU Lesser General Public License or (at your option) the GNU 00012 General Public License as published by the Free Software Foundation, either 00013 version 3 of the Licenses, or (at your option) any later version. 00014 00015 Osmium is distributed in the hope that it will be useful, but WITHOUT ANY 00016 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 00017 PARTICULAR PURPOSE. See the GNU Lesser General Public License and the GNU 00018 General Public License for more details. 00019 00020 You should have received a copy of the Licenses along with Osmium. If not, see 00021 <http://www.gnu.org/licenses/>. 00022 00023 */ 00024 00025 /* 00026 00027 About the .osm.pbf file format 00028 This is an excerpt of <http://wiki.openstreetmap.org/wiki/PBF_Format> 00029 00030 The .osm.pbf format and it's derived formats (.osh.pbf and .osc.pbf) are encoded 00031 using googles protobuf library for the low-level storage. They are constructed 00032 by nesting data on two levels: 00033 00034 On the lower level the file is constructed using BlobHeaders and Blobs. A .osm.pbf 00035 file contains multiple sequences of 00036 1. a 4-byte header size, stored in network-byte-order 00037 2. a BlobHeader of exactly this size 00038 3. a Blob 00039 00040 The BlobHeader tells the reader about the type and size of the following Blob. The 00041 Blob can contain data in raw or zlib-compressed form. After uncompressing the blob 00042 it is treated differently depending on the type specified in the BlobHeader. 00043 00044 The contents of the Blob belongs to the higher level. It contains either an HeaderBlock 00045 (type="OSMHeader") or an PrimitiveBlock (type="OSMData"). The file needs to have 00046 at least one HeaderBlock before the first PrimitiveBlock. 00047 00048 The HeaderBlock contains meta-information like the writing program or a bbox. It may 00049 also contain multiple "required features" that describe what kinds of input a 00050 reading program needs to handle in order to fully understand the files' contents. 00051 00052 The PrimitiveBlock can store multiple types of objects (i.e. 5 nodes, 2 ways and 00053 1 relation). It contains one or more PrimitiveGroup which in turn contain multiple 00054 nodes, ways or relations. A PrimitiveGroup should only contain one kind of object. 00055 00056 There's a special kind of "object type" called dense-nodes. It is used to store nodes 00057 in a very dense format, avoiding message overheads and using delta-encoding for nearly 00058 all ids. 00059 00060 All Strings are stored as indexes to rows in a StringTable. The StringTable contains 00061 one row for each used string, so strings that are used multiple times need to be 00062 stored only once. The StringTable is sorted by usage-count, so the most often used 00063 string is stored at index 1. 00064 00065 A simple outline of a .osm.pbf file could look like this: 00066 00067 4-bytes header size 00068 BlobHeader 00069 Blob 00070 HeaderBlock 00071 4-bytes header size 00072 BlobHeader 00073 Blob 00074 PrimitiveBlock 00075 StringTable 00076 PrimitiveGroup 00077 5 nodes 00078 PrimitiveGroup 00079 2 ways 00080 PrimitiveGroup 00081 1 relation 00082 00083 More complete outlines of real .osm.pbf files can be created using the osmpbf-outline tool: 00084 <https://github.com/MaZderMind/OSM-binary/tree/osmpbf-outline> 00085 */ 00086 00087 // netinet provides the network-byte-order conversion function 00088 #include <netinet/in.h> 00089 00090 // the algorithm-lib contains the sort functions 00091 #include <algorithm> 00092 00093 // math is used for round() in lonlat2int 00094 #include <math.h> 00095 00096 #include <zlib.h> 00097 00098 #include <osmpbf/osmpbf.h> 00099 00100 // StringTable management 00101 #include <osmium/utils/stringtable.hpp> 00102 00103 #include <osmium/utils/delta.hpp> 00104 00105 namespace Osmium { 00106 00107 namespace Output { 00108 00109 class PBF : public Base { 00110 00123 static const uint32_t max_block_contents = 8000; 00124 00131 static const int buffer_fill_percent = 95; 00132 00136 OSMPBF::Blob pbf_blob; 00137 00141 OSMPBF::BlobHeader pbf_blob_header; 00142 00146 OSMPBF::HeaderBlock pbf_header_block; 00147 00151 OSMPBF::PrimitiveBlock pbf_primitive_block; 00152 00157 OSMPBF::PrimitiveGroup* pbf_nodes; 00158 OSMPBF::PrimitiveGroup* pbf_ways; 00159 OSMPBF::PrimitiveGroup* pbf_relations; 00160 00168 int m_location_granularity; 00169 00175 int m_date_granularity; 00176 00187 bool m_use_dense_format; 00188 00196 bool m_use_compression; 00197 00202 bool m_should_add_metadata; 00203 00207 bool m_add_visible; 00208 00217 uint16_t primitive_block_contents; 00218 uint32_t primitive_block_size; 00219 00220 // StringTable management 00221 Osmium::StringTable string_table; 00222 00224 char m_compression_buffer[OSMPBF::max_uncompressed_blob_size]; 00225 00231 Delta<int64_t> m_delta_id; 00232 Delta<int64_t> m_delta_lat; 00233 Delta<int64_t> m_delta_lon; 00234 Delta<int64_t> m_delta_timestamp; 00235 Delta<int64_t> m_delta_changeset; 00236 Delta<int64_t> m_delta_uid; 00237 Delta<uint32_t> m_delta_user_sid; 00238 00239 00241 00248 size_t zlib_compress(std::string &in) { 00249 // zlib compression context 00250 z_stream z; 00251 00252 // next byte to compress 00253 z.next_in = (uint8_t*) in.c_str(); 00254 00255 // number of bytes to compress 00256 z.avail_in = in.size(); 00257 00258 // place to store compressed bytes 00259 z.next_out = (uint8_t*) m_compression_buffer; 00260 00261 // space for compressed data 00262 z.avail_out = OSMPBF::max_uncompressed_blob_size; 00263 00264 // custom allocator functions - not used 00265 z.zalloc = Z_NULL; 00266 z.zfree = Z_NULL; 00267 z.opaque = Z_NULL; 00268 00269 // initiate the compression 00270 if (deflateInit(&z, Z_DEFAULT_COMPRESSION) != Z_OK) { 00271 throw std::runtime_error("failed to init zlib stream"); 00272 } 00273 00274 // compress 00275 if (deflate(&z, Z_FINISH) != Z_STREAM_END) { 00276 throw std::runtime_error("failed to deflate zlib stream"); 00277 } 00278 00279 // finish compression 00280 if (deflateEnd(&z) != Z_OK) { 00281 throw std::runtime_error("failed to deinit zlib stream"); 00282 } 00283 00284 // print debug info about the compression 00285 if (Osmium::debug()) { 00286 std::cerr << "pack " << in.size() << " bytes to " << z.total_out << " bytes (1:" << (double)in.size() / z.total_out << ")" << std::endl; 00287 } 00288 00289 // number of compressed bytes 00290 return z.total_out; 00291 } 00292 00300 void store_blob(const std::string &type, const google::protobuf::MessageLite &msg) { 00301 // buffer to serialize the protobuf message to 00302 std::string data; 00303 00304 // serialize the protobuf message to the string 00305 msg.SerializeToString(&data); 00306 00307 if (use_compression()) { 00308 // compress using zlib 00309 size_t out = zlib_compress(data); 00310 00311 // set the compressed data on the Blob 00312 pbf_blob.set_zlib_data(m_compression_buffer, out); 00313 } else { // no compression 00314 // print debug info about the raw data 00315 if (Osmium::debug()) { 00316 std::cerr << "store uncompressed " << data.size() << " bytes" << std::endl; 00317 } 00318 00319 // just set the raw data on the Blob 00320 pbf_blob.set_raw(data); 00321 } 00322 00323 // set the size of the uncompressed data on the blob 00324 pbf_blob.set_raw_size(data.size()); 00325 00326 // clear the blob string 00327 data.clear(); 00328 00329 // serialize and clear the Blob 00330 pbf_blob.SerializeToString(&data); 00331 pbf_blob.Clear(); 00332 00333 // set the header-type to the supplied string on the BlobHeader 00334 pbf_blob_header.set_type(type); 00335 00336 // set the size of the serialized blob on the BlobHeader 00337 pbf_blob_header.set_datasize(data.size()); 00338 00339 // a place to serialize the BlobHeader to 00340 std::string blobhead; 00341 00342 // serialize and clear the BlobHeader 00343 pbf_blob_header.SerializeToString(&blobhead); 00344 pbf_blob_header.Clear(); 00345 00346 // the 4-byte size of the BlobHeader, transformed from Host- to Network-Byte-Order 00347 uint32_t sz = htonl(blobhead.size()); 00348 00349 // write to the file: the 4-byte BlobHeader-Size followed by the BlobHeader followed by the Blob 00350 if (::write(get_fd(), &sz, sizeof(sz)) < 0) { 00351 throw std::runtime_error("file error"); 00352 } 00353 if (::write(get_fd(), blobhead.c_str(), blobhead.size()) < 0) { 00354 throw std::runtime_error("file error"); 00355 } 00356 if (::write(get_fd(), data.c_str(), data.size()) < 0) { 00357 throw std::runtime_error("file error"); 00358 } 00359 } 00360 00368 void map_string_ids() { 00369 // test, if the node-block has been allocated 00370 if (pbf_nodes) { 00371 // iterate over all nodes, passing them to the map_common_string_ids function 00372 for (int i=0, l=pbf_nodes->nodes_size(); i<l; i++) { 00373 map_common_string_ids(pbf_nodes->mutable_nodes(i)); 00374 } 00375 00376 // test, if the node-block has a densenodes structure 00377 if (pbf_nodes->has_dense()) { 00378 // get a pointer to the densenodes structure 00379 OSMPBF::DenseNodes* dense = pbf_nodes->mutable_dense(); 00380 00381 // in the densenodes structure keys and vals are encoded in an intermixed 00382 // array, individual nodes are seperated by a value of 0 (0 in the StringTable 00383 // is always unused). String-ids of 0 are thus kept alone. 00384 for (int i=0, l=dense->keys_vals_size(); i<l; i++) { 00385 // map interim string-ids > 0 to real string ids 00386 uint16_t sid = dense->keys_vals(i); 00387 if (sid > 0) { 00388 dense->set_keys_vals(i, string_table.map_string_id(sid)); 00389 } 00390 } 00391 00392 // test if the densenodes block has meta infos 00393 if (dense->has_denseinfo()) { 00394 // get a pointer to the denseinfo structure 00395 OSMPBF::DenseInfo* denseinfo = dense->mutable_denseinfo(); 00396 00397 // iterate over all username string-ids 00398 for (int i=0, l= denseinfo->user_sid_size(); i<l; i++) { 00399 // map interim string-ids > 0 to real string ids 00400 uint16_t user_sid = string_table.map_string_id(denseinfo->user_sid(i)); 00401 00402 // delta encode the string-id 00403 denseinfo->set_user_sid(i, m_delta_user_sid.update(user_sid)); 00404 } 00405 } 00406 } 00407 } 00408 00409 // test, if the ways-block has been allocated 00410 if (pbf_ways) { 00411 // iterate over all ways, passing them to the map_common_string_ids function 00412 for (int i=0, l=pbf_ways->ways_size(); i<l; i++) { 00413 map_common_string_ids(pbf_ways->mutable_ways(i)); 00414 } 00415 } 00416 00417 // test, if the relations-block has been allocated 00418 if (pbf_relations) { 00419 // iterate over all relations 00420 for (int i=0, l=pbf_relations->relations_size(); i<l; i++) { 00421 // get a pointer to the relation 00422 OSMPBF::Relation* relation = pbf_relations->mutable_relations(i); 00423 00424 // pass them to the map_common_string_ids function 00425 map_common_string_ids(relation); 00426 00427 // iterate over all relation members, mapping the interim string-ids 00428 // of the role to real string ids 00429 for (int mi=0, ml=relation->roles_sid_size(); mi<ml; mi++) { 00430 relation->set_roles_sid(mi, string_table.map_string_id(relation->roles_sid(mi))); 00431 } 00432 } 00433 } 00434 } 00435 00442 template <class pbf_object_t> void map_common_string_ids(pbf_object_t* in) { 00443 // if the object has meta-info attached 00444 if (in->has_info()) { 00445 // map the interim-id of the user name to a real id 00446 OSMPBF::Info* info = in->mutable_info(); 00447 info->set_user_sid(string_table.map_string_id(info->user_sid())); 00448 } 00449 00450 // iterate over all tags and map the interim-ids of the key and the value to real ids 00451 for (int i=0, l=in->keys_size(); i<l; i++) { 00452 in->set_keys(i, string_table.map_string_id(in->keys(i))); 00453 in->set_vals(i, string_table.map_string_id(in->vals(i))); 00454 } 00455 } 00456 00457 00459 00463 int64_t lonlat2int(double lonlat) { 00464 return round(lonlat * OSMPBF::lonlat_resolution / location_granularity()); 00465 } 00466 00470 int64_t timestamp2int(time_t timestamp) { 00471 return round(timestamp * ((double)1000 / date_granularity())); 00472 } 00473 00480 template <class pbf_object_t> void apply_common_info(const shared_ptr<Osmium::OSM::Object const>& in, pbf_object_t* out) { 00481 // set the object-id 00482 out->set_id(in->id()); 00483 00484 // iterate over all tags and set the keys and vals, recording the strings in the 00485 // interim StringTable and storing the interim ids 00486 Osmium::OSM::TagList::const_iterator end = in->tags().end(); 00487 for (Osmium::OSM::TagList::const_iterator it = in->tags().begin(); it != end; ++it) { 00488 out->add_keys(string_table.record_string(it->key())); 00489 out->add_vals(string_table.record_string(it->value())); 00490 } 00491 00492 if (should_add_metadata()) { 00493 // add an info-section to the pbf object and set the meta-info on it 00494 OSMPBF::Info* out_info = out->mutable_info(); 00495 if (m_add_visible) { 00496 out_info->set_visible(in->visible()); 00497 } 00498 out_info->set_version(in->version()); 00499 out_info->set_timestamp(timestamp2int(in->timestamp())); 00500 out_info->set_changeset(in->changeset()); 00501 out_info->set_uid(in->uid()); 00502 out_info->set_user_sid(string_table.record_string(in->user())); 00503 } 00504 } 00505 00506 00508 00512 void store_header_block() { 00513 if (Osmium::debug()) { 00514 std::cerr << "storing header block" << std::endl; 00515 } 00516 store_blob("OSMHeader", pbf_header_block); 00517 pbf_header_block.Clear(); 00518 } 00519 00525 void store_primitive_block() { 00526 if (Osmium::debug()) { 00527 std::cerr << "storing primitive block with " << primitive_block_contents << " items" << std::endl; 00528 } 00529 00530 // store the interim StringTable into the protobuf object 00531 string_table.store_stringtable(pbf_primitive_block.mutable_stringtable()); 00532 00533 // map all interim string ids to real ids 00534 map_string_ids(); 00535 00536 // store the Blob 00537 store_blob("OSMData", pbf_primitive_block); 00538 00539 // clear the PrimitiveBlock struct 00540 pbf_primitive_block.Clear(); 00541 00542 // add empty StringTable entry at index 0 00543 // StringTable index 0 is rserved as delimiter in the densenodes key/value list 00544 // this line also ensures that there's always a valid StringTable 00545 pbf_primitive_block.mutable_stringtable()->add_s(""); 00546 00547 // set the granularity 00548 pbf_primitive_block.set_granularity(location_granularity()); 00549 pbf_primitive_block.set_date_granularity(date_granularity()); 00550 00551 // clear the interim StringTable and its id map 00552 string_table.clear(); 00553 00554 // reset the delta variables 00555 m_delta_id.clear(); 00556 m_delta_lat.clear(); 00557 m_delta_lon.clear(); 00558 m_delta_timestamp.clear(); 00559 m_delta_changeset.clear(); 00560 m_delta_uid.clear(); 00561 m_delta_user_sid.clear(); 00562 00563 // reset the contents-counter to zero 00564 primitive_block_contents = 0; 00565 primitive_block_size = 0; 00566 00567 // reset the node/way/relation pointers to NULL 00568 pbf_nodes = NULL; 00569 pbf_ways = NULL; 00570 pbf_relations = NULL; 00571 } 00572 00581 void check_block_contents_counter() { 00582 if (primitive_block_contents >= max_block_contents) { 00583 store_primitive_block(); 00584 } 00585 else if (primitive_block_size > (static_cast<uint32_t>(OSMPBF::max_uncompressed_blob_size) * buffer_fill_percent / 100)) { 00586 if (Osmium::debug()) { 00587 std::cerr << "storing primitive_block with only " << primitive_block_contents << " items, because its ByteSize (" << primitive_block_size << ") reached " << 00588 (static_cast<float>(primitive_block_size) / static_cast<float>(OSMPBF::max_uncompressed_blob_size) * 100.0) << "% of the maximum blob-size" << std::endl; 00589 } 00590 00591 store_primitive_block(); 00592 } 00593 00594 primitive_block_contents++; 00595 } 00596 00597 00599 00605 void write_node(const shared_ptr<Osmium::OSM::Node const>& node) { 00606 // add a way to the group 00607 OSMPBF::Node* pbf_node = pbf_nodes->add_nodes(); 00608 00609 // copy the common meta-info from the osmium-object to the pbf-object 00610 apply_common_info(node, pbf_node); 00611 00612 // modify lat & lon to integers, respecting the block's granularity and copy 00613 // the ints to the pbf-object 00614 pbf_node->set_lon(lonlat2int(node->get_lon())); 00615 pbf_node->set_lat(lonlat2int(node->get_lat())); 00616 } 00617 00623 void write_dense_node(const shared_ptr<Osmium::OSM::Node const>& node) { 00624 // add a DenseNodes-Section to the PrimitiveGroup 00625 OSMPBF::DenseNodes* dense = pbf_nodes->mutable_dense(); 00626 00627 // copy the id, delta encoded 00628 dense->add_id(m_delta_id.update(node->id())); 00629 00630 // copy the longitude, delta encoded 00631 dense->add_lon(m_delta_lon.update(lonlat2int(node->get_lon()))); 00632 00633 // copy the latitude, delta encoded 00634 dense->add_lat(m_delta_lat.update(lonlat2int(node->get_lat()))); 00635 00636 // in the densenodes structure keys and vals are encoded in an intermixed 00637 // array, individual nodes are seperated by a value of 0 (0 in the StringTable 00638 // is always unused) 00639 // so for three nodes the keys_vals array may look like this: 3 5 2 1 0 0 8 5 00640 // the first node has two tags (3=>5 and 2=>1), the second node has does not 00641 // have any tags and the third node has a single tag (8=>5) 00642 Osmium::OSM::TagList::const_iterator end = node->tags().end(); 00643 for (Osmium::OSM::TagList::const_iterator it = node->tags().begin(); it != end; ++it) { 00644 dense->add_keys_vals(string_table.record_string(it->key())); 00645 dense->add_keys_vals(string_table.record_string(it->value())); 00646 } 00647 dense->add_keys_vals(0); 00648 00649 if (should_add_metadata()) { 00650 // add a DenseInfo-Section to the PrimitiveGroup 00651 OSMPBF::DenseInfo* denseinfo = dense->mutable_denseinfo(); 00652 00653 denseinfo->add_version(node->version()); 00654 00655 if (m_add_visible) { 00656 denseinfo->add_visible(node->visible()); 00657 } 00658 00659 // copy the timestamp, delta encoded 00660 denseinfo->add_timestamp(m_delta_timestamp.update(timestamp2int(node->timestamp()))); 00661 00662 // copy the changeset, delta encoded 00663 denseinfo->add_changeset(m_delta_changeset.update(node->changeset())); 00664 00665 // copy the user id, delta encoded 00666 denseinfo->add_uid(m_delta_uid.update(node->uid())); 00667 00668 // record the user-name to the interim stringtable and copy the 00669 // interim string-id to the pbf-object 00670 denseinfo->add_user_sid(string_table.record_string(node->user())); 00671 } 00672 } 00673 00679 void write_way(const shared_ptr<Osmium::OSM::Way const>& way) { 00680 // add a way to the group 00681 OSMPBF::Way* pbf_way = pbf_ways->add_ways(); 00682 00683 // copy the common meta-info from the osmium-object to the pbf-object 00684 apply_common_info(way, pbf_way); 00685 00686 // last way-node-id used for delta-encoding 00687 Delta<int64_t> delta_id; 00688 00689 // iterate over all way-nodes 00690 for (int i=0, l = way->node_count(); i<l; i++) { 00691 // copy the way-node-id, delta encoded 00692 pbf_way->add_refs(delta_id.update(way->get_node_id(i))); 00693 } 00694 00695 // count up blob size by the size of the Way 00696 primitive_block_size += pbf_way->ByteSize(); 00697 } 00698 00704 void write_relation(const shared_ptr<Osmium::OSM::Relation const>& relation) { 00705 // add a relation to the group 00706 OSMPBF::Relation* pbf_relation = pbf_relations->add_relations(); 00707 00708 // copy the common meta-info from the osmium-object to the pbf-object 00709 apply_common_info(relation, pbf_relation); 00710 00711 Delta<int64_t> delta_id; 00712 00713 // iterate over all relation-members 00714 for (int i=0, l=relation->members().size(); i<l; i++) { 00715 // save a pointer to the osmium-object representing the relation-member 00716 const Osmium::OSM::RelationMember* mem = relation->get_member(i); 00717 00718 // record the relation-member role to the interim stringtable and copy the 00719 // interim string-id to the pbf-object 00720 pbf_relation->add_roles_sid(string_table.record_string(mem->role())); 00721 00722 // copy the relation-member-id, delta encoded 00723 pbf_relation->add_memids(delta_id.update(mem->ref())); 00724 00725 // copy the relation-member-type, mapped to the OSMPBF enum 00726 switch (mem->type()) { 00727 case 'n': 00728 pbf_relation->add_types(OSMPBF::Relation::NODE); 00729 break; 00730 case 'w': 00731 pbf_relation->add_types(OSMPBF::Relation::WAY); 00732 break; 00733 case 'r': 00734 pbf_relation->add_types(OSMPBF::Relation::RELATION); 00735 break; 00736 default: 00737 throw std::runtime_error("Unknown relation member type: " + mem->type()); 00738 } 00739 } 00740 00741 // count up blob size by the size of the Relation 00742 primitive_block_size += pbf_relation->ByteSize(); 00743 } 00744 00745 public: 00746 00750 PBF(Osmium::OSMFile& file) : Base(file), 00751 pbf_nodes(NULL), 00752 pbf_ways(NULL), 00753 pbf_relations(NULL), 00754 m_location_granularity(pbf_primitive_block.granularity()), 00755 m_date_granularity(pbf_primitive_block.date_granularity()), 00756 m_use_dense_format(true), 00757 m_use_compression(true), 00758 m_should_add_metadata(true), 00759 m_add_visible(file.has_multiple_object_versions()), 00760 primitive_block_contents(0), 00761 primitive_block_size(0), 00762 string_table(), 00763 m_compression_buffer(), 00764 m_delta_id(), 00765 m_delta_lat(), 00766 m_delta_lon(), 00767 m_delta_timestamp(), 00768 m_delta_changeset(), 00769 m_delta_uid(), 00770 m_delta_user_sid() { 00771 00772 GOOGLE_PROTOBUF_VERIFY_VERSION; 00773 } 00774 00778 bool use_dense_format() const { 00779 return m_use_dense_format; 00780 } 00781 00785 PBF& use_dense_format(bool flag) { 00786 m_use_dense_format = flag; 00787 return *this; 00788 } 00789 00790 00794 bool use_compression() const { 00795 return m_use_compression; 00796 } 00797 00801 PBF& use_compression(bool flag) { 00802 m_use_compression = flag; 00803 return *this; 00804 } 00805 00806 00810 int location_granularity() const { 00811 return m_location_granularity; 00812 } 00813 00817 PBF& location_granularity(int g) { 00818 m_location_granularity = g; 00819 return *this; 00820 } 00821 00822 00826 int date_granularity() const { 00827 return m_date_granularity; 00828 } 00829 00833 PBF& date_granularity(int g) { 00834 m_date_granularity = g; 00835 return *this; 00836 } 00837 00838 00842 bool should_add_metadata() const { 00843 return m_should_add_metadata; 00844 } 00845 00849 PBF& should_add_metadata(bool flag) { 00850 m_should_add_metadata = flag; 00851 return *this; 00852 } 00853 00854 00861 void init(Osmium::OSM::Meta& meta) { 00862 if (Osmium::debug()) { 00863 std::cerr << "pbf write init" << std::endl; 00864 } 00865 00866 // add the schema version as required feature to the HeaderBlock 00867 pbf_header_block.add_required_features("OsmSchema-V0.6"); 00868 00869 // when the densenodes-feature is used, add DenseNodes as required feature 00870 if (use_dense_format()) { 00871 pbf_header_block.add_required_features("DenseNodes"); 00872 } 00873 00874 // when the resulting file will carry history information, add 00875 // HistoricalInformation as required feature 00876 if (m_file.get_type() == Osmium::OSMFile::FileType::History()) { 00877 pbf_header_block.add_required_features("HistoricalInformation"); 00878 } 00879 00880 // set the writing program 00881 pbf_header_block.set_writingprogram("Osmium (http://wiki.openstreetmap.org/wiki/Osmium)"); 00882 00883 if (meta.bounds().defined()) { 00884 OSMPBF::HeaderBBox* bbox = pbf_header_block.mutable_bbox(); 00885 bbox->set_left(meta.bounds().bl().lon() * OSMPBF::lonlat_resolution); 00886 bbox->set_bottom(meta.bounds().bl().lat() * OSMPBF::lonlat_resolution); 00887 bbox->set_right(meta.bounds().tr().lon() * OSMPBF::lonlat_resolution); 00888 bbox->set_top(meta.bounds().tr().lat() * OSMPBF::lonlat_resolution); 00889 } 00890 00891 store_header_block(); 00892 00893 // add empty StringTable entry at index 0 00894 // StringTable index 0 is reserved as delimiter in the densenodes key/value list 00895 // this line also ensures that there's always a valid StringTable 00896 pbf_primitive_block.mutable_stringtable()->add_s(""); 00897 00898 // set the granularity 00899 pbf_primitive_block.set_granularity(location_granularity()); 00900 pbf_primitive_block.set_date_granularity(date_granularity()); 00901 } 00902 00910 void node(const shared_ptr<Osmium::OSM::Node const>& node) { 00911 // first of we check the contents-counter which may flush the cached nodes to 00912 // disk if the limit is reached. This call also increases the contents-counter 00913 check_block_contents_counter(); 00914 00915 if (Osmium::debug()) { 00916 std::cerr << "node " << node->id() << " v" << node->version() << std::endl; 00917 } 00918 00919 // if no PrimitiveGroup for nodes has been added, add one and save the pointer 00920 if (!pbf_nodes) { 00921 pbf_nodes = pbf_primitive_block.add_primitivegroup(); 00922 } 00923 00924 if (use_dense_format()) { 00925 write_dense_node(node); 00926 } else { 00927 write_node(node); 00928 } 00929 } 00930 00938 void way(const shared_ptr<Osmium::OSM::Way const>& way) { 00939 // first of we check the contents-counter which may flush the cached ways to 00940 // disk if the limit is reached. This call also increases the contents-counter 00941 check_block_contents_counter(); 00942 00943 if (Osmium::debug()) { 00944 std::cerr << "way " << way->id() << " v" << way->version() << " with " << way->node_count() << " nodes" << std::endl; 00945 } 00946 00947 // if no PrimitiveGroup for nodes has been added, add one and save the pointer 00948 if (!pbf_ways) { 00949 pbf_ways = pbf_primitive_block.add_primitivegroup(); 00950 } 00951 00952 write_way(way); 00953 } 00954 00962 void relation(const shared_ptr<Osmium::OSM::Relation const>& relation) { 00963 // first of we check the contents-counter which may flush the cached relations to 00964 // disk if the limit is reached. This call also increases the contents-counter 00965 check_block_contents_counter(); 00966 00967 if (Osmium::debug()) { 00968 std::cerr << "relation " << relation->id() << " v" << relation->version() << " with " << relation->members().size() << " members" << std::endl; 00969 } 00970 00971 // if no PrimitiveGroup for relations has been added, add one and save the pointer 00972 if (!pbf_relations) { 00973 pbf_relations = pbf_primitive_block.add_primitivegroup(); 00974 } 00975 00976 write_relation(relation); 00977 } 00978 00983 void final() { 00984 if (Osmium::debug()) { 00985 std::cerr << "finishing" << std::endl; 00986 } 00987 00988 // if the current block contains any elements, flush it to the protobuf 00989 if (primitive_block_contents > 0) { 00990 store_primitive_block(); 00991 } 00992 00993 m_file.close(); 00994 } 00995 00996 }; // class PBF 00997 00998 } // namespace Output 00999 01000 } // namespace Osmium 01001 01002 #endif // OSMIUM_OUTPUT_PBF_HPP