Osmium  0.1
include/osmium/output/pbf.hpp
Go to the documentation of this file.
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
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines