Squashed 'third_party/libosmium/' content from commit 2282c84
git-subtree-dir: third_party/libosmium git-subtree-split: 2282c8450bae55839372a2002db7ca754530d2fc
This commit is contained in:
@@ -0,0 +1,485 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_DEBUG_OUTPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_DEBUG_OUTPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <future>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/io/detail/output_format.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/memory/collection.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/changeset.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/thread/pool.hpp>
|
||||
#include <osmium/util/minmax.hpp>
|
||||
#include <osmium/visitor.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
class File;
|
||||
|
||||
namespace detail {
|
||||
|
||||
constexpr const char* color_bold = "\x1b[1m";
|
||||
constexpr const char* color_black = "\x1b[30m";
|
||||
constexpr const char* color_gray = "\x1b[30;1m";
|
||||
constexpr const char* color_red = "\x1b[31m";
|
||||
constexpr const char* color_green = "\x1b[32m";
|
||||
constexpr const char* color_yellow = "\x1b[33m";
|
||||
constexpr const char* color_blue = "\x1b[34m";
|
||||
constexpr const char* color_magenta = "\x1b[35m";
|
||||
constexpr const char* color_cyan = "\x1b[36m";
|
||||
constexpr const char* color_white = "\x1b[37m";
|
||||
constexpr const char* color_reset = "\x1b[0m";
|
||||
|
||||
struct debug_output_options {
|
||||
|
||||
/// Should metadata of objects be added?
|
||||
bool add_metadata;
|
||||
|
||||
/// Output with ANSI colors?
|
||||
bool use_color;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes out one buffer with OSM data in Debug format.
|
||||
*/
|
||||
class DebugOutputBlock : public OutputBlock {
|
||||
|
||||
debug_output_options m_options;
|
||||
|
||||
const char* m_utf8_prefix = "";
|
||||
const char* m_utf8_suffix = "";
|
||||
|
||||
void append_encoded_string(const char* data) {
|
||||
append_debug_encoded_string(*m_out, data, m_utf8_prefix, m_utf8_suffix);
|
||||
}
|
||||
|
||||
void write_color(const char* color) {
|
||||
if (m_options.use_color) {
|
||||
*m_out += color;
|
||||
}
|
||||
}
|
||||
|
||||
void write_string(const char* string) {
|
||||
*m_out += '"';
|
||||
write_color(color_blue);
|
||||
append_encoded_string(string);
|
||||
write_color(color_reset);
|
||||
*m_out += '"';
|
||||
}
|
||||
|
||||
void write_object_type(const char* object_type, bool visible = true) {
|
||||
if (visible) {
|
||||
write_color(color_bold);
|
||||
} else {
|
||||
write_color(color_white);
|
||||
}
|
||||
*m_out += object_type;
|
||||
write_color(color_reset);
|
||||
*m_out += ' ';
|
||||
}
|
||||
|
||||
void write_fieldname(const char* name) {
|
||||
*m_out += " ";
|
||||
write_color(color_cyan);
|
||||
*m_out += name;
|
||||
write_color(color_reset);
|
||||
*m_out += ": ";
|
||||
}
|
||||
|
||||
void write_comment_field(const char* name) {
|
||||
write_color(color_cyan);
|
||||
*m_out += name;
|
||||
write_color(color_reset);
|
||||
*m_out += ": ";
|
||||
}
|
||||
|
||||
void write_counter(int width, int n) {
|
||||
write_color(color_white);
|
||||
output_formatted(" %0*d: ", width, n++);
|
||||
write_color(color_reset);
|
||||
}
|
||||
|
||||
void write_error(const char* msg) {
|
||||
write_color(color_red);
|
||||
*m_out += msg;
|
||||
write_color(color_reset);
|
||||
}
|
||||
|
||||
void write_timestamp(const osmium::Timestamp& timestamp) {
|
||||
if (timestamp.valid()) {
|
||||
*m_out += timestamp.to_iso();
|
||||
output_formatted(" (%d)", timestamp.seconds_since_epoch());
|
||||
} else {
|
||||
write_error("NOT SET");
|
||||
}
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void write_meta(const osmium::OSMObject& object) {
|
||||
output_formatted("%" PRId64 "\n", object.id());
|
||||
if (m_options.add_metadata) {
|
||||
write_fieldname("version");
|
||||
output_formatted(" %d", object.version());
|
||||
if (object.visible()) {
|
||||
*m_out += " visible\n";
|
||||
} else {
|
||||
write_error(" deleted\n");
|
||||
}
|
||||
write_fieldname("changeset");
|
||||
output_formatted("%d\n", object.changeset());
|
||||
write_fieldname("timestamp");
|
||||
write_timestamp(object.timestamp());
|
||||
write_fieldname("user");
|
||||
output_formatted(" %d ", object.uid());
|
||||
write_string(object.user());
|
||||
*m_out += '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void write_tags(const osmium::TagList& tags, const char* padding="") {
|
||||
if (!tags.empty()) {
|
||||
write_fieldname("tags");
|
||||
*m_out += padding;
|
||||
output_formatted(" %d\n", tags.size());
|
||||
|
||||
osmium::max_op<size_t> max;
|
||||
for (const auto& tag : tags) {
|
||||
max.update(std::strlen(tag.key()));
|
||||
}
|
||||
for (const auto& tag : tags) {
|
||||
*m_out += " ";
|
||||
write_string(tag.key());
|
||||
auto spacing = max() - std::strlen(tag.key());
|
||||
while (spacing--) {
|
||||
*m_out += " ";
|
||||
}
|
||||
*m_out += " = ";
|
||||
write_string(tag.value());
|
||||
*m_out += '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_location(const osmium::Location& location) {
|
||||
write_fieldname("lon/lat");
|
||||
output_formatted(" %.7f,%.7f", location.lon_without_check(), location.lat_without_check());
|
||||
if (!location.valid()) {
|
||||
write_error(" INVALID LOCATION!");
|
||||
}
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void write_box(const osmium::Box& box) {
|
||||
write_fieldname("box l/b/r/t");
|
||||
if (!box) {
|
||||
write_error("BOX NOT SET!\n");
|
||||
return;
|
||||
}
|
||||
const auto& bl = box.bottom_left();
|
||||
const auto& tr = box.top_right();
|
||||
output_formatted("%.7f,%.7f %.7f,%.7f", bl.lon_without_check(), bl.lat_without_check(), tr.lon_without_check(), tr.lat_without_check());
|
||||
if (!box.valid()) {
|
||||
write_error(" INVALID BOX!");
|
||||
}
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
DebugOutputBlock(osmium::memory::Buffer&& buffer, const debug_output_options& options) :
|
||||
OutputBlock(std::move(buffer)),
|
||||
m_options(options),
|
||||
m_utf8_prefix(options.use_color ? color_red : ""),
|
||||
m_utf8_suffix(options.use_color ? color_blue : "") {
|
||||
}
|
||||
|
||||
DebugOutputBlock(const DebugOutputBlock&) = default;
|
||||
DebugOutputBlock& operator=(const DebugOutputBlock&) = default;
|
||||
|
||||
DebugOutputBlock(DebugOutputBlock&&) = default;
|
||||
DebugOutputBlock& operator=(DebugOutputBlock&&) = default;
|
||||
|
||||
~DebugOutputBlock() noexcept = default;
|
||||
|
||||
std::string operator()() {
|
||||
osmium::apply(m_input_buffer->cbegin(), m_input_buffer->cend(), *this);
|
||||
|
||||
std::string out;
|
||||
using std::swap;
|
||||
swap(out, *m_out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void node(const osmium::Node& node) {
|
||||
write_object_type("node", node.visible());
|
||||
write_meta(node);
|
||||
|
||||
if (node.visible()) {
|
||||
write_location(node.location());
|
||||
}
|
||||
|
||||
write_tags(node.tags());
|
||||
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
write_object_type("way", way.visible());
|
||||
write_meta(way);
|
||||
write_tags(way.tags());
|
||||
|
||||
write_fieldname("nodes");
|
||||
|
||||
output_formatted(" %d", way.nodes().size());
|
||||
if (way.nodes().size() < 2) {
|
||||
write_error(" LESS THAN 2 NODES!\n");
|
||||
} else if (way.nodes().size() > 2000) {
|
||||
write_error(" MORE THAN 2000 NODES!\n");
|
||||
} else if (way.nodes().is_closed()) {
|
||||
*m_out += " (closed)\n";
|
||||
} else {
|
||||
*m_out += " (open)\n";
|
||||
}
|
||||
|
||||
int width = int(log10(way.nodes().size())) + 1;
|
||||
int n = 0;
|
||||
for (const auto& node_ref : way.nodes()) {
|
||||
write_counter(width, n++);
|
||||
output_formatted("%10" PRId64, node_ref.ref());
|
||||
if (node_ref.location().valid()) {
|
||||
output_formatted(" (%.7f,%.7f)", node_ref.location().lon_without_check(), node_ref.location().lat_without_check());
|
||||
}
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
static const char* short_typename[] = { "node", "way ", "rel " };
|
||||
write_object_type("relation", relation.visible());
|
||||
write_meta(relation);
|
||||
write_tags(relation.tags());
|
||||
|
||||
write_fieldname("members");
|
||||
output_formatted(" %d\n", relation.members().size());
|
||||
|
||||
int width = int(log10(relation.members().size())) + 1;
|
||||
int n = 0;
|
||||
for (const auto& member : relation.members()) {
|
||||
write_counter(width, n++);
|
||||
*m_out += short_typename[item_type_to_nwr_index(member.type())];
|
||||
output_formatted(" %10" PRId64 " ", member.ref());
|
||||
write_string(member.role());
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void changeset(const osmium::Changeset& changeset) {
|
||||
write_object_type("changeset");
|
||||
output_formatted("%d\n", changeset.id());
|
||||
|
||||
write_fieldname("num changes");
|
||||
output_formatted("%d", changeset.num_changes());
|
||||
if (changeset.num_changes() == 0) {
|
||||
write_error(" NO CHANGES!");
|
||||
}
|
||||
*m_out += '\n';
|
||||
|
||||
write_fieldname("created at");
|
||||
*m_out += ' ';
|
||||
write_timestamp(changeset.created_at());
|
||||
|
||||
write_fieldname("closed at");
|
||||
*m_out += " ";
|
||||
if (changeset.closed()) {
|
||||
write_timestamp(changeset.closed_at());
|
||||
} else {
|
||||
write_error("OPEN!\n");
|
||||
}
|
||||
|
||||
write_fieldname("user");
|
||||
output_formatted(" %d ", changeset.uid());
|
||||
write_string(changeset.user());
|
||||
*m_out += '\n';
|
||||
|
||||
write_box(changeset.bounds());
|
||||
write_tags(changeset.tags(), " ");
|
||||
|
||||
if (changeset.num_comments() > 0) {
|
||||
write_fieldname("comments");
|
||||
output_formatted(" %d\n", changeset.num_comments());
|
||||
|
||||
int width = int(log10(changeset.num_comments())) + 1;
|
||||
int n = 0;
|
||||
for (const auto& comment : changeset.discussion()) {
|
||||
write_counter(width, n++);
|
||||
|
||||
write_comment_field("date");
|
||||
write_timestamp(comment.date());
|
||||
output_formatted(" %*s", width, "");
|
||||
|
||||
write_comment_field("user");
|
||||
output_formatted("%d ", comment.uid());
|
||||
write_string(comment.user());
|
||||
output_formatted("\n %*s", width, "");
|
||||
|
||||
write_comment_field("text");
|
||||
write_string(comment.text());
|
||||
*m_out += '\n';
|
||||
}
|
||||
}
|
||||
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
}; // class DebugOutputBlock
|
||||
|
||||
class DebugOutputFormat : public osmium::io::detail::OutputFormat {
|
||||
|
||||
debug_output_options m_options;
|
||||
|
||||
void write_fieldname(std::string& out, const char* name) {
|
||||
out += " ";
|
||||
if (m_options.use_color) {
|
||||
out += color_cyan;
|
||||
}
|
||||
out += name;
|
||||
if (m_options.use_color) {
|
||||
out += color_reset;
|
||||
}
|
||||
out += ": ";
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
DebugOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) :
|
||||
OutputFormat(output_queue),
|
||||
m_options() {
|
||||
m_options.add_metadata = file.is_not_false("add_metadata");
|
||||
m_options.use_color = file.is_true("color");
|
||||
}
|
||||
|
||||
DebugOutputFormat(const DebugOutputFormat&) = delete;
|
||||
DebugOutputFormat& operator=(const DebugOutputFormat&) = delete;
|
||||
|
||||
~DebugOutputFormat() noexcept final = default;
|
||||
|
||||
void write_header(const osmium::io::Header& header) final {
|
||||
std::string out;
|
||||
|
||||
if (m_options.use_color) {
|
||||
out += color_bold;
|
||||
}
|
||||
out += "header\n";
|
||||
if (m_options.use_color) {
|
||||
out += color_reset;
|
||||
}
|
||||
|
||||
write_fieldname(out, "multiple object versions");
|
||||
out += header.has_multiple_object_versions() ? "yes" : "no";
|
||||
out += '\n';
|
||||
write_fieldname(out, "bounding boxes");
|
||||
out += '\n';
|
||||
for (const auto& box : header.boxes()) {
|
||||
out += " ";
|
||||
box.bottom_left().as_string(std::back_inserter(out), ',');
|
||||
out += " ";
|
||||
box.top_right().as_string(std::back_inserter(out), ',');
|
||||
out += '\n';
|
||||
}
|
||||
write_fieldname(out, "options");
|
||||
out += '\n';
|
||||
for (const auto& opt : header) {
|
||||
out += " ";
|
||||
out += opt.first;
|
||||
out += " = ";
|
||||
out += opt.second;
|
||||
out += '\n';
|
||||
}
|
||||
out += "\n=============================================\n\n";
|
||||
|
||||
send_to_output_queue(std::move(out));
|
||||
}
|
||||
|
||||
void write_buffer(osmium::memory::Buffer&& buffer) final {
|
||||
m_output_queue.push(osmium::thread::Pool::instance().submit(DebugOutputBlock{std::move(buffer), m_options}));
|
||||
}
|
||||
|
||||
}; // class DebugOutputFormat
|
||||
|
||||
// we want the register_output_format() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_debug_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::debug,
|
||||
[](const osmium::io::File& file, future_string_queue_type& output_queue) {
|
||||
return new osmium::io::detail::DebugOutputFormat(file, output_queue);
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_debug_output() noexcept {
|
||||
return registered_debug_output;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_DEBUG_OUTPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,211 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_INPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_INPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <exception>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/io/file.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/entity_bits.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class Parser {
|
||||
|
||||
future_buffer_queue_type& m_output_queue;
|
||||
std::promise<osmium::io::Header>& m_header_promise;
|
||||
queue_wrapper<std::string> m_input_queue;
|
||||
osmium::osm_entity_bits::type m_read_types;
|
||||
bool m_header_is_done;
|
||||
|
||||
protected:
|
||||
|
||||
std::string get_input() {
|
||||
return m_input_queue.pop();
|
||||
}
|
||||
|
||||
bool input_done() const {
|
||||
return m_input_queue.has_reached_end_of_data();
|
||||
}
|
||||
|
||||
osmium::osm_entity_bits::type read_types() const {
|
||||
return m_read_types;
|
||||
}
|
||||
|
||||
bool header_is_done() const {
|
||||
return m_header_is_done;
|
||||
}
|
||||
|
||||
void set_header_value(const osmium::io::Header& header) {
|
||||
if (!m_header_is_done) {
|
||||
m_header_is_done = true;
|
||||
m_header_promise.set_value(header);
|
||||
}
|
||||
}
|
||||
|
||||
void set_header_exception(const std::exception_ptr& exception) {
|
||||
if (!m_header_is_done) {
|
||||
m_header_is_done = true;
|
||||
m_header_promise.set_exception(exception);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the buffer into a future and add it to the output queue.
|
||||
*/
|
||||
void send_to_output_queue(osmium::memory::Buffer&& buffer) {
|
||||
add_to_queue(m_output_queue, std::move(buffer));
|
||||
}
|
||||
|
||||
void send_to_output_queue(std::future<osmium::memory::Buffer>&& future) {
|
||||
m_output_queue.push(std::move(future));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
Parser(future_string_queue_type& input_queue,
|
||||
future_buffer_queue_type& output_queue,
|
||||
std::promise<osmium::io::Header>& header_promise,
|
||||
osmium::osm_entity_bits::type read_types) :
|
||||
m_output_queue(output_queue),
|
||||
m_header_promise(header_promise),
|
||||
m_input_queue(input_queue),
|
||||
m_read_types(read_types),
|
||||
m_header_is_done(false) {
|
||||
}
|
||||
|
||||
Parser(const Parser&) = delete;
|
||||
Parser& operator=(const Parser&) = delete;
|
||||
|
||||
Parser(Parser&&) = delete;
|
||||
Parser& operator=(Parser&&) = delete;
|
||||
|
||||
virtual ~Parser() noexcept = default;
|
||||
|
||||
virtual void run() = 0;
|
||||
|
||||
void parse() {
|
||||
try {
|
||||
run();
|
||||
} catch (...) {
|
||||
std::exception_ptr exception = std::current_exception();
|
||||
set_header_exception(exception);
|
||||
add_to_queue(m_output_queue, std::move(exception));
|
||||
}
|
||||
|
||||
add_end_of_data_to_queue(m_output_queue);
|
||||
}
|
||||
|
||||
}; // class Parser
|
||||
|
||||
/**
|
||||
* This factory class is used to create objects that decode OSM
|
||||
* data written in a specified format.
|
||||
*
|
||||
* Do not use this class directly. Use the osmium::io::Reader
|
||||
* class instead.
|
||||
*/
|
||||
class ParserFactory {
|
||||
|
||||
public:
|
||||
|
||||
typedef std::function<
|
||||
std::unique_ptr<Parser>(
|
||||
future_string_queue_type&,
|
||||
future_buffer_queue_type&,
|
||||
std::promise<osmium::io::Header>& header_promise,
|
||||
osmium::osm_entity_bits::type read_which_entities
|
||||
)
|
||||
> create_parser_type;
|
||||
|
||||
private:
|
||||
|
||||
typedef std::map<osmium::io::file_format, create_parser_type> map_type;
|
||||
|
||||
map_type m_callbacks;
|
||||
|
||||
ParserFactory() :
|
||||
m_callbacks() {
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static ParserFactory& instance() {
|
||||
static ParserFactory factory;
|
||||
return factory;
|
||||
}
|
||||
|
||||
bool register_parser(osmium::io::file_format format, create_parser_type create_function) {
|
||||
if (! m_callbacks.insert(map_type::value_type(format, create_function)).second) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
create_parser_type get_creator_function(const osmium::io::File& file) {
|
||||
auto it = m_callbacks.find(file.format());
|
||||
if (it == m_callbacks.end()) {
|
||||
throw unsupported_file_format_error(
|
||||
std::string("Can not open file '") +
|
||||
file.filename() +
|
||||
"' with type '" +
|
||||
as_string(file.format()) +
|
||||
"'. No support for reading this format in this program.");
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
}; // class ParserFactory
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_INPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,636 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_O5M_INPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_O5M_INPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <protozero/varint.hpp>
|
||||
|
||||
#include <osmium/builder/builder.hpp>
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/io/detail/input_format.hpp>
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/entity_bits.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/thread/util.hpp>
|
||||
#include <osmium/util/cast.hpp>
|
||||
#include <osmium/util/delta.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* Exception thrown when the o5m deocder failed. The exception contains
|
||||
* (if available) information about the place where the error happened
|
||||
* and the type of error.
|
||||
*/
|
||||
struct o5m_error : public io_error {
|
||||
|
||||
explicit o5m_error(const char* what) :
|
||||
io_error(std::string("o5m format error: ") + what) {
|
||||
}
|
||||
|
||||
}; // struct o5m_error
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// Implementation of the o5m/o5c file formats according to the
|
||||
// description at http://wiki.openstreetmap.org/wiki/O5m .
|
||||
|
||||
class ReferenceTable {
|
||||
|
||||
// The following settings are from the o5m description:
|
||||
|
||||
// The maximum number of entries in this table.
|
||||
const uint64_t number_of_entries = 15000;
|
||||
|
||||
// The size of one entry in the table.
|
||||
const unsigned int entry_size = 256;
|
||||
|
||||
// The maximum length of a string in the table including
|
||||
// two \0 bytes.
|
||||
const unsigned int max_length = 250 + 2;
|
||||
|
||||
// The data is stored in this string. It is default constructed
|
||||
// and then resized on demand the first time something is added.
|
||||
// This is done because the ReferenceTable is in a O5mParser
|
||||
// object which will be copied from one thread to another. This
|
||||
// way the string is still small when it is copied.
|
||||
std::string m_table;
|
||||
|
||||
unsigned int current_entry = 0;
|
||||
|
||||
public:
|
||||
|
||||
void clear() {
|
||||
current_entry = 0;
|
||||
}
|
||||
|
||||
void add(const char* string, size_t size) {
|
||||
if (m_table.empty()) {
|
||||
m_table.resize(entry_size * number_of_entries);
|
||||
}
|
||||
if (size <= max_length) {
|
||||
std::copy_n(string, size, &m_table[current_entry * entry_size]);
|
||||
if (++current_entry == number_of_entries) {
|
||||
current_entry = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* get(uint64_t index) const {
|
||||
if (m_table.empty() || index == 0 || index > number_of_entries) {
|
||||
throw o5m_error("reference to non-existing string in table");
|
||||
}
|
||||
auto entry = (current_entry + number_of_entries - index) % number_of_entries;
|
||||
return &m_table[entry * entry_size];
|
||||
}
|
||||
|
||||
}; // class ReferenceTable
|
||||
|
||||
class O5mParser : public Parser {
|
||||
|
||||
static constexpr int buffer_size = 2 * 1000 * 1000;
|
||||
|
||||
osmium::io::Header m_header;
|
||||
|
||||
osmium::memory::Buffer m_buffer;
|
||||
|
||||
std::string m_input;
|
||||
|
||||
const char* m_data;
|
||||
const char* m_end;
|
||||
|
||||
ReferenceTable m_reference_table;
|
||||
|
||||
static int64_t zvarint(const char** data, const char* end) {
|
||||
return protozero::decode_zigzag64(protozero::decode_varint(data, end));
|
||||
}
|
||||
|
||||
bool ensure_bytes_available(size_t need_bytes) {
|
||||
if ((m_end - m_data) >= long(need_bytes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (input_done() && (m_input.size() < need_bytes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_input.erase(0, m_data - m_input.data());
|
||||
|
||||
while (m_input.size() < need_bytes) {
|
||||
std::string data = get_input();
|
||||
if (input_done()) {
|
||||
return false;
|
||||
}
|
||||
m_input.append(data);
|
||||
}
|
||||
|
||||
m_data = m_input.data();
|
||||
m_end = m_input.data() + m_input.size();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_header_magic() {
|
||||
static const unsigned char header_magic[] = { 0xff, 0xe0, 0x04, 'o', '5' };
|
||||
|
||||
if (std::strncmp(reinterpret_cast<const char*>(header_magic), m_data, sizeof(header_magic))) {
|
||||
throw o5m_error("wrong header magic");
|
||||
}
|
||||
|
||||
m_data += sizeof(header_magic);
|
||||
}
|
||||
|
||||
void check_file_type() {
|
||||
if (*m_data == 'm') { // o5m data file
|
||||
m_header.set_has_multiple_object_versions(false);
|
||||
} else if (*m_data == 'c') { // o5c change file
|
||||
m_header.set_has_multiple_object_versions(true);
|
||||
} else {
|
||||
throw o5m_error("wrong header magic");
|
||||
}
|
||||
|
||||
m_data++;
|
||||
}
|
||||
|
||||
void check_file_format_version() {
|
||||
if (*m_data != '2') {
|
||||
throw o5m_error("wrong header magic");
|
||||
}
|
||||
|
||||
m_data++;
|
||||
}
|
||||
|
||||
void decode_header() {
|
||||
if (! ensure_bytes_available(7)) { // overall length of header
|
||||
throw o5m_error("file too short (incomplete header info)");
|
||||
}
|
||||
|
||||
check_header_magic();
|
||||
check_file_type();
|
||||
check_file_format_version();
|
||||
}
|
||||
|
||||
void mark_header_as_done() {
|
||||
set_header_value(m_header);
|
||||
}
|
||||
|
||||
osmium::util::DeltaDecode<osmium::object_id_type> m_delta_id;
|
||||
|
||||
osmium::util::DeltaDecode<int64_t> m_delta_timestamp;
|
||||
osmium::util::DeltaDecode<osmium::changeset_id_type> m_delta_changeset;
|
||||
osmium::util::DeltaDecode<int64_t> m_delta_lon;
|
||||
osmium::util::DeltaDecode<int64_t> m_delta_lat;
|
||||
|
||||
osmium::util::DeltaDecode<osmium::object_id_type> m_delta_way_node_id;
|
||||
osmium::util::DeltaDecode<osmium::object_id_type> m_delta_member_ids[3];
|
||||
|
||||
void reset() {
|
||||
m_reference_table.clear();
|
||||
|
||||
m_delta_id.clear();
|
||||
m_delta_timestamp.clear();
|
||||
m_delta_changeset.clear();
|
||||
m_delta_lon.clear();
|
||||
m_delta_lat.clear();
|
||||
|
||||
m_delta_way_node_id.clear();
|
||||
m_delta_member_ids[0].clear();
|
||||
m_delta_member_ids[1].clear();
|
||||
m_delta_member_ids[2].clear();
|
||||
}
|
||||
|
||||
const char* decode_string(const char** dataptr, const char* const end) {
|
||||
if (**dataptr == 0x00) { // get inline string
|
||||
(*dataptr)++;
|
||||
if (*dataptr == end) {
|
||||
throw o5m_error("string format error");
|
||||
}
|
||||
return *dataptr;
|
||||
} else { // get from reference table
|
||||
auto index = protozero::decode_varint(dataptr, end);
|
||||
return m_reference_table.get(index);
|
||||
}
|
||||
}
|
||||
|
||||
std::pair<osmium::user_id_type, const char*> decode_user(const char** dataptr, const char* const end) {
|
||||
bool update_pointer = (**dataptr == 0x00);
|
||||
const char* data = decode_string(dataptr, end);
|
||||
const char* start = data;
|
||||
|
||||
auto uid = protozero::decode_varint(&data, end);
|
||||
|
||||
if (data == end) {
|
||||
throw o5m_error("missing user name");
|
||||
}
|
||||
|
||||
const char* user = ++data;
|
||||
|
||||
if (uid == 0 && update_pointer) {
|
||||
m_reference_table.add("\0\0", 2);
|
||||
*dataptr = data;
|
||||
return std::make_pair(0, "");
|
||||
}
|
||||
|
||||
while (*data++) {
|
||||
if (data == end) {
|
||||
throw o5m_error("no null byte in user name");
|
||||
}
|
||||
}
|
||||
|
||||
if (update_pointer) {
|
||||
m_reference_table.add(start, data - start);
|
||||
*dataptr = data;
|
||||
}
|
||||
|
||||
return std::make_pair(static_cast_with_assert<osmium::user_id_type>(uid), user);
|
||||
}
|
||||
|
||||
void decode_tags(osmium::builder::Builder* builder, const char** dataptr, const char* const end) {
|
||||
osmium::builder::TagListBuilder tl_builder(m_buffer, builder);
|
||||
|
||||
while(*dataptr != end) {
|
||||
bool update_pointer = (**dataptr == 0x00);
|
||||
const char* data = decode_string(dataptr, end);
|
||||
const char* start = data;
|
||||
|
||||
while (*data++) {
|
||||
if (data == end) {
|
||||
throw o5m_error("no null byte in tag key");
|
||||
}
|
||||
}
|
||||
|
||||
const char* value = data;
|
||||
while (*data++) {
|
||||
if (data == end) {
|
||||
throw o5m_error("no null byte in tag value");
|
||||
}
|
||||
}
|
||||
|
||||
if (update_pointer) {
|
||||
m_reference_table.add(start, data - start);
|
||||
*dataptr = data;
|
||||
}
|
||||
|
||||
tl_builder.add_tag(start, value);
|
||||
}
|
||||
}
|
||||
|
||||
const char* decode_info(osmium::OSMObject& object, const char** dataptr, const char* const end) {
|
||||
const char* user = "";
|
||||
|
||||
if (**dataptr == 0x00) { // no info section
|
||||
++*dataptr;
|
||||
} else { // has info section
|
||||
object.set_version(static_cast_with_assert<object_version_type>(protozero::decode_varint(dataptr, end)));
|
||||
auto timestamp = m_delta_timestamp.update(zvarint(dataptr, end));
|
||||
if (timestamp != 0) { // has timestamp
|
||||
object.set_timestamp(timestamp);
|
||||
object.set_changeset(m_delta_changeset.update(zvarint(dataptr, end)));
|
||||
if (*dataptr != end) {
|
||||
auto uid_user = decode_user(dataptr, end);
|
||||
object.set_uid(uid_user.first);
|
||||
user = uid_user.second;
|
||||
} else {
|
||||
object.set_uid(user_id_type(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
void decode_node(const char* data, const char* const end) {
|
||||
osmium::builder::NodeBuilder builder(m_buffer);
|
||||
osmium::Node& node = builder.object();
|
||||
|
||||
node.set_id(m_delta_id.update(zvarint(&data, end)));
|
||||
|
||||
builder.add_user(decode_info(node, &data, end));
|
||||
|
||||
if (data == end) {
|
||||
// no location, object is deleted
|
||||
builder.object().set_visible(false);
|
||||
builder.object().set_location(osmium::Location{});
|
||||
} else {
|
||||
auto lon = m_delta_lon.update(zvarint(&data, end));
|
||||
auto lat = m_delta_lat.update(zvarint(&data, end));
|
||||
builder.object().set_location(osmium::Location{lon, lat});
|
||||
|
||||
if (data != end) {
|
||||
decode_tags(&builder, &data, end);
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer.commit();
|
||||
}
|
||||
|
||||
void decode_way(const char* data, const char* const end) {
|
||||
osmium::builder::WayBuilder builder(m_buffer);
|
||||
osmium::Way& way = builder.object();
|
||||
|
||||
way.set_id(m_delta_id.update(zvarint(&data, end)));
|
||||
|
||||
builder.add_user(decode_info(way, &data, end));
|
||||
|
||||
if (data == end) {
|
||||
// no reference section, object is deleted
|
||||
builder.object().set_visible(false);
|
||||
} else {
|
||||
auto reference_section_length = protozero::decode_varint(&data, end);
|
||||
if (reference_section_length > 0) {
|
||||
const char* const end_refs = data + reference_section_length;
|
||||
if (end_refs > end) {
|
||||
throw o5m_error("way nodes ref section too long");
|
||||
}
|
||||
|
||||
osmium::builder::WayNodeListBuilder wn_builder(m_buffer, &builder);
|
||||
|
||||
while (data < end_refs) {
|
||||
wn_builder.add_node_ref(m_delta_way_node_id.update(zvarint(&data, end)));
|
||||
}
|
||||
}
|
||||
|
||||
if (data != end) {
|
||||
decode_tags(&builder, &data, end);
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer.commit();
|
||||
}
|
||||
|
||||
osmium::item_type decode_member_type(char c) {
|
||||
if (c < '0' || c > '2') {
|
||||
throw o5m_error("unknown member type");
|
||||
}
|
||||
return osmium::nwr_index_to_item_type(c - '0');
|
||||
}
|
||||
|
||||
std::pair<osmium::item_type, const char*> decode_role(const char** dataptr, const char* const end) {
|
||||
bool update_pointer = (**dataptr == 0x00);
|
||||
const char* data = decode_string(dataptr, end);
|
||||
const char* start = data;
|
||||
|
||||
auto member_type = decode_member_type(*data++);
|
||||
if (data == end) {
|
||||
throw o5m_error("missing role");
|
||||
}
|
||||
const char* role = data;
|
||||
|
||||
while (*data++) {
|
||||
if (data == end) {
|
||||
throw o5m_error("no null byte in role");
|
||||
}
|
||||
}
|
||||
|
||||
if (update_pointer) {
|
||||
m_reference_table.add(start, data - start);
|
||||
*dataptr = data;
|
||||
}
|
||||
|
||||
return std::make_pair(member_type, role);
|
||||
}
|
||||
|
||||
void decode_relation(const char* data, const char* const end) {
|
||||
osmium::builder::RelationBuilder builder(m_buffer);
|
||||
osmium::Relation& relation = builder.object();
|
||||
|
||||
relation.set_id(m_delta_id.update(zvarint(&data, end)));
|
||||
|
||||
builder.add_user(decode_info(relation, &data, end));
|
||||
|
||||
if (data == end) {
|
||||
// no reference section, object is deleted
|
||||
builder.object().set_visible(false);
|
||||
} else {
|
||||
auto reference_section_length = protozero::decode_varint(&data, end);
|
||||
if (reference_section_length > 0) {
|
||||
const char* const end_refs = data + reference_section_length;
|
||||
if (end_refs > end) {
|
||||
throw o5m_error("relation format error");
|
||||
}
|
||||
|
||||
osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder);
|
||||
|
||||
while (data < end_refs) {
|
||||
auto delta_id = zvarint(&data, end);
|
||||
if (data == end) {
|
||||
throw o5m_error("relation member format error");
|
||||
}
|
||||
auto type_role = decode_role(&data, end);
|
||||
auto i = osmium::item_type_to_nwr_index(type_role.first);
|
||||
auto ref = m_delta_member_ids[i].update(delta_id);
|
||||
rml_builder.add_member(type_role.first, ref, type_role.second);
|
||||
}
|
||||
}
|
||||
|
||||
if (data != end) {
|
||||
decode_tags(&builder, &data, end);
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer.commit();
|
||||
}
|
||||
|
||||
void decode_bbox(const char* data, const char* const end) {
|
||||
auto sw_lon = zvarint(&data, end);
|
||||
auto sw_lat = zvarint(&data, end);
|
||||
auto ne_lon = zvarint(&data, end);
|
||||
auto ne_lat = zvarint(&data, end);
|
||||
|
||||
m_header.add_box(osmium::Box{osmium::Location{sw_lon, sw_lat},
|
||||
osmium::Location{ne_lon, ne_lat}});
|
||||
}
|
||||
|
||||
void decode_timestamp(const char* data, const char* const end) {
|
||||
auto timestamp = osmium::Timestamp(zvarint(&data, end)).to_iso();
|
||||
m_header.set("o5m_timestamp", timestamp);
|
||||
m_header.set("timestamp", timestamp);
|
||||
}
|
||||
|
||||
void flush() {
|
||||
osmium::memory::Buffer buffer(buffer_size);
|
||||
using std::swap;
|
||||
swap(m_buffer, buffer);
|
||||
send_to_output_queue(std::move(buffer));
|
||||
}
|
||||
|
||||
enum class dataset_type : unsigned char {
|
||||
node = 0x10,
|
||||
way = 0x11,
|
||||
relation = 0x12,
|
||||
bounding_box = 0xdb,
|
||||
timestamp = 0xdc,
|
||||
header = 0xe0,
|
||||
sync = 0xee,
|
||||
jump = 0xef,
|
||||
reset = 0xff
|
||||
};
|
||||
|
||||
void decode_data() {
|
||||
while (ensure_bytes_available(1)) {
|
||||
dataset_type ds_type = dataset_type(*m_data++);
|
||||
if (ds_type > dataset_type::jump) {
|
||||
if (ds_type == dataset_type::reset) {
|
||||
reset();
|
||||
}
|
||||
} else {
|
||||
ensure_bytes_available(protozero::max_varint_length);
|
||||
|
||||
uint64_t length = 0;
|
||||
try {
|
||||
length = protozero::decode_varint(&m_data, m_end);
|
||||
} catch (protozero::end_of_buffer_exception&) {
|
||||
throw o5m_error("premature end of file");
|
||||
}
|
||||
|
||||
if (! ensure_bytes_available(length)) {
|
||||
throw o5m_error("premature end of file");
|
||||
}
|
||||
|
||||
switch (ds_type) {
|
||||
case dataset_type::node:
|
||||
mark_header_as_done();
|
||||
if (read_types() & osmium::osm_entity_bits::node) {
|
||||
decode_node(m_data, m_data + length);
|
||||
}
|
||||
break;
|
||||
case dataset_type::way:
|
||||
mark_header_as_done();
|
||||
if (read_types() & osmium::osm_entity_bits::way) {
|
||||
decode_way(m_data, m_data + length);
|
||||
}
|
||||
break;
|
||||
case dataset_type::relation:
|
||||
mark_header_as_done();
|
||||
if (read_types() & osmium::osm_entity_bits::relation) {
|
||||
decode_relation(m_data, m_data + length);
|
||||
}
|
||||
break;
|
||||
case dataset_type::bounding_box:
|
||||
decode_bbox(m_data, m_data + length);
|
||||
break;
|
||||
case dataset_type::timestamp:
|
||||
decode_timestamp(m_data, m_data + length);
|
||||
break;
|
||||
default:
|
||||
// ignore unknown datasets
|
||||
break;
|
||||
}
|
||||
|
||||
if (read_types() == osmium::osm_entity_bits::nothing && header_is_done()) {
|
||||
break;
|
||||
}
|
||||
|
||||
m_data += length;
|
||||
|
||||
if (m_buffer.committed() > buffer_size / 10 * 9) {
|
||||
flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (m_buffer.committed()) {
|
||||
flush();
|
||||
}
|
||||
|
||||
mark_header_as_done();
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
O5mParser(future_string_queue_type& input_queue,
|
||||
future_buffer_queue_type& output_queue,
|
||||
std::promise<osmium::io::Header>& header_promise,
|
||||
osmium::osm_entity_bits::type read_types) :
|
||||
Parser(input_queue, output_queue, header_promise, read_types),
|
||||
m_header(),
|
||||
m_buffer(buffer_size),
|
||||
m_input(),
|
||||
m_data(m_input.data()),
|
||||
m_end(m_data) {
|
||||
}
|
||||
|
||||
~O5mParser() noexcept final = default;
|
||||
|
||||
void run() final {
|
||||
osmium::thread::set_thread_name("_osmium_o5m_in");
|
||||
|
||||
decode_header();
|
||||
decode_data();
|
||||
}
|
||||
|
||||
}; // class O5mParser
|
||||
|
||||
// we want the register_parser() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_o5m_parser = ParserFactory::instance().register_parser(
|
||||
file_format::o5m,
|
||||
[](future_string_queue_type& input_queue,
|
||||
future_buffer_queue_type& output_queue,
|
||||
std::promise<osmium::io::Header>& header_promise,
|
||||
osmium::osm_entity_bits::type read_which_entities) {
|
||||
return std::unique_ptr<Parser>(new O5mParser(input_queue, output_queue, header_promise, read_which_entities));
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_o5m_parser() noexcept {
|
||||
return registered_o5m_parser;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_O5M_INPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,261 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_OPL_OUTPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_OPL_OUTPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <future>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/io/detail/output_format.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/memory/collection.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/changeset.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/thread/pool.hpp>
|
||||
#include <osmium/visitor.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
class File;
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct opl_output_options {
|
||||
|
||||
/// Should metadata of objects be added?
|
||||
bool add_metadata;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Writes out one buffer with OSM data in OPL format.
|
||||
*/
|
||||
class OPLOutputBlock : public OutputBlock {
|
||||
|
||||
opl_output_options m_options;
|
||||
|
||||
void append_encoded_string(const char* data) {
|
||||
osmium::io::detail::append_utf8_encoded_string(*m_out, data);
|
||||
}
|
||||
|
||||
void write_meta(const osmium::OSMObject& object) {
|
||||
output_formatted("%" PRId64, object.id());
|
||||
if (m_options.add_metadata) {
|
||||
output_formatted(" v%d d", object.version());
|
||||
*m_out += (object.visible() ? 'V' : 'D');
|
||||
output_formatted(" c%d t", object.changeset());
|
||||
*m_out += object.timestamp().to_iso();
|
||||
output_formatted(" i%d u", object.uid());
|
||||
append_encoded_string(object.user());
|
||||
}
|
||||
*m_out += " T";
|
||||
bool first = true;
|
||||
for (const auto& tag : object.tags()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
*m_out += ',';
|
||||
}
|
||||
append_encoded_string(tag.key());
|
||||
*m_out += '=';
|
||||
append_encoded_string(tag.value());
|
||||
}
|
||||
}
|
||||
|
||||
void write_location(const osmium::Location& location, const char x, const char y) {
|
||||
if (location) {
|
||||
output_formatted(" %c%.7f %c%.7f", x, location.lon_without_check(), y, location.lat_without_check());
|
||||
} else {
|
||||
*m_out += ' ';
|
||||
*m_out += x;
|
||||
*m_out += ' ';
|
||||
*m_out += y;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
OPLOutputBlock(osmium::memory::Buffer&& buffer, const opl_output_options& options) :
|
||||
OutputBlock(std::move(buffer)),
|
||||
m_options(options) {
|
||||
}
|
||||
|
||||
OPLOutputBlock(const OPLOutputBlock&) = default;
|
||||
OPLOutputBlock& operator=(const OPLOutputBlock&) = default;
|
||||
|
||||
OPLOutputBlock(OPLOutputBlock&&) = default;
|
||||
OPLOutputBlock& operator=(OPLOutputBlock&&) = default;
|
||||
|
||||
~OPLOutputBlock() noexcept = default;
|
||||
|
||||
std::string operator()() {
|
||||
osmium::apply(m_input_buffer->cbegin(), m_input_buffer->cend(), *this);
|
||||
|
||||
std::string out;
|
||||
using std::swap;
|
||||
swap(out, *m_out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void node(const osmium::Node& node) {
|
||||
*m_out += 'n';
|
||||
write_meta(node);
|
||||
write_location(node.location(), 'x', 'y');
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
*m_out += 'w';
|
||||
write_meta(way);
|
||||
|
||||
*m_out += " N";
|
||||
bool first = true;
|
||||
for (const auto& node_ref : way.nodes()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
*m_out += ',';
|
||||
}
|
||||
output_formatted("n%" PRId64, node_ref.ref());
|
||||
}
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
*m_out += 'r';
|
||||
write_meta(relation);
|
||||
|
||||
*m_out += " M";
|
||||
bool first = true;
|
||||
for (const auto& member : relation.members()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
*m_out += ',';
|
||||
}
|
||||
*m_out += item_type_to_char(member.type());
|
||||
output_formatted("%" PRId64 "@", member.ref());
|
||||
append_encoded_string(member.role());
|
||||
}
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
void changeset(const osmium::Changeset& changeset) {
|
||||
output_formatted("c%d k%d s", changeset.id(), changeset.num_changes());
|
||||
*m_out += changeset.created_at().to_iso();
|
||||
*m_out += " e";
|
||||
*m_out += changeset.closed_at().to_iso();
|
||||
output_formatted(" d%d i%d u", changeset.num_comments(), changeset.uid());
|
||||
append_encoded_string(changeset.user());
|
||||
write_location(changeset.bounds().bottom_left(), 'x', 'y');
|
||||
write_location(changeset.bounds().top_right(), 'X', 'Y');
|
||||
*m_out += " T";
|
||||
bool first = true;
|
||||
for (const auto& tag : changeset.tags()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
*m_out += ',';
|
||||
}
|
||||
append_encoded_string(tag.key());
|
||||
*m_out += '=';
|
||||
append_encoded_string(tag.value());
|
||||
}
|
||||
|
||||
*m_out += '\n';
|
||||
}
|
||||
|
||||
}; // class OPLOutputBlock
|
||||
|
||||
class OPLOutputFormat : public osmium::io::detail::OutputFormat {
|
||||
|
||||
opl_output_options m_options;
|
||||
|
||||
public:
|
||||
|
||||
OPLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) :
|
||||
OutputFormat(output_queue),
|
||||
m_options() {
|
||||
m_options.add_metadata = file.is_not_false("add_metadata");
|
||||
}
|
||||
|
||||
OPLOutputFormat(const OPLOutputFormat&) = delete;
|
||||
OPLOutputFormat& operator=(const OPLOutputFormat&) = delete;
|
||||
|
||||
~OPLOutputFormat() noexcept final = default;
|
||||
|
||||
void write_buffer(osmium::memory::Buffer&& buffer) final {
|
||||
m_output_queue.push(osmium::thread::Pool::instance().submit(OPLOutputBlock{std::move(buffer), m_options}));
|
||||
}
|
||||
|
||||
}; // class OPLOutputFormat
|
||||
|
||||
// we want the register_output_format() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_opl_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::opl,
|
||||
[](const osmium::io::File& file, future_string_queue_type& output_queue) {
|
||||
return new osmium::io::detail::OPLOutputFormat(file, output_queue);
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_opl_output() noexcept {
|
||||
return registered_opl_output;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_OPL_OUTPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,184 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_OUTPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_OUTPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/handler.hpp>
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/io/detail/string_util.hpp>
|
||||
#include <osmium/io/file.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
class Header;
|
||||
} // namespace io
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class OutputBlock : public osmium::handler::Handler {
|
||||
|
||||
protected:
|
||||
|
||||
std::shared_ptr<osmium::memory::Buffer> m_input_buffer;
|
||||
|
||||
std::shared_ptr<std::string> m_out;
|
||||
|
||||
explicit OutputBlock(osmium::memory::Buffer&& buffer) :
|
||||
m_input_buffer(std::make_shared<osmium::memory::Buffer>(std::move(buffer))),
|
||||
m_out(std::make_shared<std::string>()) {
|
||||
}
|
||||
|
||||
template <typename... TArgs>
|
||||
void output_formatted(const char* format, TArgs&&... args) {
|
||||
append_printf_formatted_string(*m_out, format, std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
}; // class OutputBlock;
|
||||
|
||||
/**
|
||||
* Virtual base class for all classes writing OSM files in different
|
||||
* formats.
|
||||
*
|
||||
* Do not use this class or derived classes directly. Use the
|
||||
* osmium::io::Writer class instead.
|
||||
*/
|
||||
class OutputFormat {
|
||||
|
||||
protected:
|
||||
|
||||
future_string_queue_type& m_output_queue;
|
||||
|
||||
/**
|
||||
* Wrap the string into a future and add it to the output
|
||||
* queue.
|
||||
*/
|
||||
void send_to_output_queue(std::string&& data) {
|
||||
add_to_queue(m_output_queue, std::move(data));
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit OutputFormat(future_string_queue_type& output_queue) :
|
||||
m_output_queue(output_queue) {
|
||||
}
|
||||
|
||||
OutputFormat(const OutputFormat&) = delete;
|
||||
OutputFormat(OutputFormat&&) = delete;
|
||||
|
||||
OutputFormat& operator=(const OutputFormat&) = delete;
|
||||
OutputFormat& operator=(OutputFormat&&) = delete;
|
||||
|
||||
virtual ~OutputFormat() noexcept = default;
|
||||
|
||||
virtual void write_header(const osmium::io::Header&) {
|
||||
}
|
||||
|
||||
virtual void write_buffer(osmium::memory::Buffer&&) = 0;
|
||||
|
||||
virtual void write_end() {
|
||||
}
|
||||
|
||||
}; // class OutputFormat
|
||||
|
||||
/**
|
||||
* This factory class is used to create objects that write OSM data
|
||||
* into a specified output format.
|
||||
*
|
||||
* Do not use this class directly. Instead use the osmium::io::Writer
|
||||
* class.
|
||||
*/
|
||||
class OutputFormatFactory {
|
||||
|
||||
public:
|
||||
|
||||
typedef std::function<osmium::io::detail::OutputFormat*(const osmium::io::File&, future_string_queue_type&)> create_output_type;
|
||||
|
||||
private:
|
||||
|
||||
typedef std::map<osmium::io::file_format, create_output_type> map_type;
|
||||
|
||||
map_type m_callbacks;
|
||||
|
||||
OutputFormatFactory() :
|
||||
m_callbacks() {
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
static OutputFormatFactory& instance() {
|
||||
static OutputFormatFactory factory;
|
||||
return factory;
|
||||
}
|
||||
|
||||
bool register_output_format(osmium::io::file_format format, create_output_type create_function) {
|
||||
if (! m_callbacks.insert(map_type::value_type(format, create_function)).second) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<osmium::io::detail::OutputFormat> create_output(const osmium::io::File& file, future_string_queue_type& output_queue) {
|
||||
auto it = m_callbacks.find(file.format());
|
||||
if (it != m_callbacks.end()) {
|
||||
return std::unique_ptr<osmium::io::detail::OutputFormat>((it->second)(file, output_queue));
|
||||
}
|
||||
|
||||
throw unsupported_file_format_error(
|
||||
std::string("Can not open file '") +
|
||||
file.filename() +
|
||||
"' with type '" +
|
||||
as_string(file.format()) +
|
||||
"'. No support for writing this format in this program.");
|
||||
}
|
||||
|
||||
}; // class OutputFormatFactory
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_OUTPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,89 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_PBF_HPP
|
||||
#define OSMIUM_IO_DETAIL_PBF_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
// needed for htonl and ntohl
|
||||
#ifndef _WIN32
|
||||
# include <netinet/in.h>
|
||||
#else
|
||||
# include <winsock2.h>
|
||||
#endif
|
||||
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* Exception thrown when there was a problem with parsing the PBF format of
|
||||
* a file.
|
||||
*/
|
||||
struct pbf_error : public io_error {
|
||||
|
||||
explicit pbf_error(const std::string& what) :
|
||||
io_error(std::string("PBF error: ") + what) {
|
||||
}
|
||||
|
||||
explicit pbf_error(const char* what) :
|
||||
io_error(std::string("PBF error: ") + what) {
|
||||
}
|
||||
|
||||
}; // struct pbf_error
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// the maximum size of a blob header in bytes
|
||||
const int max_blob_header_size = 64 * 1024; // 64 kB
|
||||
|
||||
// the maximum size of an uncompressed blob in bytes
|
||||
const uint64_t max_uncompressed_blob_size = 32 * 1024 * 1024; // 32 MB
|
||||
|
||||
// resolution for longitude/latitude used for conversion
|
||||
// between representation as double and as int
|
||||
const int64_t lonlat_resolution = 1000 * 1000 * 1000;
|
||||
|
||||
const int64_t resolution_convert = lonlat_resolution / osmium::Location::coordinate_precision;
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_PBF_HPP
|
||||
@@ -0,0 +1,777 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_PBF_DECODER_HPP
|
||||
#define OSMIUM_IO_DETAIL_PBF_DECODER_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <protozero/pbf_message.hpp>
|
||||
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/detail/protobuf_tags.hpp>
|
||||
#include <osmium/io/detail/zlib.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm/entity_bits.hpp>
|
||||
#include <osmium/util/cast.hpp>
|
||||
#include <osmium/util/delta.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
using ptr_len_type = std::pair<const char*, size_t>;
|
||||
using osm_string_len_type = std::pair<const char*, osmium::string_size_type>;
|
||||
|
||||
class PBFPrimitiveBlockDecoder {
|
||||
|
||||
static constexpr size_t initial_buffer_size = 2 * 1024 * 1024;
|
||||
|
||||
ptr_len_type m_data;
|
||||
std::vector<osm_string_len_type> m_stringtable;
|
||||
|
||||
int64_t m_lon_offset = 0;
|
||||
int64_t m_lat_offset = 0;
|
||||
int64_t m_date_factor = 1000;
|
||||
int32_t m_granularity = 100;
|
||||
|
||||
osmium::osm_entity_bits::type m_read_types;
|
||||
|
||||
osmium::memory::Buffer m_buffer { initial_buffer_size };
|
||||
|
||||
void decode_stringtable(const ptr_len_type& data) {
|
||||
if (!m_stringtable.empty()) {
|
||||
throw osmium::pbf_error("more than one stringtable in pbf file");
|
||||
}
|
||||
|
||||
protozero::pbf_message<OSMFormat::StringTable> pbf_string_table(data);
|
||||
while (pbf_string_table.next(OSMFormat::StringTable::repeated_bytes_s)) {
|
||||
auto str_len = pbf_string_table.get_data();
|
||||
if (str_len.second > osmium::max_osm_string_length) {
|
||||
throw osmium::pbf_error("overlong string in string table");
|
||||
}
|
||||
m_stringtable.emplace_back(str_len.first, osmium::string_size_type(str_len.second));
|
||||
}
|
||||
}
|
||||
|
||||
void decode_primitive_block_metadata() {
|
||||
protozero::pbf_message<OSMFormat::PrimitiveBlock> pbf_primitive_block(m_data);
|
||||
while (pbf_primitive_block.next()) {
|
||||
switch (pbf_primitive_block.tag()) {
|
||||
case OSMFormat::PrimitiveBlock::required_StringTable_stringtable:
|
||||
decode_stringtable(pbf_primitive_block.get_data());
|
||||
break;
|
||||
case OSMFormat::PrimitiveBlock::optional_int32_granularity:
|
||||
m_granularity = pbf_primitive_block.get_int32();
|
||||
break;
|
||||
case OSMFormat::PrimitiveBlock::optional_int32_date_granularity:
|
||||
m_date_factor = pbf_primitive_block.get_int32();
|
||||
break;
|
||||
case OSMFormat::PrimitiveBlock::optional_int64_lat_offset:
|
||||
m_lat_offset = pbf_primitive_block.get_int64();
|
||||
break;
|
||||
case OSMFormat::PrimitiveBlock::optional_int64_lon_offset:
|
||||
m_lon_offset = pbf_primitive_block.get_int64();
|
||||
break;
|
||||
default:
|
||||
pbf_primitive_block.skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void decode_primitive_block_data() {
|
||||
protozero::pbf_message<OSMFormat::PrimitiveBlock> pbf_primitive_block(m_data);
|
||||
while (pbf_primitive_block.next(OSMFormat::PrimitiveBlock::repeated_PrimitiveGroup_primitivegroup)) {
|
||||
protozero::pbf_message<OSMFormat::PrimitiveGroup> pbf_primitive_group = pbf_primitive_block.get_message();
|
||||
while (pbf_primitive_group.next()) {
|
||||
switch (pbf_primitive_group.tag()) {
|
||||
case OSMFormat::PrimitiveGroup::repeated_Node_nodes:
|
||||
if (m_read_types & osmium::osm_entity_bits::node) {
|
||||
decode_node(pbf_primitive_group.get_data());
|
||||
} else {
|
||||
pbf_primitive_group.skip();
|
||||
}
|
||||
break;
|
||||
case OSMFormat::PrimitiveGroup::optional_DenseNodes_dense:
|
||||
if (m_read_types & osmium::osm_entity_bits::node) {
|
||||
decode_dense_nodes(pbf_primitive_group.get_data());
|
||||
} else {
|
||||
pbf_primitive_group.skip();
|
||||
}
|
||||
break;
|
||||
case OSMFormat::PrimitiveGroup::repeated_Way_ways:
|
||||
if (m_read_types & osmium::osm_entity_bits::way) {
|
||||
decode_way(pbf_primitive_group.get_data());
|
||||
} else {
|
||||
pbf_primitive_group.skip();
|
||||
}
|
||||
break;
|
||||
case OSMFormat::PrimitiveGroup::repeated_Relation_relations:
|
||||
if (m_read_types & osmium::osm_entity_bits::relation) {
|
||||
decode_relation(pbf_primitive_group.get_data());
|
||||
} else {
|
||||
pbf_primitive_group.skip();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
pbf_primitive_group.skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
osm_string_len_type decode_info(const ptr_len_type& data, osmium::OSMObject& object) {
|
||||
osm_string_len_type user = std::make_pair("", 0);
|
||||
|
||||
protozero::pbf_message<OSMFormat::Info> pbf_info(data);
|
||||
while (pbf_info.next()) {
|
||||
switch (pbf_info.tag()) {
|
||||
case OSMFormat::Info::optional_int32_version:
|
||||
{
|
||||
auto version = pbf_info.get_int32();
|
||||
if (version < 0) {
|
||||
throw osmium::pbf_error("object version must not be negative");
|
||||
}
|
||||
object.set_version(static_cast_with_assert<object_version_type>(version));
|
||||
}
|
||||
break;
|
||||
case OSMFormat::Info::optional_int64_timestamp:
|
||||
object.set_timestamp(pbf_info.get_int64() * m_date_factor / 1000);
|
||||
break;
|
||||
case OSMFormat::Info::optional_int64_changeset:
|
||||
{
|
||||
auto changeset_id = pbf_info.get_int64();
|
||||
if (changeset_id < 0) {
|
||||
throw osmium::pbf_error("object changeset_id must not be negative");
|
||||
}
|
||||
object.set_changeset(static_cast_with_assert<changeset_id_type>(changeset_id));
|
||||
}
|
||||
break;
|
||||
case OSMFormat::Info::optional_int32_uid:
|
||||
object.set_uid_from_signed(pbf_info.get_int32());
|
||||
break;
|
||||
case OSMFormat::Info::optional_uint32_user_sid:
|
||||
user = m_stringtable.at(pbf_info.get_uint32());
|
||||
break;
|
||||
case OSMFormat::Info::optional_bool_visible:
|
||||
object.set_visible(pbf_info.get_bool());
|
||||
break;
|
||||
default:
|
||||
pbf_info.skip();
|
||||
}
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
using kv_type = std::pair<protozero::pbf_reader::const_uint32_iterator, protozero::pbf_reader::const_uint32_iterator>;
|
||||
|
||||
void build_tag_list(osmium::builder::Builder& builder, const kv_type& keys, const kv_type& vals) {
|
||||
if (keys.first != keys.second) {
|
||||
osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
|
||||
auto kit = keys.first;
|
||||
auto vit = vals.first;
|
||||
while (kit != keys.second) {
|
||||
if (vit == vals.second) {
|
||||
// this is against the spec, must have same number of elements
|
||||
throw osmium::pbf_error("PBF format error");
|
||||
}
|
||||
const auto& k = m_stringtable.at(*kit++);
|
||||
const auto& v = m_stringtable.at(*vit++);
|
||||
tl_builder.add_tag(k.first, k.second, v.first, v.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int32_t convert_pbf_coordinate(int64_t c) const {
|
||||
return int32_t((c * m_granularity + m_lon_offset) / resolution_convert);
|
||||
}
|
||||
|
||||
void decode_node(const ptr_len_type& data) {
|
||||
osmium::builder::NodeBuilder builder(m_buffer);
|
||||
osmium::Node& node = builder.object();
|
||||
|
||||
kv_type keys;
|
||||
kv_type vals;
|
||||
int64_t lon = std::numeric_limits<int64_t>::max();
|
||||
int64_t lat = std::numeric_limits<int64_t>::max();
|
||||
|
||||
osm_string_len_type user = { "", 0 };
|
||||
|
||||
protozero::pbf_message<OSMFormat::Node> pbf_node(data);
|
||||
while (pbf_node.next()) {
|
||||
switch (pbf_node.tag()) {
|
||||
case OSMFormat::Node::required_sint64_id:
|
||||
node.set_id(pbf_node.get_sint64());
|
||||
break;
|
||||
case OSMFormat::Node::packed_uint32_keys:
|
||||
keys = pbf_node.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Node::packed_uint32_vals:
|
||||
vals = pbf_node.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Node::optional_Info_info:
|
||||
user = decode_info(pbf_node.get_data(), builder.object());
|
||||
break;
|
||||
case OSMFormat::Node::required_sint64_lat:
|
||||
lat = pbf_node.get_sint64();
|
||||
break;
|
||||
case OSMFormat::Node::required_sint64_lon:
|
||||
lon = pbf_node.get_sint64();
|
||||
break;
|
||||
default:
|
||||
pbf_node.skip();
|
||||
}
|
||||
}
|
||||
|
||||
if (node.visible()) {
|
||||
if (lon == std::numeric_limits<int64_t>::max() ||
|
||||
lat == std::numeric_limits<int64_t>::max()) {
|
||||
throw osmium::pbf_error("illegal coordinate format");
|
||||
}
|
||||
node.set_location(osmium::Location(
|
||||
convert_pbf_coordinate(lon),
|
||||
convert_pbf_coordinate(lat)
|
||||
));
|
||||
}
|
||||
|
||||
builder.add_user(user.first, user.second);
|
||||
|
||||
build_tag_list(builder, keys, vals);
|
||||
|
||||
m_buffer.commit();
|
||||
}
|
||||
|
||||
void decode_way(const ptr_len_type& data) {
|
||||
osmium::builder::WayBuilder builder(m_buffer);
|
||||
|
||||
kv_type keys;
|
||||
kv_type vals;
|
||||
std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> refs;
|
||||
|
||||
osm_string_len_type user = { "", 0 };
|
||||
|
||||
protozero::pbf_message<OSMFormat::Way> pbf_way(data);
|
||||
while (pbf_way.next()) {
|
||||
switch (pbf_way.tag()) {
|
||||
case OSMFormat::Way::required_int64_id:
|
||||
builder.object().set_id(pbf_way.get_int64());
|
||||
break;
|
||||
case OSMFormat::Way::packed_uint32_keys:
|
||||
keys = pbf_way.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Way::packed_uint32_vals:
|
||||
vals = pbf_way.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Way::optional_Info_info:
|
||||
user = decode_info(pbf_way.get_data(), builder.object());
|
||||
break;
|
||||
case OSMFormat::Way::packed_sint64_refs:
|
||||
refs = pbf_way.get_packed_sint64();
|
||||
break;
|
||||
default:
|
||||
pbf_way.skip();
|
||||
}
|
||||
}
|
||||
|
||||
builder.add_user(user.first, user.second);
|
||||
|
||||
if (refs.first != refs.second) {
|
||||
osmium::builder::WayNodeListBuilder wnl_builder(m_buffer, &builder);
|
||||
osmium::util::DeltaDecode<int64_t> ref;
|
||||
while (refs.first != refs.second) {
|
||||
wnl_builder.add_node_ref(ref.update(*refs.first++));
|
||||
}
|
||||
}
|
||||
|
||||
build_tag_list(builder, keys, vals);
|
||||
|
||||
m_buffer.commit();
|
||||
}
|
||||
|
||||
void decode_relation(const ptr_len_type& data) {
|
||||
osmium::builder::RelationBuilder builder(m_buffer);
|
||||
|
||||
kv_type keys;
|
||||
kv_type vals;
|
||||
std::pair<protozero::pbf_reader::const_int32_iterator, protozero::pbf_reader::const_int32_iterator> roles;
|
||||
std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> refs;
|
||||
std::pair<protozero::pbf_reader::const_int32_iterator, protozero::pbf_reader::const_int32_iterator> types;
|
||||
|
||||
osm_string_len_type user = { "", 0 };
|
||||
|
||||
protozero::pbf_message<OSMFormat::Relation> pbf_relation(data);
|
||||
while (pbf_relation.next()) {
|
||||
switch (pbf_relation.tag()) {
|
||||
case OSMFormat::Relation::required_int64_id:
|
||||
builder.object().set_id(pbf_relation.get_int64());
|
||||
break;
|
||||
case OSMFormat::Relation::packed_uint32_keys:
|
||||
keys = pbf_relation.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Relation::packed_uint32_vals:
|
||||
vals = pbf_relation.get_packed_uint32();
|
||||
break;
|
||||
case OSMFormat::Relation::optional_Info_info:
|
||||
user = decode_info(pbf_relation.get_data(), builder.object());
|
||||
break;
|
||||
case OSMFormat::Relation::packed_int32_roles_sid:
|
||||
roles = pbf_relation.get_packed_int32();
|
||||
break;
|
||||
case OSMFormat::Relation::packed_sint64_memids:
|
||||
refs = pbf_relation.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::Relation::packed_MemberType_types:
|
||||
types = pbf_relation.get_packed_enum();
|
||||
break;
|
||||
default:
|
||||
pbf_relation.skip();
|
||||
}
|
||||
}
|
||||
|
||||
builder.add_user(user.first, user.second);
|
||||
|
||||
if (refs.first != refs.second) {
|
||||
osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder);
|
||||
osmium::util::DeltaDecode<int64_t> ref;
|
||||
while (roles.first != roles.second && refs.first != refs.second && types.first != types.second) {
|
||||
const auto& r = m_stringtable.at(*roles.first++);
|
||||
int type = *types.first++;
|
||||
if (type < 0 || type > 2) {
|
||||
throw osmium::pbf_error("unknown relation member type");
|
||||
}
|
||||
rml_builder.add_member(
|
||||
osmium::item_type(type + 1),
|
||||
ref.update(*refs.first++),
|
||||
r.first,
|
||||
r.second
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
build_tag_list(builder, keys, vals);
|
||||
|
||||
m_buffer.commit();
|
||||
}
|
||||
|
||||
void decode_dense_nodes(const ptr_len_type& data) {
|
||||
bool has_info = false;
|
||||
bool has_visibles = false;
|
||||
|
||||
std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> ids;
|
||||
std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> lats;
|
||||
std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> lons;
|
||||
|
||||
std::pair<protozero::pbf_reader::const_int32_iterator, protozero::pbf_reader::const_int32_iterator> tags;
|
||||
|
||||
std::pair<protozero::pbf_reader::const_int32_iterator, protozero::pbf_reader::const_int32_iterator> versions;
|
||||
std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> timestamps;
|
||||
std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> changesets;
|
||||
std::pair<protozero::pbf_reader::const_sint32_iterator, protozero::pbf_reader::const_sint32_iterator> uids;
|
||||
std::pair<protozero::pbf_reader::const_sint32_iterator, protozero::pbf_reader::const_sint32_iterator> user_sids;
|
||||
std::pair<protozero::pbf_reader::const_int32_iterator, protozero::pbf_reader::const_int32_iterator> visibles;
|
||||
|
||||
protozero::pbf_message<OSMFormat::DenseNodes> pbf_dense_nodes(data);
|
||||
while (pbf_dense_nodes.next()) {
|
||||
switch (pbf_dense_nodes.tag()) {
|
||||
case OSMFormat::DenseNodes::packed_sint64_id:
|
||||
ids = pbf_dense_nodes.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseNodes::optional_DenseInfo_denseinfo:
|
||||
{
|
||||
has_info = true;
|
||||
protozero::pbf_message<OSMFormat::DenseInfo> pbf_dense_info = pbf_dense_nodes.get_message();
|
||||
while (pbf_dense_info.next()) {
|
||||
switch (pbf_dense_info.tag()) {
|
||||
case OSMFormat::DenseInfo::packed_int32_version:
|
||||
versions = pbf_dense_info.get_packed_int32();
|
||||
break;
|
||||
case OSMFormat::DenseInfo::packed_sint64_timestamp:
|
||||
timestamps = pbf_dense_info.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseInfo::packed_sint64_changeset:
|
||||
changesets = pbf_dense_info.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseInfo::packed_sint32_uid:
|
||||
uids = pbf_dense_info.get_packed_sint32();
|
||||
break;
|
||||
case OSMFormat::DenseInfo::packed_sint32_user_sid:
|
||||
user_sids = pbf_dense_info.get_packed_sint32();
|
||||
break;
|
||||
case OSMFormat::DenseInfo::packed_bool_visible:
|
||||
has_visibles = true;
|
||||
visibles = pbf_dense_info.get_packed_bool();
|
||||
break;
|
||||
default:
|
||||
pbf_dense_info.skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OSMFormat::DenseNodes::packed_sint64_lat:
|
||||
lats = pbf_dense_nodes.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseNodes::packed_sint64_lon:
|
||||
lons = pbf_dense_nodes.get_packed_sint64();
|
||||
break;
|
||||
case OSMFormat::DenseNodes::packed_int32_keys_vals:
|
||||
tags = pbf_dense_nodes.get_packed_int32();
|
||||
break;
|
||||
default:
|
||||
pbf_dense_nodes.skip();
|
||||
}
|
||||
}
|
||||
|
||||
osmium::util::DeltaDecode<int64_t> dense_id;
|
||||
osmium::util::DeltaDecode<int64_t> dense_latitude;
|
||||
osmium::util::DeltaDecode<int64_t> dense_longitude;
|
||||
osmium::util::DeltaDecode<int64_t> dense_uid;
|
||||
osmium::util::DeltaDecode<int64_t> dense_user_sid;
|
||||
osmium::util::DeltaDecode<int64_t> dense_changeset;
|
||||
osmium::util::DeltaDecode<int64_t> dense_timestamp;
|
||||
|
||||
auto tag_it = tags.first;
|
||||
|
||||
while (ids.first != ids.second) {
|
||||
if (lons.first == lons.second ||
|
||||
lats.first == lats.second) {
|
||||
// this is against the spec, must have same number of elements
|
||||
throw osmium::pbf_error("PBF format error");
|
||||
}
|
||||
|
||||
bool visible = true;
|
||||
|
||||
osmium::builder::NodeBuilder builder(m_buffer);
|
||||
osmium::Node& node = builder.object();
|
||||
|
||||
node.set_id(dense_id.update(*ids.first++));
|
||||
|
||||
if (has_info) {
|
||||
if (versions.first == versions.second ||
|
||||
changesets.first == changesets.second ||
|
||||
timestamps.first == timestamps.second ||
|
||||
uids.first == uids.second ||
|
||||
user_sids.first == user_sids.second) {
|
||||
// this is against the spec, must have same number of elements
|
||||
throw osmium::pbf_error("PBF format error");
|
||||
}
|
||||
|
||||
auto version = *versions.first++;
|
||||
if (version < 0) {
|
||||
throw osmium::pbf_error("object version must not be negative");
|
||||
}
|
||||
node.set_version(static_cast<osmium::object_version_type>(version));
|
||||
|
||||
auto changeset_id = dense_changeset.update(*changesets.first++);
|
||||
if (changeset_id < 0) {
|
||||
throw osmium::pbf_error("object changeset_id must not be negative");
|
||||
}
|
||||
node.set_changeset(static_cast<osmium::changeset_id_type>(changeset_id));
|
||||
|
||||
node.set_timestamp(dense_timestamp.update(*timestamps.first++) * m_date_factor / 1000);
|
||||
node.set_uid_from_signed(static_cast<osmium::signed_user_id_type>(dense_uid.update(*uids.first++)));
|
||||
|
||||
if (has_visibles) {
|
||||
if (visibles.first == visibles.second) {
|
||||
// this is against the spec, must have same number of elements
|
||||
throw osmium::pbf_error("PBF format error");
|
||||
}
|
||||
visible = (*visibles.first++) != 0;
|
||||
}
|
||||
node.set_visible(visible);
|
||||
|
||||
const auto& u = m_stringtable.at(dense_user_sid.update(*user_sids.first++));
|
||||
builder.add_user(u.first, u.second);
|
||||
} else {
|
||||
builder.add_user("");
|
||||
}
|
||||
|
||||
// even if the node isn't visible, there's still a record
|
||||
// of its lat/lon in the dense arrays.
|
||||
const auto lon = dense_longitude.update(*lons.first++);
|
||||
const auto lat = dense_latitude.update(*lats.first++);
|
||||
if (visible) {
|
||||
builder.object().set_location(osmium::Location(
|
||||
convert_pbf_coordinate(lon),
|
||||
convert_pbf_coordinate(lat)
|
||||
));
|
||||
}
|
||||
|
||||
if (tag_it != tags.second) {
|
||||
osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
|
||||
while (tag_it != tags.second && *tag_it != 0) {
|
||||
const auto& k = m_stringtable.at(*tag_it++);
|
||||
if (tag_it == tags.second) {
|
||||
throw osmium::pbf_error("PBF format error"); // this is against the spec, keys/vals must come in pairs
|
||||
}
|
||||
const auto& v = m_stringtable.at(*tag_it++);
|
||||
tl_builder.add_tag(k.first, k.second, v.first, v.second);
|
||||
}
|
||||
|
||||
if (tag_it != tags.second) {
|
||||
++tag_it;
|
||||
}
|
||||
}
|
||||
|
||||
m_buffer.commit();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
PBFPrimitiveBlockDecoder(const ptr_len_type& data, osmium::osm_entity_bits::type read_types) :
|
||||
m_data(data),
|
||||
m_read_types(read_types) {
|
||||
}
|
||||
|
||||
PBFPrimitiveBlockDecoder(const PBFPrimitiveBlockDecoder&) = delete;
|
||||
PBFPrimitiveBlockDecoder& operator=(const PBFPrimitiveBlockDecoder&) = delete;
|
||||
|
||||
PBFPrimitiveBlockDecoder(PBFPrimitiveBlockDecoder&&) = delete;
|
||||
PBFPrimitiveBlockDecoder& operator=(PBFPrimitiveBlockDecoder&&) = delete;
|
||||
|
||||
~PBFPrimitiveBlockDecoder() noexcept = default;
|
||||
|
||||
osmium::memory::Buffer operator()() {
|
||||
try {
|
||||
decode_primitive_block_metadata();
|
||||
decode_primitive_block_data();
|
||||
} catch (std::out_of_range&) {
|
||||
throw osmium::pbf_error("string id out of range");
|
||||
}
|
||||
|
||||
return std::move(m_buffer);
|
||||
}
|
||||
|
||||
}; // class PBFPrimitiveBlockDecoder
|
||||
|
||||
inline ptr_len_type decode_blob(const std::string& blob_data, std::string& output) {
|
||||
int32_t raw_size = 0;
|
||||
std::pair<const char*, protozero::pbf_length_type> zlib_data = {nullptr, 0};
|
||||
|
||||
protozero::pbf_message<FileFormat::Blob> pbf_blob(blob_data);
|
||||
while (pbf_blob.next()) {
|
||||
switch (pbf_blob.tag()) {
|
||||
case FileFormat::Blob::optional_bytes_raw:
|
||||
{
|
||||
auto data_len = pbf_blob.get_data();
|
||||
if (data_len.second > max_uncompressed_blob_size) {
|
||||
throw osmium::pbf_error("illegal blob size");
|
||||
}
|
||||
return data_len;
|
||||
}
|
||||
case FileFormat::Blob::optional_int32_raw_size:
|
||||
raw_size = pbf_blob.get_int32();
|
||||
if (raw_size <= 0 || uint32_t(raw_size) > max_uncompressed_blob_size) {
|
||||
throw osmium::pbf_error("illegal blob size");
|
||||
}
|
||||
break;
|
||||
case FileFormat::Blob::optional_bytes_zlib_data:
|
||||
zlib_data = pbf_blob.get_data();
|
||||
break;
|
||||
case FileFormat::Blob::optional_bytes_lzma_data:
|
||||
throw osmium::pbf_error("lzma blobs not implemented");
|
||||
default:
|
||||
throw osmium::pbf_error("unknown compression");
|
||||
}
|
||||
}
|
||||
|
||||
if (zlib_data.second != 0 && raw_size != 0) {
|
||||
return osmium::io::detail::zlib_uncompress_string(
|
||||
zlib_data.first,
|
||||
static_cast<unsigned long>(zlib_data.second),
|
||||
static_cast<unsigned long>(raw_size),
|
||||
output
|
||||
);
|
||||
}
|
||||
|
||||
throw osmium::pbf_error("blob contains no data");
|
||||
}
|
||||
|
||||
inline osmium::Box decode_header_bbox(const ptr_len_type& data) {
|
||||
int64_t left = std::numeric_limits<int64_t>::max();
|
||||
int64_t right = std::numeric_limits<int64_t>::max();
|
||||
int64_t top = std::numeric_limits<int64_t>::max();
|
||||
int64_t bottom = std::numeric_limits<int64_t>::max();
|
||||
|
||||
protozero::pbf_message<OSMFormat::HeaderBBox> pbf_header_bbox(data);
|
||||
while (pbf_header_bbox.next()) {
|
||||
switch (pbf_header_bbox.tag()) {
|
||||
case OSMFormat::HeaderBBox::required_sint64_left:
|
||||
left = pbf_header_bbox.get_sint64();
|
||||
break;
|
||||
case OSMFormat::HeaderBBox::required_sint64_right:
|
||||
right = pbf_header_bbox.get_sint64();
|
||||
break;
|
||||
case OSMFormat::HeaderBBox::required_sint64_top:
|
||||
top = pbf_header_bbox.get_sint64();
|
||||
break;
|
||||
case OSMFormat::HeaderBBox::required_sint64_bottom:
|
||||
bottom = pbf_header_bbox.get_sint64();
|
||||
break;
|
||||
default:
|
||||
pbf_header_bbox.skip();
|
||||
}
|
||||
}
|
||||
|
||||
if (left == std::numeric_limits<int64_t>::max() ||
|
||||
right == std::numeric_limits<int64_t>::max() ||
|
||||
top == std::numeric_limits<int64_t>::max() ||
|
||||
bottom == std::numeric_limits<int64_t>::max()) {
|
||||
throw osmium::pbf_error("invalid bbox");
|
||||
}
|
||||
|
||||
osmium::Box box;
|
||||
box.extend(osmium::Location(left / resolution_convert, bottom / resolution_convert));
|
||||
box.extend(osmium::Location(right / resolution_convert, top / resolution_convert));
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
inline osmium::io::Header decode_header_block(const ptr_len_type& data) {
|
||||
osmium::io::Header header;
|
||||
int i = 0;
|
||||
|
||||
protozero::pbf_message<OSMFormat::HeaderBlock> pbf_header_block(data);
|
||||
while (pbf_header_block.next()) {
|
||||
switch (pbf_header_block.tag()) {
|
||||
case OSMFormat::HeaderBlock::optional_HeaderBBox_bbox:
|
||||
header.add_box(decode_header_bbox(pbf_header_block.get_data()));
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::repeated_string_required_features:
|
||||
{
|
||||
auto feature = pbf_header_block.get_data();
|
||||
if (!strncmp("OsmSchema-V0.6", feature.first, feature.second)) {
|
||||
// intentionally left blank
|
||||
} else if (!strncmp("DenseNodes", feature.first, feature.second)) {
|
||||
header.set("pbf_dense_nodes", true);
|
||||
} else if (!strncmp("HistoricalInformation", feature.first, feature.second)) {
|
||||
header.set_has_multiple_object_versions(true);
|
||||
} else {
|
||||
std::string msg("required feature not supported: ");
|
||||
msg.append(feature.first, feature.second);
|
||||
throw osmium::pbf_error(msg);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::repeated_string_optional_features:
|
||||
header.set("pbf_optional_feature_" + std::to_string(i++), pbf_header_block.get_string());
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::optional_string_writingprogram:
|
||||
header.set("generator", pbf_header_block.get_string());
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp:
|
||||
{
|
||||
auto timestamp = osmium::Timestamp(pbf_header_block.get_int64()).to_iso();
|
||||
header.set("osmosis_replication_timestamp", timestamp);
|
||||
header.set("timestamp", timestamp);
|
||||
}
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::optional_int64_osmosis_replication_sequence_number:
|
||||
header.set("osmosis_replication_sequence_number", std::to_string(pbf_header_block.get_int64()));
|
||||
break;
|
||||
case OSMFormat::HeaderBlock::optional_string_osmosis_replication_base_url:
|
||||
header.set("osmosis_replication_base_url", pbf_header_block.get_string());
|
||||
break;
|
||||
default:
|
||||
pbf_header_block.skip();
|
||||
}
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode HeaderBlock.
|
||||
*
|
||||
* @param header_block_data Input data
|
||||
* @returns Header object
|
||||
* @throws osmium::pbf_error If there was a parsing error
|
||||
*/
|
||||
inline osmium::io::Header decode_header(const std::string& header_block_data) {
|
||||
std::string output;
|
||||
|
||||
return decode_header_block(decode_blob(header_block_data, output));
|
||||
}
|
||||
|
||||
class PBFDataBlobDecoder {
|
||||
|
||||
std::shared_ptr<std::string> m_input_buffer;
|
||||
osmium::osm_entity_bits::type m_read_types;
|
||||
|
||||
public:
|
||||
|
||||
PBFDataBlobDecoder(std::string&& input_buffer, osmium::osm_entity_bits::type read_types) :
|
||||
m_input_buffer(std::make_shared<std::string>(std::move(input_buffer))),
|
||||
m_read_types(read_types) {
|
||||
}
|
||||
|
||||
PBFDataBlobDecoder(const PBFDataBlobDecoder&) = default;
|
||||
PBFDataBlobDecoder& operator=(const PBFDataBlobDecoder&) = default;
|
||||
|
||||
PBFDataBlobDecoder(PBFDataBlobDecoder&&) = default;
|
||||
PBFDataBlobDecoder& operator=(PBFDataBlobDecoder&&) = default;
|
||||
|
||||
~PBFDataBlobDecoder() noexcept = default;
|
||||
|
||||
osmium::memory::Buffer operator()() {
|
||||
std::string output;
|
||||
PBFPrimitiveBlockDecoder decoder(decode_blob(*m_input_buffer, output), m_read_types);
|
||||
return decoder();
|
||||
}
|
||||
|
||||
}; // class PBFDataBlobDecoder
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_PBF_DECODER_HPP
|
||||
@@ -0,0 +1,242 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_PBF_INPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_PBF_INPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
|
||||
#include <protozero/pbf_message.hpp>
|
||||
|
||||
#include <osmium/io/detail/input_format.hpp>
|
||||
#include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/detail/pbf_decoder.hpp>
|
||||
#include <osmium/io/detail/protobuf_tags.hpp>
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/io/file.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/osm.hpp>
|
||||
#include <osmium/osm/entity_bits.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/thread/pool.hpp>
|
||||
#include <osmium/thread/util.hpp>
|
||||
#include <osmium/util/config.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class PBFParser : public Parser {
|
||||
|
||||
std::string m_input_buffer;
|
||||
|
||||
/**
|
||||
* Read the given number of bytes from the input queue.
|
||||
*
|
||||
* @param size Number of bytes to read
|
||||
* @returns String with the data
|
||||
* @throws osmium::pbf_error If size bytes can't be read
|
||||
*/
|
||||
std::string read_from_input_queue(size_t size) {
|
||||
while (m_input_buffer.size() < size) {
|
||||
std::string new_data = get_input();
|
||||
if (input_done()) {
|
||||
throw osmium::pbf_error("truncated data (EOF encountered)");
|
||||
}
|
||||
m_input_buffer += new_data;
|
||||
}
|
||||
|
||||
std::string output { m_input_buffer.substr(size) };
|
||||
m_input_buffer.resize(size);
|
||||
|
||||
using std::swap;
|
||||
swap(output, m_input_buffer);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read 4 bytes in network byte order from file. They contain
|
||||
* the length of the following BlobHeader.
|
||||
*/
|
||||
uint32_t read_blob_header_size_from_file() {
|
||||
uint32_t size_in_network_byte_order;
|
||||
|
||||
try {
|
||||
const std::string input_data = read_from_input_queue(sizeof(size_in_network_byte_order));
|
||||
size_in_network_byte_order = *reinterpret_cast<const uint32_t*>(input_data.data());
|
||||
} catch (osmium::pbf_error&) {
|
||||
return 0; // EOF
|
||||
}
|
||||
|
||||
const uint32_t size = ntohl(size_in_network_byte_order);
|
||||
if (size > static_cast<uint32_t>(max_blob_header_size)) {
|
||||
throw osmium::pbf_error("invalid BlobHeader size (> max_blob_header_size)");
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the BlobHeader. Make sure it contains the expected
|
||||
* type. Return the size of the following Blob.
|
||||
*/
|
||||
size_t decode_blob_header(protozero::pbf_message<FileFormat::BlobHeader>&& pbf_blob_header, const char* expected_type) {
|
||||
std::pair<const char*, size_t> blob_header_type;
|
||||
size_t blob_header_datasize = 0;
|
||||
|
||||
while (pbf_blob_header.next()) {
|
||||
switch (pbf_blob_header.tag()) {
|
||||
case FileFormat::BlobHeader::required_string_type:
|
||||
blob_header_type = pbf_blob_header.get_data();
|
||||
break;
|
||||
case FileFormat::BlobHeader::required_int32_datasize:
|
||||
blob_header_datasize = pbf_blob_header.get_int32();
|
||||
break;
|
||||
default:
|
||||
pbf_blob_header.skip();
|
||||
}
|
||||
}
|
||||
|
||||
if (blob_header_datasize == 0) {
|
||||
throw osmium::pbf_error("PBF format error: BlobHeader.datasize missing or zero.");
|
||||
}
|
||||
|
||||
if (strncmp(expected_type, blob_header_type.first, blob_header_type.second)) {
|
||||
throw osmium::pbf_error("blob does not have expected type (OSMHeader in first blob, OSMData in following blobs)");
|
||||
}
|
||||
|
||||
return blob_header_datasize;
|
||||
}
|
||||
|
||||
size_t check_type_and_get_blob_size(const char* expected_type) {
|
||||
assert(expected_type);
|
||||
|
||||
const auto size = read_blob_header_size_from_file();
|
||||
if (size == 0) { // EOF
|
||||
return 0;
|
||||
}
|
||||
|
||||
const std::string blob_header = read_from_input_queue(size);
|
||||
|
||||
return decode_blob_header(protozero::pbf_message<FileFormat::BlobHeader>(blob_header), expected_type);
|
||||
}
|
||||
|
||||
std::string read_from_input_queue_with_check(size_t size) {
|
||||
if (size > max_uncompressed_blob_size) {
|
||||
throw osmium::pbf_error(std::string("invalid blob size: " +
|
||||
std::to_string(size)));
|
||||
}
|
||||
return read_from_input_queue(size);
|
||||
}
|
||||
|
||||
// Parse the header in the PBF OSMHeader blob.
|
||||
void parse_header_blob() {
|
||||
osmium::io::Header header;
|
||||
const auto size = check_type_and_get_blob_size("OSMHeader");
|
||||
header = decode_header(read_from_input_queue_with_check(size));
|
||||
set_header_value(header);
|
||||
}
|
||||
|
||||
void parse_data_blobs() {
|
||||
while (const auto size = check_type_and_get_blob_size("OSMData")) {
|
||||
std::string input_buffer = read_from_input_queue_with_check(size);
|
||||
|
||||
PBFDataBlobDecoder data_blob_parser{ std::move(input_buffer), read_types() };
|
||||
|
||||
if (osmium::config::use_pool_threads_for_pbf_parsing()) {
|
||||
send_to_output_queue(osmium::thread::Pool::instance().submit(std::move(data_blob_parser)));
|
||||
} else {
|
||||
send_to_output_queue(data_blob_parser());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
PBFParser(future_string_queue_type& input_queue,
|
||||
future_buffer_queue_type& output_queue,
|
||||
std::promise<osmium::io::Header>& header_promise,
|
||||
osmium::osm_entity_bits::type read_types) :
|
||||
Parser(input_queue, output_queue, header_promise, read_types),
|
||||
m_input_buffer() {
|
||||
}
|
||||
|
||||
~PBFParser() noexcept final = default;
|
||||
|
||||
void run() final {
|
||||
osmium::thread::set_thread_name("_osmium_pbf_in");
|
||||
|
||||
parse_header_blob();
|
||||
|
||||
if (read_types() != osmium::osm_entity_bits::nothing) {
|
||||
parse_data_blobs();
|
||||
}
|
||||
}
|
||||
|
||||
}; // class PBFParser
|
||||
|
||||
// we want the register_parser() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_pbf_parser = ParserFactory::instance().register_parser(
|
||||
file_format::pbf,
|
||||
[](future_string_queue_type& input_queue,
|
||||
future_buffer_queue_type& output_queue,
|
||||
std::promise<osmium::io::Header>& header_promise,
|
||||
osmium::osm_entity_bits::type read_which_entities) {
|
||||
return std::unique_ptr<Parser>(new PBFParser(input_queue, output_queue, header_promise, read_which_entities));
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_pbf_parser() noexcept {
|
||||
return registered_pbf_parser;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_PBF_INPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,636 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_PBF_OUTPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_PBF_OUTPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <time.h>
|
||||
#include <utility>
|
||||
|
||||
#include <protozero/pbf_builder.hpp>
|
||||
|
||||
#include <osmium/handler.hpp>
|
||||
#include <osmium/io/detail/output_format.hpp>
|
||||
#include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
|
||||
#include <osmium/io/detail/protobuf_tags.hpp>
|
||||
#include <osmium/io/detail/string_table.hpp>
|
||||
#include <osmium/io/detail/zlib.hpp>
|
||||
#include <osmium/io/file.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/memory/collection.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/thread/pool.hpp>
|
||||
#include <osmium/util/cast.hpp>
|
||||
#include <osmium/util/delta.hpp>
|
||||
#include <osmium/visitor.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct pbf_output_options {
|
||||
|
||||
/// Should nodes be encoded in DenseNodes?
|
||||
bool use_dense_nodes;
|
||||
|
||||
/**
|
||||
* Should the PBF blobs contain zlib compressed data?
|
||||
*
|
||||
* The zlib compression is optional, it's possible to store the
|
||||
* blobs in raw format. Disabling the compression can improve
|
||||
* the writing speed a little but the output will be 2x to 3x
|
||||
* bigger.
|
||||
*/
|
||||
bool use_compression;
|
||||
|
||||
/// Should metadata of objects be written?
|
||||
bool add_metadata;
|
||||
|
||||
/// Add the "HistoricalInformation" header flag.
|
||||
bool add_historical_information_flag;
|
||||
|
||||
/// Should the visible flag be added to all OSM objects?
|
||||
bool add_visible_flag;
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
* Maximum number of items in a primitive block.
|
||||
*
|
||||
* The uncompressed length of a Blob *should* be less
|
||||
* than 16 megabytes and *must* be less than 32 megabytes.
|
||||
*
|
||||
* A block may contain any number of entities, as long as
|
||||
* the size limits for the surrounding blob are obeyed.
|
||||
* However, for simplicity, the current Osmosis (0.38)
|
||||
* as well as Osmium implementation always
|
||||
* uses at most 8k entities in a block.
|
||||
*/
|
||||
constexpr int32_t max_entities_per_block = 8000;
|
||||
|
||||
constexpr int location_granularity = 100;
|
||||
|
||||
/**
|
||||
* convert a double lat or lon value to an int, respecting the granularity
|
||||
*/
|
||||
inline int64_t lonlat2int(double lonlat) {
|
||||
return static_cast<int64_t>(std::round(lonlat * lonlat_resolution / location_granularity));
|
||||
}
|
||||
|
||||
enum class pbf_blob_type {
|
||||
header = 0,
|
||||
data = 1
|
||||
};
|
||||
|
||||
class SerializeBlob {
|
||||
|
||||
std::string m_msg;
|
||||
|
||||
pbf_blob_type m_blob_type;
|
||||
|
||||
bool m_use_compression;
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Initialize a blob serializer.
|
||||
*
|
||||
* @param msg Protobuf-message containing the blob data
|
||||
* @param type Type of blob.
|
||||
* @param use_compression Should the output be compressed using
|
||||
* zlib?
|
||||
*/
|
||||
SerializeBlob(std::string&& msg, pbf_blob_type type, bool use_compression) :
|
||||
m_msg(std::move(msg)),
|
||||
m_blob_type(type),
|
||||
m_use_compression(use_compression) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize a protobuf message into a Blob, optionally apply
|
||||
* compression and return it together with a BlobHeader ready
|
||||
* to be written to a file.
|
||||
*/
|
||||
std::string operator()() {
|
||||
assert(m_msg.size() <= max_uncompressed_blob_size);
|
||||
|
||||
std::string blob_data;
|
||||
protozero::pbf_builder<FileFormat::Blob> pbf_blob(blob_data);
|
||||
|
||||
if (m_use_compression) {
|
||||
pbf_blob.add_int32(FileFormat::Blob::optional_int32_raw_size, int32_t(m_msg.size()));
|
||||
pbf_blob.add_bytes(FileFormat::Blob::optional_bytes_zlib_data, osmium::io::detail::zlib_compress(m_msg));
|
||||
} else {
|
||||
pbf_blob.add_bytes(FileFormat::Blob::optional_bytes_raw, m_msg);
|
||||
}
|
||||
|
||||
std::string blob_header_data;
|
||||
protozero::pbf_builder<FileFormat::BlobHeader> pbf_blob_header(blob_header_data);
|
||||
|
||||
pbf_blob_header.add_string(FileFormat::BlobHeader::required_string_type, m_blob_type == pbf_blob_type::data ? "OSMData" : "OSMHeader");
|
||||
pbf_blob_header.add_int32(FileFormat::BlobHeader::required_int32_datasize, static_cast_with_assert<int32_t>(blob_data.size()));
|
||||
|
||||
uint32_t sz = htonl(static_cast_with_assert<uint32_t>(blob_header_data.size()));
|
||||
|
||||
// write to output: the 4-byte BlobHeader-Size followed by the BlobHeader followed by the Blob
|
||||
std::string output;
|
||||
output.reserve(sizeof(sz) + blob_header_data.size() + blob_data.size());
|
||||
output.append(reinterpret_cast<const char*>(&sz), sizeof(sz));
|
||||
output.append(blob_header_data);
|
||||
output.append(blob_data);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}; // class SerializeBlob
|
||||
|
||||
/**
|
||||
* Contains the code to pack any number of nodes into a DenseNode
|
||||
* structure.
|
||||
*
|
||||
* Because this needs to allocate a lot of memory on the heap,
|
||||
* only one object of this class will be created and then re-used
|
||||
* after calling clear() on it.
|
||||
*/
|
||||
class DenseNodes {
|
||||
|
||||
StringTable& m_stringtable;
|
||||
|
||||
std::vector<int64_t> m_ids;
|
||||
|
||||
std::vector<int32_t> m_versions;
|
||||
std::vector<int64_t> m_timestamps;
|
||||
std::vector<int64_t> m_changesets;
|
||||
std::vector<int32_t> m_uids;
|
||||
std::vector<int32_t> m_user_sids;
|
||||
std::vector<bool> m_visibles;
|
||||
|
||||
std::vector<int64_t> m_lats;
|
||||
std::vector<int64_t> m_lons;
|
||||
std::vector<int32_t> m_tags;
|
||||
|
||||
osmium::util::DeltaEncode<object_id_type, int64_t> m_delta_id;
|
||||
|
||||
osmium::util::DeltaEncode<uint32_t, int64_t> m_delta_timestamp;
|
||||
osmium::util::DeltaEncode<changeset_id_type, int64_t> m_delta_changeset;
|
||||
osmium::util::DeltaEncode<user_id_type, int32_t> m_delta_uid;
|
||||
osmium::util::DeltaEncode<uint32_t, int32_t> m_delta_user_sid;
|
||||
|
||||
osmium::util::DeltaEncode<int64_t, int64_t> m_delta_lat;
|
||||
osmium::util::DeltaEncode<int64_t, int64_t> m_delta_lon;
|
||||
|
||||
const pbf_output_options& m_options;
|
||||
|
||||
public:
|
||||
|
||||
DenseNodes(StringTable& stringtable, const pbf_output_options& options) :
|
||||
m_stringtable(stringtable),
|
||||
m_options(options) {
|
||||
}
|
||||
|
||||
/// Clear object for re-use. Keep the allocated memory.
|
||||
void clear() {
|
||||
m_ids.clear();
|
||||
|
||||
m_versions.clear();
|
||||
m_timestamps.clear();
|
||||
m_changesets.clear();
|
||||
m_uids.clear();
|
||||
m_user_sids.clear();
|
||||
m_visibles.clear();
|
||||
|
||||
m_lats.clear();
|
||||
m_lons.clear();
|
||||
m_tags.clear();
|
||||
|
||||
m_delta_id.clear();
|
||||
|
||||
m_delta_timestamp.clear();
|
||||
m_delta_changeset.clear();
|
||||
m_delta_uid.clear();
|
||||
m_delta_user_sid.clear();
|
||||
|
||||
m_delta_lat.clear();
|
||||
m_delta_lon.clear();
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return m_ids.size() * 3 * sizeof(int64_t);
|
||||
}
|
||||
|
||||
void add_node(const osmium::Node& node) {
|
||||
m_ids.push_back(m_delta_id.update(node.id()));
|
||||
|
||||
if (m_options.add_metadata) {
|
||||
m_versions.push_back(static_cast_with_assert<int32_t>(node.version()));
|
||||
m_timestamps.push_back(m_delta_timestamp.update(uint32_t(node.timestamp())));
|
||||
m_changesets.push_back(m_delta_changeset.update(node.changeset()));
|
||||
m_uids.push_back(m_delta_uid.update(node.uid()));
|
||||
m_user_sids.push_back(m_delta_user_sid.update(m_stringtable.add(node.user())));
|
||||
if (m_options.add_visible_flag) {
|
||||
m_visibles.push_back(node.visible());
|
||||
}
|
||||
}
|
||||
|
||||
m_lats.push_back(m_delta_lat.update(lonlat2int(node.location().lat_without_check())));
|
||||
m_lons.push_back(m_delta_lon.update(lonlat2int(node.location().lon_without_check())));
|
||||
|
||||
for (const auto& tag : node.tags()) {
|
||||
m_tags.push_back(static_cast_with_assert<int32_t>(m_stringtable.add(tag.key())));
|
||||
m_tags.push_back(static_cast_with_assert<int32_t>(m_stringtable.add(tag.value())));
|
||||
}
|
||||
m_tags.push_back(0);
|
||||
}
|
||||
|
||||
std::string serialize() const {
|
||||
std::string data;
|
||||
protozero::pbf_builder<OSMFormat::DenseNodes> pbf_dense_nodes(data);
|
||||
|
||||
pbf_dense_nodes.add_packed_sint64(OSMFormat::DenseNodes::packed_sint64_id, m_ids.cbegin(), m_ids.cend());
|
||||
|
||||
if (m_options.add_metadata) {
|
||||
protozero::pbf_builder<OSMFormat::DenseInfo> pbf_dense_info(pbf_dense_nodes, OSMFormat::DenseNodes::optional_DenseInfo_denseinfo);
|
||||
pbf_dense_info.add_packed_int32(OSMFormat::DenseInfo::packed_int32_version, m_versions.cbegin(), m_versions.cend());
|
||||
pbf_dense_info.add_packed_sint64(OSMFormat::DenseInfo::packed_sint64_timestamp, m_timestamps.cbegin(), m_timestamps.cend());
|
||||
pbf_dense_info.add_packed_sint64(OSMFormat::DenseInfo::packed_sint64_changeset, m_changesets.cbegin(), m_changesets.cend());
|
||||
pbf_dense_info.add_packed_sint32(OSMFormat::DenseInfo::packed_sint32_uid, m_uids.cbegin(), m_uids.cend());
|
||||
pbf_dense_info.add_packed_sint32(OSMFormat::DenseInfo::packed_sint32_user_sid, m_user_sids.cbegin(), m_user_sids.cend());
|
||||
|
||||
if (m_options.add_visible_flag) {
|
||||
pbf_dense_info.add_packed_bool(OSMFormat::DenseInfo::packed_bool_visible, m_visibles.cbegin(), m_visibles.cend());
|
||||
}
|
||||
}
|
||||
|
||||
pbf_dense_nodes.add_packed_sint64(OSMFormat::DenseNodes::packed_sint64_lat, m_lats.cbegin(), m_lats.cend());
|
||||
pbf_dense_nodes.add_packed_sint64(OSMFormat::DenseNodes::packed_sint64_lon, m_lons.cbegin(), m_lons.cend());
|
||||
|
||||
pbf_dense_nodes.add_packed_int32(OSMFormat::DenseNodes::packed_int32_keys_vals, m_tags.cbegin(), m_tags.cend());
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
}; // class DenseNodes
|
||||
|
||||
class PrimitiveBlock {
|
||||
|
||||
std::string m_pbf_primitive_group_data;
|
||||
protozero::pbf_builder<OSMFormat::PrimitiveGroup> m_pbf_primitive_group;
|
||||
StringTable m_stringtable;
|
||||
DenseNodes m_dense_nodes;
|
||||
OSMFormat::PrimitiveGroup m_type;
|
||||
int m_count;
|
||||
|
||||
public:
|
||||
|
||||
explicit PrimitiveBlock(const pbf_output_options& options) :
|
||||
m_pbf_primitive_group_data(),
|
||||
m_pbf_primitive_group(m_pbf_primitive_group_data),
|
||||
m_stringtable(),
|
||||
m_dense_nodes(m_stringtable, options),
|
||||
m_type(OSMFormat::PrimitiveGroup::unknown),
|
||||
m_count(0) {
|
||||
}
|
||||
|
||||
const std::string& group_data() {
|
||||
if (type() == OSMFormat::PrimitiveGroup::optional_DenseNodes_dense) {
|
||||
m_pbf_primitive_group.add_message(OSMFormat::PrimitiveGroup::optional_DenseNodes_dense, m_dense_nodes.serialize());
|
||||
}
|
||||
return m_pbf_primitive_group_data;
|
||||
}
|
||||
|
||||
void reset(OSMFormat::PrimitiveGroup type) {
|
||||
m_pbf_primitive_group_data.clear();
|
||||
m_stringtable.clear();
|
||||
m_dense_nodes.clear();
|
||||
m_type = type;
|
||||
m_count = 0;
|
||||
}
|
||||
|
||||
void write_stringtable(protozero::pbf_builder<OSMFormat::StringTable>& pbf_string_table) {
|
||||
for (const char* s : m_stringtable) {
|
||||
pbf_string_table.add_bytes(OSMFormat::StringTable::repeated_bytes_s, s);
|
||||
}
|
||||
}
|
||||
|
||||
protozero::pbf_builder<OSMFormat::PrimitiveGroup>& group() {
|
||||
++m_count;
|
||||
return m_pbf_primitive_group;
|
||||
}
|
||||
|
||||
void add_dense_node(const osmium::Node& node) {
|
||||
m_dense_nodes.add_node(node);
|
||||
++m_count;
|
||||
}
|
||||
|
||||
uint32_t store_in_stringtable(const char* s) {
|
||||
return m_stringtable.add(s);
|
||||
}
|
||||
|
||||
int count() const {
|
||||
return m_count;
|
||||
}
|
||||
|
||||
OSMFormat::PrimitiveGroup type() const {
|
||||
return m_type;
|
||||
}
|
||||
|
||||
size_t size() const {
|
||||
return m_pbf_primitive_group_data.size() + m_stringtable.size() + m_dense_nodes.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* The output buffer (block) will be filled to about
|
||||
* 95% and then written to disk. This leaves more than
|
||||
* enough space for the string table (which typically
|
||||
* needs about 0.1 to 0.3% of the block size).
|
||||
*/
|
||||
constexpr static size_t max_used_blob_size = max_uncompressed_blob_size * 95 / 100;
|
||||
|
||||
bool can_add(OSMFormat::PrimitiveGroup type) const {
|
||||
if (type != m_type) {
|
||||
return false;
|
||||
}
|
||||
if (count() >= max_entities_per_block) {
|
||||
return false;
|
||||
}
|
||||
return size() < max_used_blob_size;
|
||||
}
|
||||
|
||||
}; // class PrimitiveBlock
|
||||
|
||||
class PBFOutputFormat : public osmium::io::detail::OutputFormat, public osmium::handler::Handler {
|
||||
|
||||
pbf_output_options m_options;
|
||||
|
||||
PrimitiveBlock m_primitive_block;
|
||||
|
||||
void store_primitive_block() {
|
||||
if (m_primitive_block.count() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::string primitive_block_data;
|
||||
protozero::pbf_builder<OSMFormat::PrimitiveBlock> primitive_block(primitive_block_data);
|
||||
|
||||
{
|
||||
protozero::pbf_builder<OSMFormat::StringTable> pbf_string_table(primitive_block, OSMFormat::PrimitiveBlock::required_StringTable_stringtable);
|
||||
m_primitive_block.write_stringtable(pbf_string_table);
|
||||
}
|
||||
|
||||
primitive_block.add_message(OSMFormat::PrimitiveBlock::repeated_PrimitiveGroup_primitivegroup, m_primitive_block.group_data());
|
||||
|
||||
m_output_queue.push(osmium::thread::Pool::instance().submit(
|
||||
SerializeBlob{std::move(primitive_block_data),
|
||||
pbf_blob_type::data,
|
||||
m_options.use_compression}
|
||||
));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void add_meta(const osmium::OSMObject& object, T& pbf_object) {
|
||||
{
|
||||
protozero::packed_field_uint32 field{pbf_object, protozero::pbf_tag_type(T::enum_type::packed_uint32_keys)};
|
||||
for (const auto& tag : object.tags()) {
|
||||
field.add_element(m_primitive_block.store_in_stringtable(tag.key()));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
protozero::packed_field_uint32 field{pbf_object, protozero::pbf_tag_type(T::enum_type::packed_uint32_vals)};
|
||||
for (const auto& tag : object.tags()) {
|
||||
field.add_element(m_primitive_block.store_in_stringtable(tag.value()));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_options.add_metadata) {
|
||||
protozero::pbf_builder<OSMFormat::Info> pbf_info(pbf_object, T::enum_type::optional_Info_info);
|
||||
|
||||
pbf_info.add_int32(OSMFormat::Info::optional_int32_version, static_cast_with_assert<int32_t>(object.version()));
|
||||
pbf_info.add_int64(OSMFormat::Info::optional_int64_timestamp, uint32_t(object.timestamp()));
|
||||
pbf_info.add_int64(OSMFormat::Info::optional_int64_changeset, object.changeset());
|
||||
pbf_info.add_int32(OSMFormat::Info::optional_int32_uid, static_cast_with_assert<int32_t>(object.uid()));
|
||||
pbf_info.add_uint32(OSMFormat::Info::optional_uint32_user_sid, m_primitive_block.store_in_stringtable(object.user()));
|
||||
if (m_options.add_visible_flag) {
|
||||
pbf_info.add_bool(OSMFormat::Info::optional_bool_visible, object.visible());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void switch_primitive_block_type(OSMFormat::PrimitiveGroup type) {
|
||||
if (!m_primitive_block.can_add(type)) {
|
||||
store_primitive_block();
|
||||
m_primitive_block.reset(type);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
PBFOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) :
|
||||
OutputFormat(output_queue),
|
||||
m_options(),
|
||||
m_primitive_block(m_options) {
|
||||
m_options.use_dense_nodes = file.is_not_false("pbf_dense_nodes");
|
||||
m_options.use_compression = file.get("pbf_compression") != "none" && file.is_not_false("pbf_compression");
|
||||
m_options.add_metadata = file.is_not_false("pbf_add_metadata") && file.is_not_false("add_metadata");
|
||||
m_options.add_historical_information_flag = file.has_multiple_object_versions();
|
||||
m_options.add_visible_flag = file.has_multiple_object_versions();
|
||||
}
|
||||
|
||||
PBFOutputFormat(const PBFOutputFormat&) = delete;
|
||||
PBFOutputFormat& operator=(const PBFOutputFormat&) = delete;
|
||||
|
||||
~PBFOutputFormat() noexcept final = default;
|
||||
|
||||
void write_header(const osmium::io::Header& header) final {
|
||||
std::string data;
|
||||
protozero::pbf_builder<OSMFormat::HeaderBlock> pbf_header_block(data);
|
||||
|
||||
if (!header.boxes().empty()) {
|
||||
protozero::pbf_builder<OSMFormat::HeaderBBox> pbf_header_bbox(pbf_header_block, OSMFormat::HeaderBlock::optional_HeaderBBox_bbox);
|
||||
|
||||
osmium::Box box = header.joined_boxes();
|
||||
pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_left, int64_t(box.bottom_left().lon() * lonlat_resolution));
|
||||
pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_right, int64_t(box.top_right().lon() * lonlat_resolution));
|
||||
pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_top, int64_t(box.top_right().lat() * lonlat_resolution));
|
||||
pbf_header_bbox.add_sint64(OSMFormat::HeaderBBox::required_sint64_bottom, int64_t(box.bottom_left().lat() * lonlat_resolution));
|
||||
}
|
||||
|
||||
pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "OsmSchema-V0.6");
|
||||
|
||||
if (m_options.use_dense_nodes) {
|
||||
pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "DenseNodes");
|
||||
}
|
||||
|
||||
if (m_options.add_historical_information_flag) {
|
||||
pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "HistoricalInformation");
|
||||
}
|
||||
|
||||
pbf_header_block.add_string(OSMFormat::HeaderBlock::optional_string_writingprogram, header.get("generator"));
|
||||
|
||||
std::string osmosis_replication_timestamp = header.get("osmosis_replication_timestamp");
|
||||
if (!osmosis_replication_timestamp.empty()) {
|
||||
osmium::Timestamp ts(osmosis_replication_timestamp.c_str());
|
||||
pbf_header_block.add_int64(OSMFormat::HeaderBlock::optional_int64_osmosis_replication_timestamp, uint32_t(ts));
|
||||
}
|
||||
|
||||
std::string osmosis_replication_sequence_number = header.get("osmosis_replication_sequence_number");
|
||||
if (!osmosis_replication_sequence_number.empty()) {
|
||||
pbf_header_block.add_int64(OSMFormat::HeaderBlock::optional_int64_osmosis_replication_sequence_number, std::atoll(osmosis_replication_sequence_number.c_str()));
|
||||
}
|
||||
|
||||
std::string osmosis_replication_base_url = header.get("osmosis_replication_base_url");
|
||||
if (!osmosis_replication_base_url.empty()) {
|
||||
pbf_header_block.add_string(OSMFormat::HeaderBlock::optional_string_osmosis_replication_base_url, osmosis_replication_base_url);
|
||||
}
|
||||
|
||||
m_output_queue.push(osmium::thread::Pool::instance().submit(
|
||||
SerializeBlob{std::move(data),
|
||||
pbf_blob_type::header,
|
||||
m_options.use_compression}
|
||||
));
|
||||
}
|
||||
|
||||
void write_buffer(osmium::memory::Buffer&& buffer) final {
|
||||
osmium::apply(buffer.cbegin(), buffer.cend(), *this);
|
||||
}
|
||||
|
||||
void write_end() final {
|
||||
store_primitive_block();
|
||||
}
|
||||
|
||||
void node(const osmium::Node& node) {
|
||||
if (m_options.use_dense_nodes) {
|
||||
switch_primitive_block_type(OSMFormat::PrimitiveGroup::optional_DenseNodes_dense);
|
||||
m_primitive_block.add_dense_node(node);
|
||||
return;
|
||||
}
|
||||
|
||||
switch_primitive_block_type(OSMFormat::PrimitiveGroup::repeated_Node_nodes);
|
||||
protozero::pbf_builder<OSMFormat::Node> pbf_node{ m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Node_nodes };
|
||||
|
||||
pbf_node.add_sint64(OSMFormat::Node::required_sint64_id, node.id());
|
||||
add_meta(node, pbf_node);
|
||||
|
||||
pbf_node.add_sint64(OSMFormat::Node::required_sint64_lat, lonlat2int(node.location().lat_without_check()));
|
||||
pbf_node.add_sint64(OSMFormat::Node::required_sint64_lon, lonlat2int(node.location().lon_without_check()));
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
switch_primitive_block_type(OSMFormat::PrimitiveGroup::repeated_Way_ways);
|
||||
protozero::pbf_builder<OSMFormat::Way> pbf_way{ m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Way_ways };
|
||||
|
||||
pbf_way.add_int64(OSMFormat::Way::required_int64_id, way.id());
|
||||
add_meta(way, pbf_way);
|
||||
|
||||
static auto map_node_ref = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> osmium::object_id_type {
|
||||
return node_ref->ref();
|
||||
};
|
||||
typedef osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_ref), osmium::object_id_type> it_type;
|
||||
|
||||
const auto& nodes = way.nodes();
|
||||
it_type first { nodes.cbegin(), nodes.cend(), map_node_ref };
|
||||
it_type last { nodes.cend(), nodes.cend(), map_node_ref };
|
||||
pbf_way.add_packed_sint64(OSMFormat::Way::packed_sint64_refs, first, last);
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
switch_primitive_block_type(OSMFormat::PrimitiveGroup::repeated_Relation_relations);
|
||||
protozero::pbf_builder<OSMFormat::Relation> pbf_relation { m_primitive_block.group(), OSMFormat::PrimitiveGroup::repeated_Relation_relations };
|
||||
|
||||
pbf_relation.add_int64(OSMFormat::Relation::required_int64_id, relation.id());
|
||||
add_meta(relation, pbf_relation);
|
||||
|
||||
{
|
||||
protozero::packed_field_int32 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_int32_roles_sid)};
|
||||
for (const auto& member : relation.members()) {
|
||||
field.add_element(m_primitive_block.store_in_stringtable(member.role()));
|
||||
}
|
||||
}
|
||||
|
||||
static auto map_member_ref = [](osmium::RelationMemberList::const_iterator member) noexcept -> osmium::object_id_type {
|
||||
return member->ref();
|
||||
};
|
||||
typedef osmium::util::DeltaEncodeIterator<osmium::RelationMemberList::const_iterator, decltype(map_member_ref), osmium::object_id_type> it_type;
|
||||
const auto& members = relation.members();
|
||||
it_type first { members.cbegin(), members.cend(), map_member_ref };
|
||||
it_type last { members.cend(), members.cend(), map_member_ref };
|
||||
pbf_relation.add_packed_sint64(OSMFormat::Relation::packed_sint64_memids, first, last);
|
||||
|
||||
{
|
||||
protozero::packed_field_int32 field{pbf_relation, protozero::pbf_tag_type(OSMFormat::Relation::packed_MemberType_types)};
|
||||
for (const auto& member : relation.members()) {
|
||||
field.add_element(int32_t(osmium::item_type_to_nwr_index(member.type())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // class PBFOutputFormat
|
||||
|
||||
// we want the register_output_format() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_pbf_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::pbf,
|
||||
[](const osmium::io::File& file, future_string_queue_type& output_queue) {
|
||||
return new osmium::io::detail::PBFOutputFormat(file, output_queue);
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_pbf_output() noexcept {
|
||||
return registered_pbf_output;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_PBF_OUTPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,170 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_PROTOBUF_TAGS_HPP
|
||||
#define OSMIUM_IO_DETAIL_PROTOBUF_TAGS_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <protozero/types.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
// directly translated from
|
||||
// https://github.com/scrosby/OSM-binary/blob/master/src/fileformat.proto
|
||||
|
||||
namespace FileFormat {
|
||||
|
||||
enum class Blob : protozero::pbf_tag_type {
|
||||
optional_bytes_raw = 1,
|
||||
optional_int32_raw_size = 2,
|
||||
optional_bytes_zlib_data = 3,
|
||||
optional_bytes_lzma_data = 4
|
||||
};
|
||||
|
||||
enum class BlobHeader : protozero::pbf_tag_type {
|
||||
required_string_type = 1,
|
||||
optional_bytes_indexdata = 2,
|
||||
required_int32_datasize = 3
|
||||
};
|
||||
|
||||
} // namespace FileFormat
|
||||
|
||||
// directly translated from
|
||||
// https://github.com/scrosby/OSM-binary/blob/master/src/osmformat.proto
|
||||
|
||||
namespace OSMFormat {
|
||||
|
||||
enum class HeaderBlock : protozero::pbf_tag_type {
|
||||
optional_HeaderBBox_bbox = 1,
|
||||
repeated_string_required_features = 4,
|
||||
repeated_string_optional_features = 5,
|
||||
optional_string_writingprogram = 16,
|
||||
optional_string_source = 17,
|
||||
optional_int64_osmosis_replication_timestamp = 32,
|
||||
optional_int64_osmosis_replication_sequence_number = 33,
|
||||
optional_string_osmosis_replication_base_url = 34
|
||||
};
|
||||
|
||||
enum class HeaderBBox : protozero::pbf_tag_type {
|
||||
required_sint64_left = 1,
|
||||
required_sint64_right = 2,
|
||||
required_sint64_top = 3,
|
||||
required_sint64_bottom = 4
|
||||
};
|
||||
|
||||
enum class PrimitiveBlock : protozero::pbf_tag_type {
|
||||
required_StringTable_stringtable = 1,
|
||||
repeated_PrimitiveGroup_primitivegroup = 2,
|
||||
optional_int32_granularity = 17,
|
||||
optional_int32_date_granularity = 18,
|
||||
optional_int64_lat_offset = 19,
|
||||
optional_int64_lon_offset = 20
|
||||
};
|
||||
|
||||
enum class PrimitiveGroup : protozero::pbf_tag_type {
|
||||
unknown = 0,
|
||||
repeated_Node_nodes = 1,
|
||||
optional_DenseNodes_dense = 2,
|
||||
repeated_Way_ways = 3,
|
||||
repeated_Relation_relations = 4,
|
||||
repeated_ChangeSet_changesets = 5
|
||||
};
|
||||
|
||||
enum class StringTable : protozero::pbf_tag_type {
|
||||
repeated_bytes_s = 1
|
||||
};
|
||||
|
||||
enum class Info : protozero::pbf_tag_type {
|
||||
optional_int32_version = 1,
|
||||
optional_int64_timestamp = 2,
|
||||
optional_int64_changeset = 3,
|
||||
optional_int32_uid = 4,
|
||||
optional_uint32_user_sid = 5,
|
||||
optional_bool_visible = 6
|
||||
};
|
||||
|
||||
enum class DenseInfo : protozero::pbf_tag_type {
|
||||
packed_int32_version = 1,
|
||||
packed_sint64_timestamp = 2,
|
||||
packed_sint64_changeset = 3,
|
||||
packed_sint32_uid = 4,
|
||||
packed_sint32_user_sid = 5,
|
||||
packed_bool_visible = 6
|
||||
};
|
||||
|
||||
enum class Node : protozero::pbf_tag_type {
|
||||
required_sint64_id = 1,
|
||||
packed_uint32_keys = 2,
|
||||
packed_uint32_vals = 3,
|
||||
optional_Info_info = 4,
|
||||
required_sint64_lat = 8,
|
||||
required_sint64_lon = 9
|
||||
};
|
||||
|
||||
enum class DenseNodes : protozero::pbf_tag_type {
|
||||
packed_sint64_id = 1,
|
||||
optional_DenseInfo_denseinfo = 5,
|
||||
packed_sint64_lat = 8,
|
||||
packed_sint64_lon = 9,
|
||||
packed_int32_keys_vals = 10
|
||||
};
|
||||
|
||||
enum class Way : protozero::pbf_tag_type {
|
||||
required_int64_id = 1,
|
||||
packed_uint32_keys = 2,
|
||||
packed_uint32_vals = 3,
|
||||
optional_Info_info = 4,
|
||||
packed_sint64_refs = 8
|
||||
};
|
||||
|
||||
enum class Relation : protozero::pbf_tag_type {
|
||||
required_int64_id = 1,
|
||||
packed_uint32_keys = 2,
|
||||
packed_uint32_vals = 3,
|
||||
optional_Info_info = 4,
|
||||
packed_int32_roles_sid = 8,
|
||||
packed_sint64_memids = 9,
|
||||
packed_MemberType_types = 10
|
||||
};
|
||||
|
||||
} // namespace OSMFormat
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_PROTOBUF_TAGS_HPP
|
||||
@@ -0,0 +1,157 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_QUEUE_UTIL_HPP
|
||||
#define OSMIUM_IO_DETAIL_QUEUE_UTIL_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <string>
|
||||
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/thread/queue.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* This type of queue contains buffers with OSM data in them.
|
||||
* The "end of file" is marked by an invalid Buffer.
|
||||
* The buffers are wrapped in a std::future so that they can also
|
||||
* transport exceptions. The future also helps with keeping the
|
||||
* data in order.
|
||||
*/
|
||||
using future_buffer_queue_type = osmium::thread::Queue<std::future<osmium::memory::Buffer>>;
|
||||
|
||||
/**
|
||||
* This type of queue contains OSM file data in the form it is
|
||||
* stored on disk, ie encoded as XML, PBF, etc.
|
||||
* The "end of file" is marked by an empty string.
|
||||
*/
|
||||
using string_queue_type = osmium::thread::Queue<std::string>;
|
||||
|
||||
/**
|
||||
* This type of queue contains OSM file data in the form it is
|
||||
* stored on disk, ie encoded as XML, PBF, etc.
|
||||
* The "end of file" is marked by an empty string.
|
||||
* The strings are wrapped in a std::future so that they can also
|
||||
* transport exceptions. The future also helps with keeping the
|
||||
* data in order.
|
||||
*/
|
||||
using future_string_queue_type = osmium::thread::Queue<std::future<std::string>>;
|
||||
|
||||
template <typename T>
|
||||
inline void add_to_queue(osmium::thread::Queue<std::future<T>>& queue, T&& data) {
|
||||
std::promise<T> promise;
|
||||
queue.push(promise.get_future());
|
||||
promise.set_value(std::forward<T>(data));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void add_to_queue(osmium::thread::Queue<std::future<T>>& queue, std::exception_ptr&& exception) {
|
||||
std::promise<T> promise;
|
||||
queue.push(promise.get_future());
|
||||
promise.set_exception(std::move(exception));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void add_end_of_data_to_queue(osmium::thread::Queue<std::future<T>>& queue) {
|
||||
add_to_queue<T>(queue, T{});
|
||||
}
|
||||
|
||||
inline bool at_end_of_data(const std::string& data) {
|
||||
return data.empty();
|
||||
}
|
||||
|
||||
inline bool at_end_of_data(osmium::memory::Buffer& buffer) {
|
||||
return !buffer;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
class queue_wrapper {
|
||||
|
||||
using queue_type = osmium::thread::Queue<std::future<T>>;
|
||||
|
||||
queue_type& m_queue;
|
||||
bool m_has_reached_end_of_data;
|
||||
|
||||
public:
|
||||
|
||||
explicit queue_wrapper(queue_type& queue) :
|
||||
m_queue(queue),
|
||||
m_has_reached_end_of_data(false) {
|
||||
}
|
||||
|
||||
~queue_wrapper() noexcept {
|
||||
drain();
|
||||
}
|
||||
|
||||
void drain() {
|
||||
while (!m_has_reached_end_of_data) {
|
||||
try {
|
||||
pop();
|
||||
} catch (...) {
|
||||
// Ignore any exceptions.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool has_reached_end_of_data() const noexcept {
|
||||
return m_has_reached_end_of_data;
|
||||
}
|
||||
|
||||
T pop() {
|
||||
T data;
|
||||
if (!m_has_reached_end_of_data) {
|
||||
std::future<T> data_future;
|
||||
m_queue.wait_and_pop(data_future);
|
||||
data = std::move(data_future.get());
|
||||
if (at_end_of_data(data)) {
|
||||
m_has_reached_end_of_data = true;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
}; // class queue_wrapper
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_QUEUE_UTIL_HPP
|
||||
@@ -0,0 +1,133 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_READ_THREAD_HPP
|
||||
#define OSMIUM_IO_DETAIL_READ_THREAD_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/io/compression.hpp>
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/thread/util.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* This code uses an internally managed thread to read data from
|
||||
* the input file and (optionally) decompress it. The result is
|
||||
* sent to the given queue. Any exceptions will also be send to
|
||||
* the queue.
|
||||
*/
|
||||
class ReadThreadManager {
|
||||
|
||||
// only used in the sub-thread
|
||||
osmium::io::Decompressor& m_decompressor;
|
||||
future_string_queue_type& m_queue;
|
||||
|
||||
// used in both threads
|
||||
std::atomic<bool> m_done;
|
||||
|
||||
// only used in the main thread
|
||||
std::thread m_thread;
|
||||
|
||||
void run_in_thread() {
|
||||
osmium::thread::set_thread_name("_osmium_read");
|
||||
|
||||
try {
|
||||
while (!m_done) {
|
||||
std::string data {m_decompressor.read()};
|
||||
if (at_end_of_data(data)) {
|
||||
break;
|
||||
}
|
||||
add_to_queue(m_queue, std::move(data));
|
||||
}
|
||||
|
||||
m_decompressor.close();
|
||||
} catch (...) {
|
||||
add_to_queue(m_queue, std::current_exception());
|
||||
}
|
||||
|
||||
add_end_of_data_to_queue(m_queue);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
ReadThreadManager(osmium::io::Decompressor& decompressor,
|
||||
future_string_queue_type& queue) :
|
||||
m_decompressor(decompressor),
|
||||
m_queue(queue),
|
||||
m_done(false),
|
||||
m_thread(std::thread(&ReadThreadManager::run_in_thread, this)) {
|
||||
}
|
||||
|
||||
ReadThreadManager(const ReadThreadManager&) = delete;
|
||||
ReadThreadManager& operator=(const ReadThreadManager&) = delete;
|
||||
|
||||
ReadThreadManager(ReadThreadManager&&) = delete;
|
||||
ReadThreadManager& operator=(ReadThreadManager&&) = delete;
|
||||
|
||||
~ReadThreadManager() noexcept {
|
||||
try {
|
||||
close();
|
||||
} catch (...) {
|
||||
// Ignore any exceptions because destructor must not throw.
|
||||
}
|
||||
}
|
||||
|
||||
void stop() noexcept {
|
||||
m_done = true;
|
||||
}
|
||||
|
||||
void close() {
|
||||
stop();
|
||||
if (m_thread.joinable()) {
|
||||
m_thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
}; // class ReadThreadManager
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_READ_THREAD_HPP
|
||||
@@ -0,0 +1,180 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_READ_WRITE_HPP
|
||||
#define OSMIUM_IO_DETAIL_READ_WRITE_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <string>
|
||||
#include <system_error>
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# include <unistd.h>
|
||||
#else
|
||||
# include <io.h>
|
||||
#endif
|
||||
|
||||
#include <osmium/io/writer_options.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
/**
|
||||
* @brief Namespace for Osmium internal use
|
||||
*/
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Open file for writing. If the file exists, it is truncated, if
|
||||
* not, it is created. If the file name is empty or "-", no file
|
||||
* is opened and the stdout file descriptor (1) is returned.
|
||||
*
|
||||
* @param filename Name of file to be opened.
|
||||
* @param allow_overwrite If the file exists, should it be overwritten?
|
||||
* @returns File descriptor of open file.
|
||||
* @throws system_error if the file can't be opened.
|
||||
*/
|
||||
inline int open_for_writing(const std::string& filename, osmium::io::overwrite allow_overwrite = osmium::io::overwrite::no) {
|
||||
if (filename == "" || filename == "-") {
|
||||
#ifdef _WIN32
|
||||
_setmode(1, _O_BINARY);
|
||||
#endif
|
||||
return 1; // stdout
|
||||
}
|
||||
|
||||
int flags = O_WRONLY | O_CREAT;
|
||||
if (allow_overwrite == osmium::io::overwrite::allow) {
|
||||
flags |= O_TRUNC;
|
||||
} else {
|
||||
flags |= O_EXCL;
|
||||
}
|
||||
#ifdef _WIN32
|
||||
flags |= O_BINARY;
|
||||
#endif
|
||||
int fd = ::open(filename.c_str(), flags, 0666);
|
||||
if (fd < 0) {
|
||||
throw std::system_error(errno, std::system_category(), std::string("Open failed for '") + filename + "'");
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open file for reading. If the file name is empty or "-", no file
|
||||
* is opened and the stdin file descriptor (0) is returned.
|
||||
*
|
||||
* @param filename Name of file to be opened.
|
||||
* @returns File descriptor of open file.
|
||||
* @throws system_error if the file can't be opened.
|
||||
*/
|
||||
inline int open_for_reading(const std::string& filename) {
|
||||
if (filename == "" || filename == "-") {
|
||||
return 0; // stdin
|
||||
}
|
||||
|
||||
int flags = O_RDONLY;
|
||||
#ifdef _WIN32
|
||||
flags |= O_BINARY;
|
||||
#endif
|
||||
int fd = ::open(filename.c_str(), flags);
|
||||
if (fd < 0) {
|
||||
throw std::system_error(errno, std::system_category(), std::string("Open failed for '") + filename + "'");
|
||||
}
|
||||
return fd;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given number of bytes from the output_buffer to the file descriptor.
|
||||
* This is just a wrapper around write(2), because write(2) can write less than
|
||||
* the given number of bytes.
|
||||
*
|
||||
* @param fd File descriptor.
|
||||
* @param output_buffer Buffer with data to be written. Must be at least size bytes long.
|
||||
* @param size Number of bytes to write.
|
||||
* @throws std::system_error On error.
|
||||
*/
|
||||
inline void reliable_write(const int fd, const unsigned char* output_buffer, const size_t size) {
|
||||
constexpr size_t max_write = 100L * 1024L * 1024L; // Max 100 MByte per write
|
||||
size_t offset = 0;
|
||||
do {
|
||||
auto write_count = size - offset;
|
||||
if (write_count > max_write) {
|
||||
write_count = max_write;
|
||||
}
|
||||
auto length = ::write(fd, output_buffer + offset, static_cast<unsigned int>(write_count));
|
||||
if (length < 0) {
|
||||
throw std::system_error(errno, std::system_category(), "Write failed");
|
||||
}
|
||||
offset += static_cast<size_t>(length);
|
||||
} while (offset < size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes the given number of bytes from the output_buffer to the file descriptor.
|
||||
* This is just a wrapper around write(2), because write(2) can write less than
|
||||
* the given number of bytes.
|
||||
*
|
||||
* @param fd File descriptor.
|
||||
* @param output_buffer Buffer with data to be written. Must be at least size bytes long.
|
||||
* @param size Number of bytes to write.
|
||||
* @throws std::system_error On error.
|
||||
*/
|
||||
inline void reliable_write(const int fd, const char* output_buffer, const size_t size) {
|
||||
reliable_write(fd, reinterpret_cast<const unsigned char*>(output_buffer), size);
|
||||
}
|
||||
|
||||
inline void reliable_fsync(const int fd) {
|
||||
#ifdef _WIN32
|
||||
if (_commit(fd) != 0) {
|
||||
#else
|
||||
if (::fsync(fd) != 0) {
|
||||
#endif
|
||||
throw std::system_error(errno, std::system_category(), "Fsync failed");
|
||||
}
|
||||
}
|
||||
|
||||
inline void reliable_close(const int fd) {
|
||||
if (::close(fd) != 0) {
|
||||
throw std::system_error(errno, std::system_category(), "Close failed");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_READ_WRITE_HPP
|
||||
@@ -0,0 +1,265 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_STRING_TABLE_HPP
|
||||
#define OSMIUM_IO_DETAIL_STRING_TABLE_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iterator>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include <osmium/io/detail/pbf.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* class StringStore
|
||||
*
|
||||
* Storage of lots of strings (const char *). Memory is allocated in chunks.
|
||||
* If a string is added and there is no space in the current chunk, a new
|
||||
* chunk will be allocated. Strings added to the store must not be larger
|
||||
* than the chunk size.
|
||||
*
|
||||
* All memory is released when the destructor is called. There is no other way
|
||||
* to release all or part of the memory.
|
||||
*
|
||||
*/
|
||||
class StringStore {
|
||||
|
||||
size_t m_chunk_size;
|
||||
|
||||
std::list<std::string> m_chunks;
|
||||
|
||||
void add_chunk() {
|
||||
m_chunks.push_front(std::string());
|
||||
m_chunks.front().reserve(m_chunk_size);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit StringStore(size_t chunk_size) :
|
||||
m_chunk_size(chunk_size),
|
||||
m_chunks() {
|
||||
add_chunk();
|
||||
}
|
||||
|
||||
void clear() noexcept {
|
||||
m_chunks.erase(std::next(m_chunks.begin()), m_chunks.end());
|
||||
m_chunks.front().clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a null terminated string to the store. This will
|
||||
* automatically get more memory if we are out.
|
||||
* Returns a pointer to the copy of the string we have
|
||||
* allocated.
|
||||
*/
|
||||
const char* add(const char* string) {
|
||||
size_t len = std::strlen(string) + 1;
|
||||
|
||||
assert(len <= m_chunk_size);
|
||||
|
||||
size_t chunk_len = m_chunks.front().size();
|
||||
if (chunk_len + len > m_chunks.front().capacity()) {
|
||||
add_chunk();
|
||||
chunk_len = 0;
|
||||
}
|
||||
|
||||
m_chunks.front().append(string);
|
||||
m_chunks.front().append(1, '\0');
|
||||
|
||||
return m_chunks.front().c_str() + chunk_len;
|
||||
}
|
||||
|
||||
class const_iterator : public std::iterator<std::forward_iterator_tag, const char*> {
|
||||
|
||||
typedef std::list<std::string>::const_iterator it_type;
|
||||
it_type m_it;
|
||||
const it_type m_last;
|
||||
const char* m_pos;
|
||||
|
||||
public:
|
||||
|
||||
const_iterator(it_type it, it_type last) :
|
||||
m_it(it),
|
||||
m_last(last),
|
||||
m_pos(it == last ? nullptr : m_it->c_str()) {
|
||||
}
|
||||
|
||||
const_iterator& operator++() {
|
||||
assert(m_it != m_last);
|
||||
auto last_pos = m_it->c_str() + m_it->size();
|
||||
while (m_pos != last_pos && *m_pos) ++m_pos;
|
||||
if (m_pos != last_pos) ++m_pos;
|
||||
if (m_pos == last_pos) {
|
||||
++m_it;
|
||||
if (m_it != m_last) {
|
||||
m_pos = m_it->c_str();
|
||||
} else {
|
||||
m_pos = nullptr;
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const_iterator operator++(int) {
|
||||
const_iterator tmp(*this);
|
||||
operator++();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
bool operator==(const const_iterator& rhs) const {
|
||||
return m_it == rhs.m_it && m_pos == rhs.m_pos;
|
||||
}
|
||||
|
||||
bool operator!=(const const_iterator& rhs) const {
|
||||
return !(*this == rhs);
|
||||
}
|
||||
|
||||
const char* operator*() const {
|
||||
assert(m_it != m_last);
|
||||
assert(m_pos != nullptr);
|
||||
return m_pos;
|
||||
}
|
||||
|
||||
}; // class const_iterator
|
||||
|
||||
const_iterator begin() const {
|
||||
if (m_chunks.front().empty()) {
|
||||
return end();
|
||||
}
|
||||
return const_iterator(m_chunks.begin(), m_chunks.end());
|
||||
}
|
||||
|
||||
const_iterator end() const {
|
||||
return const_iterator(m_chunks.end(), m_chunks.end());
|
||||
}
|
||||
|
||||
// These functions get you some idea how much memory was
|
||||
// used.
|
||||
size_t get_chunk_size() const noexcept {
|
||||
return m_chunk_size;
|
||||
}
|
||||
|
||||
size_t get_chunk_count() const noexcept {
|
||||
return m_chunks.size();
|
||||
}
|
||||
|
||||
size_t get_used_bytes_in_last_chunk() const noexcept {
|
||||
return m_chunks.front().size();
|
||||
}
|
||||
|
||||
}; // class StringStore
|
||||
|
||||
struct StrComp {
|
||||
|
||||
bool operator()(const char* lhs, const char* rhs) const {
|
||||
return strcmp(lhs, rhs) < 0;
|
||||
}
|
||||
|
||||
}; // struct StrComp
|
||||
|
||||
class StringTable {
|
||||
|
||||
// This is the maximum number of entries in a string table.
|
||||
// This should never be reached in practice but we better
|
||||
// make sure it doesn't. If we had max_uncompressed_blob_size
|
||||
// many entries, we are sure they would never fit into a PBF
|
||||
// Blob.
|
||||
static constexpr const uint32_t max_entries = max_uncompressed_blob_size;
|
||||
|
||||
StringStore m_strings;
|
||||
std::map<const char*, size_t, StrComp> m_index;
|
||||
uint32_t m_size;
|
||||
|
||||
public:
|
||||
|
||||
StringTable() :
|
||||
m_strings(1024 * 1024),
|
||||
m_index(),
|
||||
m_size(0) {
|
||||
m_strings.add("");
|
||||
}
|
||||
|
||||
void clear() {
|
||||
m_strings.clear();
|
||||
m_index.clear();
|
||||
m_size = 0;
|
||||
m_strings.add("");
|
||||
}
|
||||
|
||||
uint32_t size() const noexcept {
|
||||
return m_size + 1;
|
||||
}
|
||||
|
||||
uint32_t add(const char* s) {
|
||||
auto f = m_index.find(s);
|
||||
if (f != m_index.end()) {
|
||||
return uint32_t(f->second);
|
||||
}
|
||||
|
||||
const char* cs = m_strings.add(s);
|
||||
m_index[cs] = ++m_size;
|
||||
|
||||
if (m_size > max_entries) {
|
||||
throw osmium::pbf_error("string table has too many entries");
|
||||
}
|
||||
|
||||
return m_size;
|
||||
}
|
||||
|
||||
StringStore::const_iterator begin() const {
|
||||
return m_strings.begin();
|
||||
}
|
||||
|
||||
StringStore::const_iterator end() const {
|
||||
return m_strings.end();
|
||||
}
|
||||
|
||||
}; // class StringTable
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_STRING_TABLE_HPP
|
||||
@@ -0,0 +1,209 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_STRING_UTIL_HPP
|
||||
#define OSMIUM_IO_DETAIL_STRING_UTIL_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <utf8.h>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
#ifndef _MSC_VER
|
||||
# define SNPRINTF std::snprintf
|
||||
#else
|
||||
# define SNPRINTF _snprintf
|
||||
#endif
|
||||
|
||||
template <typename... TArgs>
|
||||
inline int string_snprintf(std::string& out,
|
||||
size_t old_size,
|
||||
size_t max_size,
|
||||
const char* format,
|
||||
TArgs&&... args) {
|
||||
out.resize(old_size + max_size);
|
||||
|
||||
return SNPRINTF(max_size ? const_cast<char*>(out.c_str()) + old_size : nullptr,
|
||||
max_size,
|
||||
format,
|
||||
std::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
#undef SNPRINTF
|
||||
|
||||
/**
|
||||
* This is a helper function for writing printf-like formatted
|
||||
* data into a std::string.
|
||||
*
|
||||
* @param out The data will be appended to this string.
|
||||
* @param format A string with formatting instructions a la printf.
|
||||
* @param args Any further arguments like in printf.
|
||||
* @throws std::bad_alloc If the string needed to grow and there
|
||||
* wasn't enough memory.
|
||||
*/
|
||||
template <typename... TArgs>
|
||||
inline void append_printf_formatted_string(std::string& out,
|
||||
const char* format,
|
||||
TArgs&&... args) {
|
||||
|
||||
// First try to write string with the max_size, if that doesn't
|
||||
// work snprintf will tell us how much space it needs. We
|
||||
// reserve that much space and try again. So this will always
|
||||
// work, even if the output is larger than the given max_size.
|
||||
//
|
||||
// Unfortunately this trick doesn't work on Windows, because
|
||||
// the _snprintf() function there only returns the length it
|
||||
// needs if max_size==0 and the buffer pointer is the null
|
||||
// pointer. So we have to take this into account.
|
||||
|
||||
#ifndef _MSC_VER
|
||||
static const size_t max_size = 100;
|
||||
#else
|
||||
static const size_t max_size = 0;
|
||||
#endif
|
||||
|
||||
size_t old_size = out.size();
|
||||
|
||||
int len = string_snprintf(out,
|
||||
old_size,
|
||||
max_size,
|
||||
format,
|
||||
std::forward<TArgs>(args)...);
|
||||
assert(len > 0);
|
||||
|
||||
if (size_t(len) >= max_size) {
|
||||
#ifndef NDEBUG
|
||||
int len2 =
|
||||
#endif
|
||||
string_snprintf(out,
|
||||
old_size,
|
||||
size_t(len) + 1,
|
||||
format,
|
||||
std::forward<TArgs>(args)...);
|
||||
assert(len2 == len);
|
||||
}
|
||||
|
||||
out.resize(old_size + size_t(len));
|
||||
}
|
||||
|
||||
inline void append_utf8_encoded_string(std::string& out, const char* data) {
|
||||
const char* end = data + std::strlen(data);
|
||||
|
||||
while (data != end) {
|
||||
const char* last = data;
|
||||
uint32_t c = utf8::next(data, end);
|
||||
|
||||
// This is a list of Unicode code points that we let
|
||||
// through instead of escaping them. It is incomplete
|
||||
// and can be extended later.
|
||||
// Generally we don't want to let through any character
|
||||
// that has special meaning in the OPL format such as
|
||||
// space, comma, @, etc. and any non-printing characters.
|
||||
if ((0x0021 <= c && c <= 0x0024) ||
|
||||
(0x0026 <= c && c <= 0x002b) ||
|
||||
(0x002d <= c && c <= 0x003c) ||
|
||||
(0x003e <= c && c <= 0x003f) ||
|
||||
(0x0041 <= c && c <= 0x007e) ||
|
||||
(0x00a1 <= c && c <= 0x00ac) ||
|
||||
(0x00ae <= c && c <= 0x05ff)) {
|
||||
out.append(last, data);
|
||||
} else {
|
||||
out += '%';
|
||||
if (c <= 0xff) {
|
||||
append_printf_formatted_string(out, "%02x", c);
|
||||
} else {
|
||||
append_printf_formatted_string(out, "%04x", c);
|
||||
}
|
||||
out += '%';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void append_xml_encoded_string(std::string& out, const char* data) {
|
||||
for (; *data != '\0'; ++data) {
|
||||
switch(*data) {
|
||||
case '&': out += "&"; break;
|
||||
case '\"': out += """; break;
|
||||
case '\'': out += "'"; break;
|
||||
case '<': out += "<"; break;
|
||||
case '>': out += ">"; break;
|
||||
case '\n': out += "
"; break;
|
||||
case '\r': out += "
"; break;
|
||||
case '\t': out += "	"; break;
|
||||
default: out += *data; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline void append_debug_encoded_string(std::string& out, const char* data, const char* prefix, const char* suffix) {
|
||||
const char* end = data + std::strlen(data);
|
||||
|
||||
while (data != end) {
|
||||
const char* last = data;
|
||||
uint32_t c = utf8::next(data, end);
|
||||
|
||||
// This is a list of Unicode code points that we let
|
||||
// through instead of escaping them. It is incomplete
|
||||
// and can be extended later.
|
||||
// Generally we don't want to let through any
|
||||
// non-printing characters.
|
||||
if ((0x0020 <= c && c <= 0x0021) ||
|
||||
(0x0023 <= c && c <= 0x003b) ||
|
||||
(0x003d == c) ||
|
||||
(0x003f <= c && c <= 0x007e) ||
|
||||
(0x00a1 <= c && c <= 0x00ac) ||
|
||||
(0x00ae <= c && c <= 0x05ff)) {
|
||||
out.append(last, data);
|
||||
} else {
|
||||
out.append(prefix);
|
||||
append_printf_formatted_string(out, "<U+%04X>", c);
|
||||
out.append(suffix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_STRING_UTIL_HPP
|
||||
@@ -0,0 +1,107 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_WRITE_THREAD_HPP
|
||||
#define OSMIUM_IO_DETAIL_WRITE_THREAD_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <string>
|
||||
|
||||
#include <osmium/io/compression.hpp>
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/thread/util.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* This codes runs in its own thread, getting data from the given
|
||||
* queue, (optionally) compressing it, and writing it to the output
|
||||
* file.
|
||||
*/
|
||||
class WriteThread {
|
||||
|
||||
queue_wrapper<std::string> m_queue;
|
||||
std::unique_ptr<osmium::io::Compressor> m_compressor;
|
||||
std::promise<bool> m_promise;
|
||||
|
||||
public:
|
||||
|
||||
WriteThread(future_string_queue_type& input_queue,
|
||||
std::unique_ptr<osmium::io::Compressor>&& compressor,
|
||||
std::promise<bool>&& promise) :
|
||||
m_queue(input_queue),
|
||||
m_compressor(std::move(compressor)),
|
||||
m_promise(std::move(promise)) {
|
||||
}
|
||||
|
||||
WriteThread(const WriteThread&) = delete;
|
||||
WriteThread& operator=(const WriteThread&) = delete;
|
||||
|
||||
WriteThread(WriteThread&&) = delete;
|
||||
WriteThread& operator=(WriteThread&&) = delete;
|
||||
|
||||
~WriteThread() noexcept = default;
|
||||
|
||||
void operator()() {
|
||||
osmium::thread::set_thread_name("_osmium_write");
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
std::string data = m_queue.pop();
|
||||
if (at_end_of_data(data)) {
|
||||
break;
|
||||
}
|
||||
m_compressor->write(data);
|
||||
}
|
||||
m_compressor->close();
|
||||
m_promise.set_value(true);
|
||||
} catch (...) {
|
||||
m_promise.set_exception(std::current_exception());
|
||||
m_queue.drain();
|
||||
}
|
||||
}
|
||||
|
||||
}; // class WriteThread
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_WRITE_THREAD_HPP
|
||||
@@ -0,0 +1,679 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_XML_INPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_XML_INPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include <expat.h>
|
||||
|
||||
#include <osmium/builder/builder.hpp>
|
||||
#include <osmium/builder/osm_object_builder.hpp>
|
||||
#include <osmium/io/detail/input_format.hpp>
|
||||
#include <osmium/io/detail/queue_util.hpp>
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/osm.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/entity_bits.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/types_from_string.hpp>
|
||||
#include <osmium/thread/queue.hpp>
|
||||
#include <osmium/thread/util.hpp>
|
||||
#include <osmium/util/cast.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
/**
|
||||
* Exception thrown when the XML parser failed. The exception contains
|
||||
* (if available) information about the place where the error happened
|
||||
* and the type of error.
|
||||
*/
|
||||
struct xml_error : public io_error {
|
||||
|
||||
unsigned long line;
|
||||
unsigned long column;
|
||||
XML_Error error_code;
|
||||
std::string error_string;
|
||||
|
||||
explicit xml_error(XML_Parser parser) :
|
||||
io_error(std::string("XML parsing error at line ")
|
||||
+ std::to_string(XML_GetCurrentLineNumber(parser))
|
||||
+ ", column "
|
||||
+ std::to_string(XML_GetCurrentColumnNumber(parser))
|
||||
+ ": "
|
||||
+ XML_ErrorString(XML_GetErrorCode(parser))),
|
||||
line(XML_GetCurrentLineNumber(parser)),
|
||||
column(XML_GetCurrentColumnNumber(parser)),
|
||||
error_code(XML_GetErrorCode(parser)),
|
||||
error_string(XML_ErrorString(error_code)) {
|
||||
}
|
||||
|
||||
explicit xml_error(const std::string& message) :
|
||||
io_error(message),
|
||||
line(0),
|
||||
column(0),
|
||||
error_code(),
|
||||
error_string(message) {
|
||||
}
|
||||
|
||||
}; // struct xml_error
|
||||
|
||||
/**
|
||||
* Exception thrown when an OSM XML files contains no version attribute
|
||||
* on the 'osm' element or if the version is unknown.
|
||||
*/
|
||||
struct format_version_error : public io_error {
|
||||
|
||||
std::string version;
|
||||
|
||||
explicit format_version_error() :
|
||||
io_error("Can not read file without version (missing version attribute on osm element)."),
|
||||
version() {
|
||||
}
|
||||
|
||||
explicit format_version_error(const char* v) :
|
||||
io_error(std::string("Can not read file with version ") + v),
|
||||
version(v) {
|
||||
}
|
||||
|
||||
}; // struct format_version_error
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
class XMLParser : public Parser {
|
||||
|
||||
static constexpr int buffer_size = 2 * 1000 * 1000;
|
||||
|
||||
enum class context {
|
||||
root,
|
||||
top,
|
||||
node,
|
||||
way,
|
||||
relation,
|
||||
changeset,
|
||||
discussion,
|
||||
comment,
|
||||
comment_text,
|
||||
ignored_node,
|
||||
ignored_way,
|
||||
ignored_relation,
|
||||
ignored_changeset,
|
||||
in_object
|
||||
}; // enum class context
|
||||
|
||||
context m_context;
|
||||
context m_last_context;
|
||||
|
||||
/**
|
||||
* This is used only for change files which contain create, modify,
|
||||
* and delete sections.
|
||||
*/
|
||||
bool m_in_delete_section;
|
||||
|
||||
osmium::io::Header m_header;
|
||||
|
||||
osmium::memory::Buffer m_buffer;
|
||||
|
||||
std::unique_ptr<osmium::builder::NodeBuilder> m_node_builder;
|
||||
std::unique_ptr<osmium::builder::WayBuilder> m_way_builder;
|
||||
std::unique_ptr<osmium::builder::RelationBuilder> m_relation_builder;
|
||||
std::unique_ptr<osmium::builder::ChangesetBuilder> m_changeset_builder;
|
||||
std::unique_ptr<osmium::builder::ChangesetDiscussionBuilder> m_changeset_discussion_builder;
|
||||
|
||||
std::unique_ptr<osmium::builder::TagListBuilder> m_tl_builder;
|
||||
std::unique_ptr<osmium::builder::WayNodeListBuilder> m_wnl_builder;
|
||||
std::unique_ptr<osmium::builder::RelationMemberListBuilder> m_rml_builder;
|
||||
|
||||
std::string m_comment_text;
|
||||
|
||||
/**
|
||||
* A C++ wrapper for the Expat parser that makes sure no memory is leaked.
|
||||
*/
|
||||
template <typename T>
|
||||
class ExpatXMLParser {
|
||||
|
||||
XML_Parser m_parser;
|
||||
|
||||
static void XMLCALL start_element_wrapper(void* data, const XML_Char* element, const XML_Char** attrs) {
|
||||
static_cast<XMLParser*>(data)->start_element(element, attrs);
|
||||
}
|
||||
|
||||
static void XMLCALL end_element_wrapper(void* data, const XML_Char* element) {
|
||||
static_cast<XMLParser*>(data)->end_element(element);
|
||||
}
|
||||
|
||||
static void XMLCALL character_data_wrapper(void* data, const XML_Char* text, int len) {
|
||||
static_cast<XMLParser*>(data)->characters(text, len);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
explicit ExpatXMLParser(T* callback_object) :
|
||||
m_parser(XML_ParserCreate(nullptr)) {
|
||||
if (!m_parser) {
|
||||
throw osmium::io_error("Internal error: Can not create parser");
|
||||
}
|
||||
XML_SetUserData(m_parser, callback_object);
|
||||
XML_SetElementHandler(m_parser, start_element_wrapper, end_element_wrapper);
|
||||
XML_SetCharacterDataHandler(m_parser, character_data_wrapper);
|
||||
}
|
||||
|
||||
ExpatXMLParser(const ExpatXMLParser&) = delete;
|
||||
ExpatXMLParser(ExpatXMLParser&&) = delete;
|
||||
|
||||
ExpatXMLParser& operator=(const ExpatXMLParser&) = delete;
|
||||
ExpatXMLParser& operator=(ExpatXMLParser&&) = delete;
|
||||
|
||||
~ExpatXMLParser() noexcept {
|
||||
XML_ParserFree(m_parser);
|
||||
}
|
||||
|
||||
void operator()(const std::string& data, bool last) {
|
||||
if (XML_Parse(m_parser, data.data(), static_cast_with_assert<int>(data.size()), last) == XML_STATUS_ERROR) {
|
||||
throw osmium::xml_error(m_parser);
|
||||
}
|
||||
}
|
||||
|
||||
}; // class ExpatXMLParser
|
||||
|
||||
template <typename T>
|
||||
static void check_attributes(const XML_Char** attrs, T check) {
|
||||
while (*attrs) {
|
||||
check(attrs[0], attrs[1]);
|
||||
attrs += 2;
|
||||
}
|
||||
}
|
||||
|
||||
const char* init_object(osmium::OSMObject& object, const XML_Char** attrs) {
|
||||
const char* user = "";
|
||||
|
||||
if (m_in_delete_section) {
|
||||
object.set_visible(false);
|
||||
}
|
||||
|
||||
osmium::Location location;
|
||||
|
||||
check_attributes(attrs, [&location, &user, &object](const XML_Char* name, const XML_Char* value) {
|
||||
if (!strcmp(name, "lon")) {
|
||||
location.set_lon(std::atof(value)); // XXX doesn't detect garbage after the number
|
||||
} else if (!strcmp(name, "lat")) {
|
||||
location.set_lat(std::atof(value)); // XXX doesn't detect garbage after the number
|
||||
} else if (!strcmp(name, "user")) {
|
||||
user = value;
|
||||
} else {
|
||||
object.set_attribute(name, value);
|
||||
}
|
||||
});
|
||||
|
||||
if (location && object.type() == osmium::item_type::node) {
|
||||
static_cast<osmium::Node&>(object).set_location(location);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
void init_changeset(osmium::builder::ChangesetBuilder* builder, const XML_Char** attrs) {
|
||||
const char* user = "";
|
||||
osmium::Changeset& new_changeset = builder->object();
|
||||
|
||||
osmium::Location min;
|
||||
osmium::Location max;
|
||||
check_attributes(attrs, [&min, &max, &user, &new_changeset](const XML_Char* name, const XML_Char* value) {
|
||||
if (!strcmp(name, "min_lon")) {
|
||||
min.set_lon(atof(value));
|
||||
} else if (!strcmp(name, "min_lat")) {
|
||||
min.set_lat(atof(value));
|
||||
} else if (!strcmp(name, "max_lon")) {
|
||||
max.set_lon(atof(value));
|
||||
} else if (!strcmp(name, "max_lat")) {
|
||||
max.set_lat(atof(value));
|
||||
} else if (!strcmp(name, "user")) {
|
||||
user = value;
|
||||
} else {
|
||||
new_changeset.set_attribute(name, value);
|
||||
}
|
||||
});
|
||||
|
||||
new_changeset.bounds().extend(min);
|
||||
new_changeset.bounds().extend(max);
|
||||
|
||||
builder->add_user(user);
|
||||
}
|
||||
|
||||
void get_tag(osmium::builder::Builder* builder, const XML_Char** attrs) {
|
||||
const char* k = "";
|
||||
const char* v = "";
|
||||
check_attributes(attrs, [&k, &v](const XML_Char* name, const XML_Char* value) {
|
||||
if (name[0] == 'k' && name[1] == 0) {
|
||||
k = value;
|
||||
} else if (name[0] == 'v' && name[1] == 0) {
|
||||
v = value;
|
||||
}
|
||||
});
|
||||
if (!m_tl_builder) {
|
||||
m_tl_builder = std::unique_ptr<osmium::builder::TagListBuilder>(new osmium::builder::TagListBuilder(m_buffer, builder));
|
||||
}
|
||||
m_tl_builder->add_tag(k, v);
|
||||
}
|
||||
|
||||
void mark_header_as_done() {
|
||||
set_header_value(m_header);
|
||||
}
|
||||
|
||||
void start_element(const XML_Char* element, const XML_Char** attrs) {
|
||||
switch (m_context) {
|
||||
case context::root:
|
||||
if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) {
|
||||
if (!strcmp(element, "osmChange")) {
|
||||
m_header.set_has_multiple_object_versions(true);
|
||||
}
|
||||
check_attributes(attrs, [this](const XML_Char* name, const XML_Char* value) {
|
||||
if (!strcmp(name, "version")) {
|
||||
m_header.set("version", value);
|
||||
if (strcmp(value, "0.6")) {
|
||||
throw osmium::format_version_error(value);
|
||||
}
|
||||
} else if (!strcmp(name, "generator")) {
|
||||
m_header.set("generator", value);
|
||||
}
|
||||
});
|
||||
if (m_header.get("version") == "") {
|
||||
throw osmium::format_version_error();
|
||||
}
|
||||
} else {
|
||||
throw osmium::xml_error(std::string("Unknown top-level element: ") + element);
|
||||
}
|
||||
m_context = context::top;
|
||||
break;
|
||||
case context::top:
|
||||
assert(!m_tl_builder);
|
||||
if (!strcmp(element, "node")) {
|
||||
mark_header_as_done();
|
||||
if (read_types() & osmium::osm_entity_bits::node) {
|
||||
m_node_builder = std::unique_ptr<osmium::builder::NodeBuilder>(new osmium::builder::NodeBuilder(m_buffer));
|
||||
m_node_builder->add_user(init_object(m_node_builder->object(), attrs));
|
||||
m_context = context::node;
|
||||
} else {
|
||||
m_context = context::ignored_node;
|
||||
}
|
||||
} else if (!strcmp(element, "way")) {
|
||||
mark_header_as_done();
|
||||
if (read_types() & osmium::osm_entity_bits::way) {
|
||||
m_way_builder = std::unique_ptr<osmium::builder::WayBuilder>(new osmium::builder::WayBuilder(m_buffer));
|
||||
m_way_builder->add_user(init_object(m_way_builder->object(), attrs));
|
||||
m_context = context::way;
|
||||
} else {
|
||||
m_context = context::ignored_way;
|
||||
}
|
||||
} else if (!strcmp(element, "relation")) {
|
||||
mark_header_as_done();
|
||||
if (read_types() & osmium::osm_entity_bits::relation) {
|
||||
m_relation_builder = std::unique_ptr<osmium::builder::RelationBuilder>(new osmium::builder::RelationBuilder(m_buffer));
|
||||
m_relation_builder->add_user(init_object(m_relation_builder->object(), attrs));
|
||||
m_context = context::relation;
|
||||
} else {
|
||||
m_context = context::ignored_relation;
|
||||
}
|
||||
} else if (!strcmp(element, "changeset")) {
|
||||
mark_header_as_done();
|
||||
if (read_types() & osmium::osm_entity_bits::changeset) {
|
||||
m_changeset_builder = std::unique_ptr<osmium::builder::ChangesetBuilder>(new osmium::builder::ChangesetBuilder(m_buffer));
|
||||
init_changeset(m_changeset_builder.get(), attrs);
|
||||
m_context = context::changeset;
|
||||
} else {
|
||||
m_context = context::ignored_changeset;
|
||||
}
|
||||
} else if (!strcmp(element, "bounds")) {
|
||||
osmium::Location min;
|
||||
osmium::Location max;
|
||||
check_attributes(attrs, [&min, &max](const XML_Char* name, const XML_Char* value) {
|
||||
if (!strcmp(name, "minlon")) {
|
||||
min.set_lon(atof(value));
|
||||
} else if (!strcmp(name, "minlat")) {
|
||||
min.set_lat(atof(value));
|
||||
} else if (!strcmp(name, "maxlon")) {
|
||||
max.set_lon(atof(value));
|
||||
} else if (!strcmp(name, "maxlat")) {
|
||||
max.set_lat(atof(value));
|
||||
}
|
||||
});
|
||||
osmium::Box box;
|
||||
box.extend(min).extend(max);
|
||||
m_header.add_box(box);
|
||||
} else if (!strcmp(element, "delete")) {
|
||||
m_in_delete_section = true;
|
||||
}
|
||||
break;
|
||||
case context::node:
|
||||
m_last_context = context::node;
|
||||
m_context = context::in_object;
|
||||
if (!strcmp(element, "tag")) {
|
||||
get_tag(m_node_builder.get(), attrs);
|
||||
}
|
||||
break;
|
||||
case context::way:
|
||||
m_last_context = context::way;
|
||||
m_context = context::in_object;
|
||||
if (!strcmp(element, "nd")) {
|
||||
m_tl_builder.reset();
|
||||
|
||||
if (!m_wnl_builder) {
|
||||
m_wnl_builder = std::unique_ptr<osmium::builder::WayNodeListBuilder>(new osmium::builder::WayNodeListBuilder(m_buffer, m_way_builder.get()));
|
||||
}
|
||||
|
||||
check_attributes(attrs, [this](const XML_Char* name, const XML_Char* value) {
|
||||
if (!strcmp(name, "ref")) {
|
||||
m_wnl_builder->add_node_ref(osmium::string_to_object_id(value));
|
||||
}
|
||||
});
|
||||
} else if (!strcmp(element, "tag")) {
|
||||
m_wnl_builder.reset();
|
||||
get_tag(m_way_builder.get(), attrs);
|
||||
}
|
||||
break;
|
||||
case context::relation:
|
||||
m_last_context = context::relation;
|
||||
m_context = context::in_object;
|
||||
if (!strcmp(element, "member")) {
|
||||
m_tl_builder.reset();
|
||||
|
||||
if (!m_rml_builder) {
|
||||
m_rml_builder = std::unique_ptr<osmium::builder::RelationMemberListBuilder>(new osmium::builder::RelationMemberListBuilder(m_buffer, m_relation_builder.get()));
|
||||
}
|
||||
|
||||
item_type type = item_type::undefined;
|
||||
object_id_type ref = 0;
|
||||
const char* role = "";
|
||||
check_attributes(attrs, [&type, &ref, &role](const XML_Char* name, const XML_Char* value) {
|
||||
if (!strcmp(name, "type")) {
|
||||
type = char_to_item_type(value[0]);
|
||||
} else if (!strcmp(name, "ref")) {
|
||||
ref = osmium::string_to_object_id(value);
|
||||
} else if (!strcmp(name, "role")) {
|
||||
role = static_cast<const char*>(value);
|
||||
}
|
||||
});
|
||||
if (type != item_type::node && type != item_type::way && type != item_type::relation) {
|
||||
throw osmium::xml_error("Unknown type on relation member");
|
||||
}
|
||||
if (ref == 0) {
|
||||
throw osmium::xml_error("Missing ref on relation member");
|
||||
}
|
||||
m_rml_builder->add_member(type, ref, role);
|
||||
} else if (!strcmp(element, "tag")) {
|
||||
m_rml_builder.reset();
|
||||
get_tag(m_relation_builder.get(), attrs);
|
||||
}
|
||||
break;
|
||||
case context::changeset:
|
||||
m_last_context = context::changeset;
|
||||
if (!strcmp(element, "discussion")) {
|
||||
m_context = context::discussion;
|
||||
m_tl_builder.reset();
|
||||
if (!m_changeset_discussion_builder) {
|
||||
m_changeset_discussion_builder = std::unique_ptr<osmium::builder::ChangesetDiscussionBuilder>(new osmium::builder::ChangesetDiscussionBuilder(m_buffer, m_changeset_builder.get()));
|
||||
}
|
||||
} else if (!strcmp(element, "tag")) {
|
||||
m_context = context::in_object;
|
||||
m_changeset_discussion_builder.reset();
|
||||
get_tag(m_changeset_builder.get(), attrs);
|
||||
}
|
||||
break;
|
||||
case context::discussion:
|
||||
if (!strcmp(element, "comment")) {
|
||||
m_context = context::comment;
|
||||
osmium::Timestamp date;
|
||||
osmium::user_id_type uid = 0;
|
||||
const char* user = "";
|
||||
check_attributes(attrs, [&date, &uid, &user](const XML_Char* name, const XML_Char* value) {
|
||||
if (!strcmp(name, "date")) {
|
||||
date = osmium::Timestamp(value);
|
||||
} else if (!strcmp(name, "uid")) {
|
||||
uid = osmium::string_to_user_id(value);
|
||||
} else if (!strcmp(name, "user")) {
|
||||
user = static_cast<const char*>(value);
|
||||
}
|
||||
});
|
||||
m_changeset_discussion_builder->add_comment(date, uid, user);
|
||||
}
|
||||
break;
|
||||
case context::comment:
|
||||
if (!strcmp(element, "text")) {
|
||||
m_context = context::comment_text;
|
||||
}
|
||||
break;
|
||||
case context::comment_text:
|
||||
break;
|
||||
case context::ignored_node:
|
||||
break;
|
||||
case context::ignored_way:
|
||||
break;
|
||||
case context::ignored_relation:
|
||||
break;
|
||||
case context::ignored_changeset:
|
||||
break;
|
||||
case context::in_object:
|
||||
assert(false); // should never be here
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void end_element(const XML_Char* element) {
|
||||
switch (m_context) {
|
||||
case context::root:
|
||||
assert(false); // should never be here
|
||||
break;
|
||||
case context::top:
|
||||
if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) {
|
||||
mark_header_as_done();
|
||||
m_context = context::root;
|
||||
} else if (!strcmp(element, "delete")) {
|
||||
m_in_delete_section = false;
|
||||
}
|
||||
break;
|
||||
case context::node:
|
||||
assert(!strcmp(element, "node"));
|
||||
m_tl_builder.reset();
|
||||
m_node_builder.reset();
|
||||
m_buffer.commit();
|
||||
m_context = context::top;
|
||||
flush_buffer();
|
||||
break;
|
||||
case context::way:
|
||||
assert(!strcmp(element, "way"));
|
||||
m_tl_builder.reset();
|
||||
m_wnl_builder.reset();
|
||||
m_way_builder.reset();
|
||||
m_buffer.commit();
|
||||
m_context = context::top;
|
||||
flush_buffer();
|
||||
break;
|
||||
case context::relation:
|
||||
assert(!strcmp(element, "relation"));
|
||||
m_tl_builder.reset();
|
||||
m_rml_builder.reset();
|
||||
m_relation_builder.reset();
|
||||
m_buffer.commit();
|
||||
m_context = context::top;
|
||||
flush_buffer();
|
||||
break;
|
||||
case context::changeset:
|
||||
assert(!strcmp(element, "changeset"));
|
||||
m_tl_builder.reset();
|
||||
m_changeset_discussion_builder.reset();
|
||||
m_changeset_builder.reset();
|
||||
m_buffer.commit();
|
||||
m_context = context::top;
|
||||
flush_buffer();
|
||||
break;
|
||||
case context::discussion:
|
||||
assert(!strcmp(element, "discussion"));
|
||||
m_context = context::changeset;
|
||||
break;
|
||||
case context::comment:
|
||||
assert(!strcmp(element, "comment"));
|
||||
m_context = context::discussion;
|
||||
break;
|
||||
case context::comment_text:
|
||||
assert(!strcmp(element, "text"));
|
||||
m_context = context::comment;
|
||||
m_changeset_discussion_builder->add_comment_text(m_comment_text);
|
||||
break;
|
||||
case context::in_object:
|
||||
m_context = m_last_context;
|
||||
break;
|
||||
case context::ignored_node:
|
||||
if (!strcmp(element, "node")) {
|
||||
m_context = context::top;
|
||||
}
|
||||
break;
|
||||
case context::ignored_way:
|
||||
if (!strcmp(element, "way")) {
|
||||
m_context = context::top;
|
||||
}
|
||||
break;
|
||||
case context::ignored_relation:
|
||||
if (!strcmp(element, "relation")) {
|
||||
m_context = context::top;
|
||||
}
|
||||
break;
|
||||
case context::ignored_changeset:
|
||||
if (!strcmp(element, "changeset")) {
|
||||
m_context = context::top;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void characters(const XML_Char* text, int len) {
|
||||
if (m_context == context::comment_text) {
|
||||
m_comment_text.append(text, len);
|
||||
} else {
|
||||
m_comment_text.resize(0);
|
||||
}
|
||||
}
|
||||
|
||||
void flush_buffer() {
|
||||
if (m_buffer.committed() > buffer_size / 10 * 9) {
|
||||
send_to_output_queue(std::move(m_buffer));
|
||||
osmium::memory::Buffer buffer(buffer_size);
|
||||
using std::swap;
|
||||
swap(m_buffer, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
XMLParser(future_string_queue_type& input_queue,
|
||||
future_buffer_queue_type& output_queue,
|
||||
std::promise<osmium::io::Header>& header_promise,
|
||||
osmium::osm_entity_bits::type read_types) :
|
||||
Parser(input_queue, output_queue, header_promise, read_types),
|
||||
m_context(context::root),
|
||||
m_last_context(context::root),
|
||||
m_in_delete_section(false),
|
||||
m_header(),
|
||||
m_buffer(buffer_size),
|
||||
m_node_builder(),
|
||||
m_way_builder(),
|
||||
m_relation_builder(),
|
||||
m_changeset_builder(),
|
||||
m_changeset_discussion_builder(),
|
||||
m_tl_builder(),
|
||||
m_wnl_builder(),
|
||||
m_rml_builder() {
|
||||
}
|
||||
|
||||
~XMLParser() noexcept final = default;
|
||||
|
||||
void run() final {
|
||||
osmium::thread::set_thread_name("_osmium_xml_in");
|
||||
|
||||
ExpatXMLParser<XMLParser> parser(this);
|
||||
|
||||
while (!input_done()) {
|
||||
std::string data = get_input();
|
||||
parser(data, input_done());
|
||||
if (read_types() == osmium::osm_entity_bits::nothing && header_is_done()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
mark_header_as_done();
|
||||
|
||||
if (m_buffer.committed() > 0) {
|
||||
send_to_output_queue(std::move(m_buffer));
|
||||
}
|
||||
}
|
||||
|
||||
}; // class XMLParser
|
||||
|
||||
// we want the register_parser() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_xml_parser = ParserFactory::instance().register_parser(
|
||||
file_format::xml,
|
||||
[](future_string_queue_type& input_queue,
|
||||
future_buffer_queue_type& output_queue,
|
||||
std::promise<osmium::io::Header>& header_promise,
|
||||
osmium::osm_entity_bits::type read_which_entities) {
|
||||
return std::unique_ptr<Parser>(new XMLParser(input_queue, output_queue, header_promise, read_which_entities));
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_xml_parser() noexcept {
|
||||
return registered_xml_parser;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_XML_INPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,473 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_XML_OUTPUT_FORMAT_HPP
|
||||
#define OSMIUM_IO_DETAIL_XML_OUTPUT_FORMAT_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cinttypes>
|
||||
#include <cstddef>
|
||||
#include <cstdio>
|
||||
#include <future>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
#include <osmium/io/detail/output_format.hpp>
|
||||
#include <osmium/io/file.hpp>
|
||||
#include <osmium/io/file_format.hpp>
|
||||
#include <osmium/io/header.hpp>
|
||||
#include <osmium/memory/buffer.hpp>
|
||||
#include <osmium/memory/collection.hpp>
|
||||
#include <osmium/osm/box.hpp>
|
||||
#include <osmium/osm/changeset.hpp>
|
||||
#include <osmium/osm/item_type.hpp>
|
||||
#include <osmium/osm/location.hpp>
|
||||
#include <osmium/osm/node.hpp>
|
||||
#include <osmium/osm/object.hpp>
|
||||
#include <osmium/osm/relation.hpp>
|
||||
#include <osmium/osm/tag.hpp>
|
||||
#include <osmium/osm/timestamp.hpp>
|
||||
#include <osmium/osm/types.hpp>
|
||||
#include <osmium/osm/way.hpp>
|
||||
#include <osmium/thread/pool.hpp>
|
||||
#include <osmium/visitor.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
struct XMLWriteError {};
|
||||
|
||||
struct xml_output_options {
|
||||
|
||||
/// Should metadata of objects be added?
|
||||
bool add_metadata;
|
||||
|
||||
/// Should the visible flag be added to all OSM objects?
|
||||
bool add_visible_flag;
|
||||
|
||||
/**
|
||||
* Should <create>, <modify>, <delete> "operations" be added?
|
||||
* (This is used for .osc files.)
|
||||
*/
|
||||
bool use_change_ops;
|
||||
|
||||
};
|
||||
|
||||
class XMLOutputBlock : public OutputBlock {
|
||||
|
||||
// operation (create, modify, delete) for osc files
|
||||
enum class operation {
|
||||
op_none = 0,
|
||||
op_create = 1,
|
||||
op_modify = 2,
|
||||
op_delete = 3
|
||||
}; // enum class operation
|
||||
|
||||
operation m_last_op {operation::op_none};
|
||||
|
||||
xml_output_options m_options;
|
||||
|
||||
void write_spaces(int num) {
|
||||
for (; num != 0; --num) {
|
||||
*m_out += ' ';
|
||||
}
|
||||
}
|
||||
|
||||
int prefix_spaces() {
|
||||
return m_options.use_change_ops ? 4 : 2;
|
||||
}
|
||||
|
||||
void write_prefix() {
|
||||
write_spaces(prefix_spaces());
|
||||
}
|
||||
|
||||
void write_meta(const osmium::OSMObject& object) {
|
||||
output_formatted(" id=\"%" PRId64 "\"", object.id());
|
||||
|
||||
if (m_options.add_metadata) {
|
||||
if (object.version()) {
|
||||
output_formatted(" version=\"%d\"", object.version());
|
||||
}
|
||||
|
||||
if (object.timestamp()) {
|
||||
*m_out += " timestamp=\"";
|
||||
*m_out += object.timestamp().to_iso();
|
||||
*m_out += "\"";
|
||||
}
|
||||
|
||||
if (!object.user_is_anonymous()) {
|
||||
output_formatted(" uid=\"%d\" user=\"", object.uid());
|
||||
append_xml_encoded_string(*m_out, object.user());
|
||||
*m_out += "\"";
|
||||
}
|
||||
|
||||
if (object.changeset()) {
|
||||
output_formatted(" changeset=\"%d\"", object.changeset());
|
||||
}
|
||||
|
||||
if (m_options.add_visible_flag) {
|
||||
if (object.visible()) {
|
||||
*m_out += " visible=\"true\"";
|
||||
} else {
|
||||
*m_out += " visible=\"false\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void write_tags(const osmium::TagList& tags, int spaces) {
|
||||
for (const auto& tag : tags) {
|
||||
write_spaces(spaces);
|
||||
*m_out += " <tag k=\"";
|
||||
append_xml_encoded_string(*m_out, tag.key());
|
||||
*m_out += "\" v=\"";
|
||||
append_xml_encoded_string(*m_out, tag.value());
|
||||
*m_out += "\"/>\n";
|
||||
}
|
||||
}
|
||||
|
||||
void write_discussion(const osmium::ChangesetDiscussion& comments) {
|
||||
for (const auto& comment : comments) {
|
||||
output_formatted(" <comment uid=\"%d\" user=\"", comment.uid());
|
||||
append_xml_encoded_string(*m_out, comment.user());
|
||||
*m_out += "\" date=\"";
|
||||
*m_out += comment.date().to_iso();
|
||||
*m_out += "\">\n";
|
||||
*m_out += " <text>";
|
||||
append_xml_encoded_string(*m_out, comment.text());
|
||||
*m_out += "</text>\n </comment>\n";
|
||||
}
|
||||
*m_out += " </discussion>\n";
|
||||
}
|
||||
|
||||
void open_close_op_tag(const operation op = operation::op_none) {
|
||||
if (op == m_last_op) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (m_last_op) {
|
||||
case operation::op_none:
|
||||
break;
|
||||
case operation::op_create:
|
||||
*m_out += " </create>\n";
|
||||
break;
|
||||
case operation::op_modify:
|
||||
*m_out += " </modify>\n";
|
||||
break;
|
||||
case operation::op_delete:
|
||||
*m_out += " </delete>\n";
|
||||
break;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case operation::op_none:
|
||||
break;
|
||||
case operation::op_create:
|
||||
*m_out += " <create>\n";
|
||||
break;
|
||||
case operation::op_modify:
|
||||
*m_out += " <modify>\n";
|
||||
break;
|
||||
case operation::op_delete:
|
||||
*m_out += " <delete>\n";
|
||||
break;
|
||||
}
|
||||
|
||||
m_last_op = op;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
XMLOutputBlock(osmium::memory::Buffer&& buffer, const xml_output_options& options) :
|
||||
OutputBlock(std::move(buffer)),
|
||||
m_options(options) {
|
||||
}
|
||||
|
||||
XMLOutputBlock(const XMLOutputBlock&) = default;
|
||||
XMLOutputBlock& operator=(const XMLOutputBlock&) = default;
|
||||
|
||||
XMLOutputBlock(XMLOutputBlock&&) = default;
|
||||
XMLOutputBlock& operator=(XMLOutputBlock&&) = default;
|
||||
|
||||
~XMLOutputBlock() noexcept = default;
|
||||
|
||||
std::string operator()() {
|
||||
osmium::apply(m_input_buffer->cbegin(), m_input_buffer->cend(), *this);
|
||||
|
||||
if (m_options.use_change_ops) {
|
||||
open_close_op_tag();
|
||||
}
|
||||
|
||||
std::string out;
|
||||
using std::swap;
|
||||
swap(out, *m_out);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
void node(const osmium::Node& node) {
|
||||
if (m_options.use_change_ops) {
|
||||
open_close_op_tag(node.visible() ? (node.version() == 1 ? operation::op_create : operation::op_modify) : operation::op_delete);
|
||||
}
|
||||
|
||||
write_prefix();
|
||||
*m_out += "<node";
|
||||
|
||||
write_meta(node);
|
||||
|
||||
if (node.location()) {
|
||||
*m_out += " lat=\"";
|
||||
osmium::util::double2string(std::back_inserter(*m_out), node.location().lat_without_check(), 7);
|
||||
*m_out += "\" lon=\"";
|
||||
osmium::util::double2string(std::back_inserter(*m_out), node.location().lon_without_check(), 7);
|
||||
*m_out += "\"";
|
||||
}
|
||||
|
||||
if (node.tags().empty()) {
|
||||
*m_out += "/>\n";
|
||||
return;
|
||||
}
|
||||
|
||||
*m_out += ">\n";
|
||||
|
||||
write_tags(node.tags(), prefix_spaces());
|
||||
|
||||
write_prefix();
|
||||
*m_out += "</node>\n";
|
||||
}
|
||||
|
||||
void way(const osmium::Way& way) {
|
||||
if (m_options.use_change_ops) {
|
||||
open_close_op_tag(way.visible() ? (way.version() == 1 ? operation::op_create : operation::op_modify) : operation::op_delete);
|
||||
}
|
||||
|
||||
write_prefix();
|
||||
*m_out += "<way";
|
||||
write_meta(way);
|
||||
|
||||
if (way.tags().empty() && way.nodes().empty()) {
|
||||
*m_out += "/>\n";
|
||||
return;
|
||||
}
|
||||
|
||||
*m_out += ">\n";
|
||||
|
||||
for (const auto& node_ref : way.nodes()) {
|
||||
write_prefix();
|
||||
output_formatted(" <nd ref=\"%" PRId64 "\"/>\n", node_ref.ref());
|
||||
}
|
||||
|
||||
write_tags(way.tags(), prefix_spaces());
|
||||
|
||||
write_prefix();
|
||||
*m_out += "</way>\n";
|
||||
}
|
||||
|
||||
void relation(const osmium::Relation& relation) {
|
||||
if (m_options.use_change_ops) {
|
||||
open_close_op_tag(relation.visible() ? (relation.version() == 1 ? operation::op_create : operation::op_modify) : operation::op_delete);
|
||||
}
|
||||
|
||||
write_prefix();
|
||||
*m_out += "<relation";
|
||||
write_meta(relation);
|
||||
|
||||
if (relation.tags().empty() && relation.members().empty()) {
|
||||
*m_out += "/>\n";
|
||||
return;
|
||||
}
|
||||
|
||||
*m_out += ">\n";
|
||||
|
||||
for (const auto& member : relation.members()) {
|
||||
write_prefix();
|
||||
*m_out += " <member type=\"";
|
||||
*m_out += item_type_to_name(member.type());
|
||||
output_formatted("\" ref=\"%" PRId64 "\" role=\"", member.ref());
|
||||
append_xml_encoded_string(*m_out, member.role());
|
||||
*m_out += "\"/>\n";
|
||||
}
|
||||
|
||||
write_tags(relation.tags(), prefix_spaces());
|
||||
|
||||
write_prefix();
|
||||
*m_out += "</relation>\n";
|
||||
}
|
||||
|
||||
void changeset(const osmium::Changeset& changeset) {
|
||||
*m_out += " <changeset";
|
||||
|
||||
output_formatted(" id=\"%" PRId32 "\"", changeset.id());
|
||||
|
||||
if (changeset.created_at()) {
|
||||
*m_out += " created_at=\"";
|
||||
*m_out += changeset.created_at().to_iso();
|
||||
*m_out += "\"";
|
||||
}
|
||||
|
||||
if (changeset.closed_at()) {
|
||||
*m_out += " closed_at=\"";
|
||||
*m_out += changeset.closed_at().to_iso();
|
||||
*m_out += "\" open=\"false\"";
|
||||
} else {
|
||||
*m_out += " open=\"true\"";
|
||||
}
|
||||
|
||||
if (!changeset.user_is_anonymous()) {
|
||||
*m_out += " user=\"";
|
||||
append_xml_encoded_string(*m_out, changeset.user());
|
||||
output_formatted("\" uid=\"%d\"", changeset.uid());
|
||||
}
|
||||
|
||||
if (changeset.bounds()) {
|
||||
output_formatted(" min_lat=\"%.7f\"", changeset.bounds().bottom_left().lat_without_check());
|
||||
output_formatted(" min_lon=\"%.7f\"", changeset.bounds().bottom_left().lon_without_check());
|
||||
output_formatted(" max_lat=\"%.7f\"", changeset.bounds().top_right().lat_without_check());
|
||||
output_formatted(" max_lon=\"%.7f\"", changeset.bounds().top_right().lon_without_check());
|
||||
}
|
||||
|
||||
output_formatted(" num_changes=\"%" PRId32 "\"", changeset.num_changes());
|
||||
output_formatted(" comments_count=\"%" PRId32 "\"", changeset.num_comments());
|
||||
|
||||
// If there are no tags and no comments, we can close the
|
||||
// tag right here and are done.
|
||||
if (changeset.tags().empty() && changeset.num_comments() == 0) {
|
||||
*m_out += "/>\n";
|
||||
return;
|
||||
}
|
||||
|
||||
*m_out += ">\n";
|
||||
|
||||
write_tags(changeset.tags(), 0);
|
||||
|
||||
if (changeset.num_comments() > 0) {
|
||||
*m_out += " <discussion>\n";
|
||||
write_discussion(changeset.discussion());
|
||||
}
|
||||
|
||||
*m_out += " </changeset>\n";
|
||||
}
|
||||
|
||||
}; // class XMLOutputBlock
|
||||
|
||||
class XMLOutputFormat : public osmium::io::detail::OutputFormat, public osmium::handler::Handler {
|
||||
|
||||
xml_output_options m_options;
|
||||
|
||||
public:
|
||||
|
||||
XMLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) :
|
||||
OutputFormat(output_queue),
|
||||
m_options() {
|
||||
m_options.add_metadata = file.is_not_false("add_metadata");
|
||||
m_options.use_change_ops = file.is_true("xml_change_format");
|
||||
m_options.add_visible_flag = (file.has_multiple_object_versions() || file.is_true("force_visible_flag")) && !m_options.use_change_ops;
|
||||
}
|
||||
|
||||
XMLOutputFormat(const XMLOutputFormat&) = delete;
|
||||
XMLOutputFormat& operator=(const XMLOutputFormat&) = delete;
|
||||
|
||||
~XMLOutputFormat() noexcept final = default;
|
||||
|
||||
void write_header(const osmium::io::Header& header) final {
|
||||
std::string out = "<?xml version='1.0' encoding='UTF-8'?>\n";
|
||||
|
||||
if (m_options.use_change_ops) {
|
||||
out += "<osmChange version=\"0.6\" generator=\"";
|
||||
} else {
|
||||
out += "<osm version=\"0.6\"";
|
||||
|
||||
std::string xml_josm_upload = header.get("xml_josm_upload");
|
||||
if (xml_josm_upload == "true" || xml_josm_upload == "false") {
|
||||
out += " upload=\"";
|
||||
out += xml_josm_upload;
|
||||
out += "\"";
|
||||
}
|
||||
out += " generator=\"";
|
||||
}
|
||||
append_xml_encoded_string(out, header.get("generator").c_str());
|
||||
out += "\">\n";
|
||||
|
||||
for (const auto& box : header.boxes()) {
|
||||
out += " <bounds";
|
||||
append_printf_formatted_string(out, " minlon=\"%.7f\"", box.bottom_left().lon());
|
||||
append_printf_formatted_string(out, " minlat=\"%.7f\"", box.bottom_left().lat());
|
||||
append_printf_formatted_string(out, " maxlon=\"%.7f\"", box.top_right().lon());
|
||||
append_printf_formatted_string(out, " maxlat=\"%.7f\"/>\n", box.top_right().lat());
|
||||
}
|
||||
|
||||
send_to_output_queue(std::move(out));
|
||||
}
|
||||
|
||||
void write_buffer(osmium::memory::Buffer&& buffer) final {
|
||||
m_output_queue.push(osmium::thread::Pool::instance().submit(XMLOutputBlock{std::move(buffer), m_options}));
|
||||
}
|
||||
|
||||
void write_end() final {
|
||||
std::string out;
|
||||
|
||||
if (m_options.use_change_ops) {
|
||||
out += "</osmChange>\n";
|
||||
} else {
|
||||
out += "</osm>\n";
|
||||
}
|
||||
|
||||
send_to_output_queue(std::move(out));
|
||||
}
|
||||
|
||||
}; // class XMLOutputFormat
|
||||
|
||||
// we want the register_output_format() function to run, setting
|
||||
// the variable is only a side-effect, it will never be used
|
||||
const bool registered_xml_output = osmium::io::detail::OutputFormatFactory::instance().register_output_format(osmium::io::file_format::xml,
|
||||
[](const osmium::io::File& file, future_string_queue_type& output_queue) {
|
||||
return new osmium::io::detail::XMLOutputFormat(file, output_queue);
|
||||
});
|
||||
|
||||
// dummy function to silence the unused variable warning from above
|
||||
inline bool get_registered_xml_output() noexcept {
|
||||
return registered_xml_output;
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_XML_OUTPUT_FORMAT_HPP
|
||||
@@ -0,0 +1,115 @@
|
||||
#ifndef OSMIUM_IO_DETAIL_ZLIB_HPP
|
||||
#define OSMIUM_IO_DETAIL_ZLIB_HPP
|
||||
|
||||
/*
|
||||
|
||||
This file is part of Osmium (http://osmcode.org/libosmium).
|
||||
|
||||
Copyright 2013-2016 Jochen Topf <jochen@topf.org> and others (see README).
|
||||
|
||||
Boost Software License - Version 1.0 - August 17th, 2003
|
||||
|
||||
Permission is hereby granted, free of charge, to any person or organization
|
||||
obtaining a copy of the software and accompanying documentation covered by
|
||||
this license (the "Software") to use, reproduce, display, distribute,
|
||||
execute, and transmit the Software, and to prepare derivative works of the
|
||||
Software, and to permit third-parties to whom the Software is furnished to
|
||||
do so, all subject to the following:
|
||||
|
||||
The copyright notices in the Software and this entire statement, including
|
||||
the above license grant, this restriction and the following disclaimer,
|
||||
must be included in all copies of the Software, in whole or in part, and
|
||||
all derivative works of the Software, unless such copies or derivative
|
||||
works are solely in the form of machine-executable object code generated by
|
||||
a source language processor.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#include <osmium/io/error.hpp>
|
||||
#include <osmium/util/cast.hpp>
|
||||
|
||||
namespace osmium {
|
||||
|
||||
namespace io {
|
||||
|
||||
namespace detail {
|
||||
|
||||
/**
|
||||
* Compress data using zlib.
|
||||
*
|
||||
* Note that this function can not compress data larger than
|
||||
* what fits in an unsigned long, on Windows this is usually 32bit.
|
||||
*
|
||||
* @param input Data to compress.
|
||||
* @returns Compressed data.
|
||||
*/
|
||||
inline std::string zlib_compress(const std::string& input) {
|
||||
unsigned long output_size = ::compressBound(osmium::static_cast_with_assert<unsigned long>(input.size()));
|
||||
|
||||
std::string output(output_size, '\0');
|
||||
|
||||
auto result = ::compress(
|
||||
reinterpret_cast<unsigned char*>(const_cast<char *>(output.data())),
|
||||
&output_size,
|
||||
reinterpret_cast<const unsigned char*>(input.data()),
|
||||
osmium::static_cast_with_assert<unsigned long>(input.size())
|
||||
);
|
||||
|
||||
if (result != Z_OK) {
|
||||
throw io_error(std::string("failed to compress data: ") + zError(result));
|
||||
}
|
||||
|
||||
output.resize(output_size);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uncompress data using zlib.
|
||||
*
|
||||
* Note that this function can not uncompress data larger than
|
||||
* what fits in an unsigned long, on Windows this is usually 32bit.
|
||||
*
|
||||
* @param input Compressed input data.
|
||||
* @param raw_size Size of uncompressed data.
|
||||
* @param output Uncompressed result data.
|
||||
* @returns Pointer and size to incompressed data.
|
||||
*/
|
||||
inline std::pair<const char*, size_t> zlib_uncompress_string(const char* input, unsigned long input_size, unsigned long raw_size, std::string& output) {
|
||||
output.resize(raw_size);
|
||||
|
||||
auto result = ::uncompress(
|
||||
reinterpret_cast<unsigned char*>(&*output.begin()),
|
||||
&raw_size,
|
||||
reinterpret_cast<const unsigned char*>(input),
|
||||
input_size
|
||||
);
|
||||
|
||||
if (result != Z_OK) {
|
||||
throw io_error(std::string("failed to uncompress data: ") + zError(result));
|
||||
}
|
||||
|
||||
return std::make_pair(output.data(), output.size());
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace io
|
||||
|
||||
} // namespace osmium
|
||||
|
||||
#endif // OSMIUM_IO_DETAIL_ZLIB_HPP
|
||||
Reference in New Issue
Block a user