This is an automated email from the git hooks/post-receive script. sebastic pushed a commit to branch master in repository libosmium.
commit e93e38c27c654e6a44c1dbb49ea1ca56930b0fad Author: Bas Couwenberg <sebas...@xs4all.nl> Date: Wed Jun 8 20:30:09 2016 +0200 Imported Upstream version 2.7.2 --- CHANGELOG.md | 18 ++- CMakeLists.txt | 2 +- include/osmium/geom/factory.hpp | 4 +- include/osmium/index/detail/mmap_vector_base.hpp | 9 +- include/osmium/io/detail/debug_output_format.hpp | 66 ++++++--- include/osmium/io/detail/opl_output_format.hpp | 171 +++++++++++++---------- include/osmium/io/detail/output_format.hpp | 25 +++- include/osmium/io/detail/pbf.hpp | 2 +- include/osmium/io/detail/string_util.hpp | 30 +++- include/osmium/io/detail/xml_input_format.hpp | 24 ++-- include/osmium/io/detail/xml_output_format.hpp | 89 +++++++----- include/osmium/osm/location.hpp | 169 ++++++++++++++++++++-- include/osmium/version.hpp | 4 +- test/t/basic/test_location.cpp | 108 ++++++++++++++ 14 files changed, 555 insertions(+), 166 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cd9a861..4b8a6a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,20 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Fixed +## [2.7.2] - 2016-06-08 + +### Changed + +- Much faster output of OSM files in XML, OPL, or debug formats. + +### Fixed + +- Parsing and output of coordinates now faster and always uses decimal dot + independant of locale setting. +- Do not output empty discussion elements in changeset XML output. +- Data corruption regression in mmap based indexes. + + ## [2.7.1] - 2016-06-01 ### Fixes @@ -327,7 +341,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). Doxygen (up to version 1.8.8). This version contains a workaround to fix this. -[unreleased]: https://github.com/osmcode/libosmium/compare/v2.7.0...HEAD +[unreleased]: https://github.com/osmcode/libosmium/compare/v2.7.2...HEAD +[2.7.2]: https://github.com/osmcode/libosmium/compare/v2.7.1...v2.7.2 +[2.7.1]: https://github.com/osmcode/libosmium/compare/v2.7.0...v2.7.1 [2.7.0]: https://github.com/osmcode/libosmium/compare/v2.6.1...v2.7.0 [2.6.1]: https://github.com/osmcode/libosmium/compare/v2.6.0...v2.6.1 [2.6.0]: https://github.com/osmcode/libosmium/compare/v2.5.4...v2.6.0 diff --git a/CMakeLists.txt b/CMakeLists.txt index 7434fb4..53251d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ project(libosmium) set(LIBOSMIUM_VERSION_MAJOR 2) set(LIBOSMIUM_VERSION_MINOR 7) -set(LIBOSMIUM_VERSION_PATCH 1) +set(LIBOSMIUM_VERSION_PATCH 2) set(LIBOSMIUM_VERSION "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}") diff --git a/include/osmium/geom/factory.hpp b/include/osmium/geom/factory.hpp index 6737278..03c1015 100644 --- a/include/osmium/geom/factory.hpp +++ b/include/osmium/geom/factory.hpp @@ -287,7 +287,7 @@ namespace osmium { return linestring_finish(num_points); } - linestring_type create_linestring(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir=direction::forward) { + linestring_type create_linestring(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir = direction::forward) { try { return create_linestring(way.nodes(), un, dir); } catch (osmium::geometry_error& e) { @@ -361,7 +361,7 @@ namespace osmium { return polygon_finish(num_points); } - polygon_type create_polygon(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir=direction::forward) { + polygon_type create_polygon(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir = direction::forward) { try { return create_polygon(way.nodes(), un, dir); } catch (osmium::geometry_error& e) { diff --git a/include/osmium/index/detail/mmap_vector_base.hpp b/include/osmium/index/detail/mmap_vector_base.hpp index f84cc2e..8f52e98 100644 --- a/include/osmium/index/detail/mmap_vector_base.hpp +++ b/include/osmium/index/detail/mmap_vector_base.hpp @@ -134,16 +134,13 @@ namespace osmium { } void push_back(const T& value) { - if (m_size >= capacity()) { - resize(m_size+1); - } - data()[m_size] = value; - ++m_size; + resize(m_size+1); + data()[m_size-1] = value; } void reserve(size_t new_capacity) { if (new_capacity > capacity()) { - size_t old_capacity = capacity(); + const size_t old_capacity = capacity(); m_mapping.resize(new_capacity); std::fill(data() + old_capacity, data() + new_capacity, osmium::index::empty_value<T>()); } diff --git a/include/osmium/io/detail/debug_output_format.hpp b/include/osmium/io/detail/debug_output_format.hpp index b45173c..691dc0c 100644 --- a/include/osmium/io/detail/debug_output_format.hpp +++ b/include/osmium/io/detail/debug_output_format.hpp @@ -112,6 +112,11 @@ namespace osmium { append_debug_encoded_string(*m_out, data, m_utf8_prefix, m_utf8_suffix); } + template <typename... TArgs> + void output_formatted(const char* format, TArgs&&... args) { + append_printf_formatted_string(*m_out, format, std::forward<TArgs>(args)...); + } + void write_color(const char* color) { if (m_options.use_color) { *m_out += color; @@ -167,7 +172,9 @@ namespace osmium { void write_timestamp(const osmium::Timestamp& timestamp) { if (timestamp.valid()) { *m_out += timestamp.to_iso(); - output_formatted(" (%d)", timestamp.seconds_since_epoch()); + *m_out += " ("; + output_int(timestamp.seconds_since_epoch()); + *m_out += ')'; } else { write_error("NOT SET"); } @@ -175,21 +182,26 @@ namespace osmium { } void write_meta(const osmium::OSMObject& object) { - output_formatted("%" PRId64 "\n", object.id()); + output_int(object.id()); + *m_out += '\n'; if (m_options.add_metadata) { write_fieldname("version"); - output_formatted(" %d", object.version()); + *m_out += " "; + output_int(object.version()); if (object.visible()) { *m_out += " visible\n"; } else { write_error(" deleted\n"); } write_fieldname("changeset"); - output_formatted("%d\n", object.changeset()); + output_int(object.changeset()); + *m_out += '\n'; write_fieldname("timestamp"); write_timestamp(object.timestamp()); write_fieldname("user"); - output_formatted(" %d ", object.uid()); + *m_out += " "; + output_int(object.uid()); + *m_out += ' '; write_string(object.user()); *m_out += '\n'; } @@ -199,7 +211,9 @@ namespace osmium { if (!tags.empty()) { write_fieldname("tags"); *m_out += padding; - output_formatted(" %d\n", tags.size()); + *m_out += " "; + output_int(tags.size()); + *m_out += '\n'; osmium::max_op<size_t> max; for (const auto& tag : tags) { @@ -221,7 +235,8 @@ namespace osmium { void write_location(const osmium::Location& location) { write_fieldname("lon/lat"); - output_formatted(" %.7f,%.7f", location.lon_without_check(), location.lat_without_check()); + *m_out += " "; + location.as_string(std::back_inserter(*m_out)); if (!location.valid()) { write_error(" INVALID LOCATION!"); } @@ -236,7 +251,9 @@ namespace osmium { } 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()); + bl.as_string(std::back_inserter(*m_out)); + *m_out += ' '; + tr.as_string(std::back_inserter(*m_out)); if (!box.valid()) { write_error(" INVALID BOX!"); } @@ -309,7 +326,8 @@ namespace osmium { write_fieldname("nodes"); - output_formatted(" %d", way.nodes().size()); + *m_out += " "; + output_int(way.nodes().size()); if (way.nodes().size() < 2) { write_error(" LESS THAN 2 NODES!\n"); } else if (way.nodes().size() > 2000) { @@ -326,7 +344,9 @@ namespace osmium { 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 += " ("; + node_ref.location().as_string(std::back_inserter(*m_out)); + *m_out += ')'; } *m_out += '\n'; } @@ -345,7 +365,9 @@ namespace osmium { write_tags(relation.tags()); write_fieldname("members"); - output_formatted(" %d\n", relation.members().size()); + *m_out += " "; + output_int(relation.members().size()); + *m_out += '\n'; int width = int(log10(relation.members().size())) + 1; int n = 0; @@ -366,10 +388,11 @@ namespace osmium { void changeset(const osmium::Changeset& changeset) { write_object_type("changeset"); - output_formatted("%d\n", changeset.id()); + output_int(changeset.id()); + *m_out += '\n'; write_fieldname("num changes"); - output_formatted("%d", changeset.num_changes()); + output_int(changeset.num_changes()); if (changeset.num_changes() == 0) { write_error(" NO CHANGES!"); } @@ -388,7 +411,9 @@ namespace osmium { } write_fieldname("user"); - output_formatted(" %d ", changeset.uid()); + *m_out += " "; + output_int(changeset.uid()); + *m_out += ' '; write_string(changeset.user()); *m_out += '\n'; @@ -397,7 +422,9 @@ namespace osmium { if (changeset.num_comments() > 0) { write_fieldname("comments"); - output_formatted(" %d\n", changeset.num_comments()); + *m_out += " "; + output_int(changeset.num_comments()); + *m_out += '\n'; int width = int(log10(changeset.num_comments())) + 1; int n = 0; @@ -409,7 +436,8 @@ namespace osmium { output_formatted(" %*s", width, ""); write_comment_field("user"); - output_formatted("%d ", comment.uid()); + output_int(comment.uid()); + *m_out += ' '; write_string(comment.user()); output_formatted("\n %*s", width, ""); @@ -477,9 +505,9 @@ namespace osmium { 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), ','); + 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"); diff --git a/include/osmium/io/detail/opl_output_format.hpp b/include/osmium/io/detail/opl_output_format.hpp index 427c49e..56c0182 100644 --- a/include/osmium/io/detail/opl_output_format.hpp +++ b/include/osmium/io/detail/opl_output_format.hpp @@ -90,38 +90,65 @@ namespace osmium { osmium::io::detail::append_utf8_encoded_string(*m_out, data); } + void write_field_int(char c, int64_t value) { + *m_out += c; + output_int(value); + } + + void write_field_timestamp(char c, const osmium::Timestamp& timestamp) { + *m_out += c; + *m_out += timestamp.to_iso(); + } + + void write_tags(const osmium::TagList& tags) { + *m_out += " T"; + + if (tags.empty()) { + return; + } + + auto it = tags.begin(); + append_encoded_string(it->key()); + *m_out += '='; + append_encoded_string(it->value()); + + for (++it; it != tags.end(); ++it) { + *m_out += ','; + append_encoded_string(it->key()); + *m_out += '='; + append_encoded_string(it->value()); + } + } + void write_meta(const osmium::OSMObject& object) { - output_formatted("%" PRId64, object.id()); + output_int(object.id()); if (m_options.add_metadata) { - output_formatted(" v%d d", object.version()); + *m_out += ' '; + write_field_int('v', object.version()); + *m_out += " d"; *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()); + *m_out += ' '; + write_field_int('c', object.changeset()); + *m_out += ' '; + write_field_timestamp('t', object.timestamp()); + *m_out += ' '; + write_field_int('i', object.uid()); + *m_out += " u"; 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()); - } + write_tags(object.tags()); } void write_location(const osmium::Location& location, const char x, const char y) { + *m_out += ' '; + *m_out += x; 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; + osmium::detail::append_location_coordinate_to_string(std::back_inserter(*m_out), location.x()); + } + *m_out += ' '; + *m_out += y; + if (location) { + osmium::detail::append_location_coordinate_to_string(std::back_inserter(*m_out), location.y()); } } @@ -157,84 +184,84 @@ namespace osmium { *m_out += '\n'; } + void write_field_ref(const osmium::NodeRef& node_ref) { + write_field_int('n', node_ref.ref()); + *m_out += 'x'; + if (node_ref.location()) { + node_ref.location().as_string(std::back_inserter(*m_out), 'y'); + } else { + *m_out += 'y'; + } + } + void way(const osmium::Way& way) { *m_out += 'w'; write_meta(way); *m_out += " N"; - bool first = true; - if (m_options.locations_on_ways) { - for (const auto& node_ref : way.nodes()) { - if (first) { - first = false; - } else { + + if (!way.nodes().empty()) { + auto it = way.nodes().begin(); + if (m_options.locations_on_ways) { + write_field_ref(*it); + for (++it; it != way.nodes().end(); ++it) { *m_out += ','; + write_field_ref(*it); } - output_formatted("n%" PRId64 "x", node_ref.ref()); - if (node_ref.location()) { - output_formatted("%.7fy%.7f", - node_ref.ref(), - node_ref.location().lon_without_check(), - node_ref.location().lat_without_check() - ); - } else { - *m_out += 'y'; - } - } - } else { - for (const auto& node_ref : way.nodes()) { - if (first) { - first = false; - } else { + } else { + write_field_int('n', it->ref()); + for (++it; it != way.nodes().end(); ++it) { *m_out += ','; + write_field_int('n', it->ref()); } - output_formatted("n%" PRId64, node_ref.ref()); } } + *m_out += '\n'; } + void relation_member(const osmium::RelationMember& member) { + *m_out += item_type_to_char(member.type()); + output_int(member.ref()); + *m_out += '@'; + append_encoded_string(member.role()); + } + 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 { + + if (!relation.members().empty()) { + auto it = relation.members().begin(); + relation_member(*it); + for (++it; it != relation.members().end(); ++it) { *m_out += ','; + relation_member(*it); } - *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()); + write_field_int('c', changeset.id()); + *m_out += ' '; + write_field_int('k', changeset.num_changes()); + *m_out += ' '; + write_field_timestamp('s', changeset.created_at()); + *m_out += ' '; + write_field_timestamp('e', changeset.closed_at()); + *m_out += ' '; + write_field_int('d', changeset.num_comments()); + *m_out += ' '; + write_field_int('i', changeset.uid()); + *m_out += " u"; 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()); - } - + write_tags(changeset.tags()); *m_out += '\n'; } diff --git a/include/osmium/io/detail/output_format.hpp b/include/osmium/io/detail/output_format.hpp index c741218..eaff001 100644 --- a/include/osmium/io/detail/output_format.hpp +++ b/include/osmium/io/detail/output_format.hpp @@ -70,9 +70,28 @@ namespace osmium { 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)...); + // Simple function to convert integer to string. This is much + // faster than using sprintf, but could be further optimized. + // See https://github.com/miloyip/itoa-benchmark . + void output_int(int64_t value) { + if (value < 0) { + *m_out += '-'; + value = -value; + } + + char temp[20]; + char *t = temp; + do { + *t++ = char(value % 10) + '0'; + value /= 10; + } while (value > 0); + + const auto old_size = m_out->size(); + m_out->resize(old_size + (t - temp)); + char* data = &(*m_out)[old_size]; + do { + *data++ += *--t; + } while (t != temp); } }; // class OutputBlock; diff --git a/include/osmium/io/detail/pbf.hpp b/include/osmium/io/detail/pbf.hpp index 88c4993..e23f8b9 100644 --- a/include/osmium/io/detail/pbf.hpp +++ b/include/osmium/io/detail/pbf.hpp @@ -78,7 +78,7 @@ namespace osmium { // 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; + const int64_t resolution_convert = lonlat_resolution / osmium::detail::coordinate_precision; } // namespace detail diff --git a/include/osmium/io/detail/string_util.hpp b/include/osmium/io/detail/string_util.hpp index f80088e..52408ff 100644 --- a/include/osmium/io/detail/string_util.hpp +++ b/include/osmium/io/detail/string_util.hpp @@ -124,7 +124,28 @@ namespace osmium { out.resize(old_size + size_t(len)); } + // Write out the value with exactly two hex digits. + inline void append_2_hex_digits(std::string& out, uint32_t value, const char* const hex_digits) { + out += hex_digits[(value >> 4) & 0xf]; + out += hex_digits[ value & 0xf]; + } + + // Write out the value with four or more hex digits. + inline void append_min_4_hex_digits(std::string& out, uint32_t value, const char* const hex_digits) { + auto + v = value & 0xf0000000; if (v) out += hex_digits[v >> 28]; + v = value & 0x0f000000; if (v) out += hex_digits[v >> 24]; + v = value & 0x00f00000; if (v) out += hex_digits[v >> 20]; + v = value & 0x000f0000; if (v) out += hex_digits[v >> 16]; + + out += hex_digits[(value >> 12) & 0xf]; + out += hex_digits[(value >> 8) & 0xf]; + out += hex_digits[(value >> 4) & 0xf]; + out += hex_digits[ value & 0xf]; + } + inline void append_utf8_encoded_string(std::string& out, const char* data) { + static const char* lookup_hex = "0123456789abcdef"; const char* end = data + std::strlen(data); while (data != end) { @@ -148,9 +169,9 @@ namespace osmium { } else { out += '%'; if (c <= 0xff) { - append_printf_formatted_string(out, "%02x", c); + append_2_hex_digits(out, c, lookup_hex); } else { - append_printf_formatted_string(out, "%04x", c); + append_min_4_hex_digits(out, c, lookup_hex); } out += '%'; } @@ -174,6 +195,7 @@ namespace osmium { } inline void append_debug_encoded_string(std::string& out, const char* data, const char* prefix, const char* suffix) { + static const char* lookup_hex = "0123456789ABCDEF"; const char* end = data + std::strlen(data); while (data != end) { @@ -194,7 +216,9 @@ namespace osmium { out.append(last, data); } else { out.append(prefix); - append_printf_formatted_string(out, "<U+%04X>", c); + out.append("<U+"); + append_min_4_hex_digits(out, c, lookup_hex); + out.append(">"); out.append(suffix); } } diff --git a/include/osmium/io/detail/xml_input_format.hpp b/include/osmium/io/detail/xml_input_format.hpp index 0233917..be1e2e7 100644 --- a/include/osmium/io/detail/xml_input_format.hpp +++ b/include/osmium/io/detail/xml_input_format.hpp @@ -253,9 +253,9 @@ namespace osmium { check_attributes(attrs, [&location, &user, &object](const XML_Char* name, const XML_Char* value) { if (!std::strcmp(name, "lon")) { - location.set_lon(std::atof(value)); // XXX doesn't detect garbage after the number + location.set_lon(value); } else if (!std::strcmp(name, "lat")) { - location.set_lat(std::atof(value)); // XXX doesn't detect garbage after the number + location.set_lat(value); } else if (!std::strcmp(name, "user")) { user = value; } else { @@ -278,13 +278,13 @@ namespace osmium { osmium::Location max; check_attributes(attrs, [&min, &max, &user, &new_changeset](const XML_Char* name, const XML_Char* value) { if (!std::strcmp(name, "min_lon")) { - min.set_lon(atof(value)); + min.set_lon(value); } else if (!std::strcmp(name, "min_lat")) { - min.set_lat(atof(value)); + min.set_lat(value); } else if (!std::strcmp(name, "max_lon")) { - max.set_lon(atof(value)); + max.set_lon(value); } else if (!std::strcmp(name, "max_lat")) { - max.set_lat(atof(value)); + max.set_lat(value); } else if (!std::strcmp(name, "user")) { user = value; } else { @@ -386,13 +386,13 @@ namespace osmium { osmium::Location max; check_attributes(attrs, [&min, &max](const XML_Char* name, const XML_Char* value) { if (!std::strcmp(name, "minlon")) { - min.set_lon(atof(value)); + min.set_lon(value); } else if (!std::strcmp(name, "minlat")) { - min.set_lat(atof(value)); + min.set_lat(value); } else if (!std::strcmp(name, "maxlon")) { - max.set_lon(atof(value)); + max.set_lon(value); } else if (!std::strcmp(name, "maxlat")) { - max.set_lat(atof(value)); + max.set_lat(value); } }); osmium::Box box; @@ -424,9 +424,9 @@ namespace osmium { if (!std::strcmp(name, "ref")) { nr.set_ref(osmium::string_to_object_id(value)); } else if (!std::strcmp(name, "lon")) { - nr.location().set_lon(std::atof(value)); // XXX doesn't detect garbage after the number + nr.location().set_lon(value); } else if (!std::strcmp(name, "lat")) { - nr.location().set_lat(std::atof(value)); // XXX doesn't detect garbage after the number + nr.location().set_lat(value); } }); m_wnl_builder->add_node_ref(nr); diff --git a/include/osmium/io/detail/xml_output_format.hpp b/include/osmium/io/detail/xml_output_format.hpp index ee58457..cc0e062 100644 --- a/include/osmium/io/detail/xml_output_format.hpp +++ b/include/osmium/io/detail/xml_output_format.hpp @@ -90,6 +90,22 @@ namespace osmium { bool locations_on_ways; }; + namespace detail { + + inline void append_lat_lon_attributes(std::string& out, const char* lat, const char* lon, const osmium::Location& location) { + out += ' '; + out += lat; + out += "=\""; + osmium::detail::append_location_coordinate_to_string(std::back_inserter(out), location.y()); + out += "\" "; + out += lon; + out += "=\""; + osmium::detail::append_location_coordinate_to_string(std::back_inserter(out), location.x()); + out += "\""; + } + + } // namespace detail + class XMLOutputBlock : public OutputBlock { // operation (create, modify, delete) for osc files @@ -118,12 +134,21 @@ namespace osmium { write_spaces(prefix_spaces()); } + template <typename T> + void write_attribute(const char* name, T value) { + *m_out += ' '; + *m_out += name; + *m_out += "=\""; + output_int(value); + *m_out += '"'; + } + void write_meta(const osmium::OSMObject& object) { - output_formatted(" id=\"%" PRId64 "\"", object.id()); + write_attribute("id", object.id()); if (m_options.add_metadata) { if (object.version()) { - output_formatted(" version=\"%d\"", object.version()); + write_attribute("version", object.version()); } if (object.timestamp()) { @@ -133,13 +158,14 @@ namespace osmium { } if (!object.user_is_anonymous()) { - output_formatted(" uid=\"%d\" user=\"", object.uid()); + write_attribute("uid", object.uid()); + *m_out += " user=\""; append_xml_encoded_string(*m_out, object.user()); *m_out += "\""; } if (object.changeset()) { - output_formatted(" changeset=\"%d\"", object.changeset()); + write_attribute("changeset", object.changeset()); } if (m_options.add_visible_flag) { @@ -164,8 +190,11 @@ namespace osmium { } void write_discussion(const osmium::ChangesetDiscussion& comments) { + *m_out += " <discussion>\n"; for (const auto& comment : comments) { - output_formatted(" <comment uid=\"%d\" user=\"", comment.uid()); + *m_out += " <comment"; + write_attribute("uid", comment.uid()); + *m_out += " user=\""; append_xml_encoded_string(*m_out, comment.user()); *m_out += "\" date=\""; *m_out += comment.date().to_iso(); @@ -253,11 +282,7 @@ namespace osmium { 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 += "\""; + detail::append_lat_lon_attributes(*m_out, "lat", "lon", node.location()); } if (node.tags().empty()) { @@ -292,20 +317,19 @@ namespace osmium { if (m_options.locations_on_ways) { for (const auto& node_ref : way.nodes()) { write_prefix(); - output_formatted(" <nd ref=\"%" PRId64 "\"", node_ref.ref()); + *m_out += " <nd"; + write_attribute("ref", node_ref.ref()); if (node_ref.location()) { - *m_out += " lat=\""; - osmium::util::double2string(std::back_inserter(*m_out), node_ref.location().lat_without_check(), 7); - *m_out += "\" lon=\""; - osmium::util::double2string(std::back_inserter(*m_out), node_ref.location().lon_without_check(), 7); - *m_out += "\""; + detail::append_lat_lon_attributes(*m_out, "lat", "lon", node_ref.location()); } *m_out += "/>\n"; } } else { for (const auto& node_ref : way.nodes()) { write_prefix(); - output_formatted(" <nd ref=\"%" PRId64 "\"/>\n", node_ref.ref()); + *m_out += " <nd"; + write_attribute("ref", node_ref.ref()); + *m_out += "/>\n"; } } @@ -335,7 +359,9 @@ namespace osmium { write_prefix(); *m_out += " <member type=\""; *m_out += item_type_to_name(member.type()); - output_formatted("\" ref=\"%" PRId64 "\" role=\"", member.ref()); + *m_out += '"'; + write_attribute("ref", member.ref()); + *m_out += " role=\""; append_xml_encoded_string(*m_out, member.role()); *m_out += "\"/>\n"; } @@ -349,7 +375,7 @@ namespace osmium { void changeset(const osmium::Changeset& changeset) { *m_out += " <changeset"; - output_formatted(" id=\"%" PRId32 "\"", changeset.id()); + write_attribute("id", changeset.id()); if (changeset.created_at()) { *m_out += " created_at=\""; @@ -368,22 +394,21 @@ namespace osmium { if (!changeset.user_is_anonymous()) { *m_out += " user=\""; append_xml_encoded_string(*m_out, changeset.user()); - output_formatted("\" uid=\"%d\"", changeset.uid()); + *m_out += '"'; + write_attribute("uid", 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()); + detail::append_lat_lon_attributes(*m_out, "min_lat", "min_lon", changeset.bounds().bottom_left()); + detail::append_lat_lon_attributes(*m_out, "max_lat", "max_lon", changeset.bounds().top_right()); } - output_formatted(" num_changes=\"%" PRId32 "\"", changeset.num_changes()); - output_formatted(" comments_count=\"%" PRId32 "\"", changeset.num_comments()); + write_attribute("num_changes", changeset.num_changes()); + write_attribute("comments_count", 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) { + if (changeset.tags().empty() && changeset.discussion().empty()) { *m_out += "/>\n"; return; } @@ -392,8 +417,7 @@ namespace osmium { write_tags(changeset.tags(), 0); - if (changeset.num_comments() > 0) { - *m_out += " <discussion>\n"; + if (!changeset.discussion().empty()) { write_discussion(changeset.discussion()); } @@ -443,10 +467,9 @@ namespace osmium { 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()); + detail::append_lat_lon_attributes(out, "minlat", "minlon", box.bottom_left()); + detail::append_lat_lon_attributes(out, "maxlat", "maxlon", box.top_right()); + out += "/>\n"; } send_to_output_queue(std::move(out)); diff --git a/include/osmium/osm/location.hpp b/include/osmium/osm/location.hpp index 2949a90..a9d8cb3 100644 --- a/include/osmium/osm/location.hpp +++ b/include/osmium/osm/location.hpp @@ -63,6 +63,142 @@ namespace osmium { }; // struct invalid_location + namespace detail { + + constexpr const int coordinate_precision = 10000000; + + // Convert string with a floating point number into integer suitable + // for use as coordinate in a Location. + inline int32_t string_to_location_coordinate(const char* str) { + const char* full = str; + + int32_t result = 0; + int sign = 1; + int scale = 7; + + // optional minus sign + if (*str == '-') { + sign = -1; + ++str; + } + + // first digit before decimal point + if (*str >= '0' && *str <= '9') { + result = *str - '0'; + ++str; + } else { + goto error; + } + + // optional second digit before decimal point + if (*str >= '0' && *str <= '9') { + result = result * 10 + *str - '0'; + ++str; + + // optional third digit before decimal point + if (*str >= '0' && *str <= '9') { + result = result * 10 + *str - '0'; + ++str; + } + } + + if (*str != '\0') { + + // decimal point + if (*str != '.') { + goto error; + } + + ++str; + + // read significant digits + for (; scale > 0 && *str >= '0' && *str <= '9'; --scale, ++str) { + result = result * 10 + (*str - '0'); + } + + // use 8th digit after decimal point for rounding + if (scale == 0 && *str >= '5' && *str <= '9') { + ++result; + ++str; + } + + // ignore further digits + while (*str >= '0' && *str <= '9') { + ++str; + } + + // should be at the end now + if (*str != '\0') { + goto error; + } + + } + + for (; scale > 0; --scale) { + result *= 10; + } + + return result * sign; + + error: + + throw invalid_location{std::string{"wrong format for coordinate: '"} + full + "'"}; + } + + // Convert integer as used by location for coordinates into a string. + template <typename T> + inline T append_location_coordinate_to_string(T iterator, int32_t value) { + // handle negative values + if (value < 0) { + *iterator++ = '-'; + value = -value; + } + + // write digits into temporary buffer + int32_t v = value; + char temp[10]; + char* t = temp; + do { + *t++ = char(v % 10) + '0'; + v /= 10; + } while (v != 0); + + while (t-temp < 7) { + *t++ = '0'; + } + + // write out digits before decimal point + if (value >= coordinate_precision) { + if (value >= 10 * coordinate_precision) { + if (value >= 100 * coordinate_precision) { + *iterator++ = *--t; + } + *iterator++ = *--t; + } + *iterator++ = *--t; + } else { + *iterator++ = '0'; + } + + // remove trailing zeros + const char* tn = temp; + while (tn < t && *tn == '0') { + ++tn; + } + + // decimal point + if (t != tn) { + *iterator++ = '.'; + while (t != tn) { + *iterator++ = *--t; + } + } + + return iterator; + } + + } // namespace detail + /** * Locations define a place on earth. * @@ -90,14 +226,12 @@ namespace osmium { // static constexpr int32_t undefined_coordinate = std::numeric_limits<int32_t>::max(); static constexpr int32_t undefined_coordinate = 2147483647; - static constexpr int coordinate_precision = 10000000; - static int32_t double_to_fix(const double c) noexcept { - return static_cast<int32_t>(std::round(c * coordinate_precision)); + return static_cast<int32_t>(std::round(c * detail::coordinate_precision)); } static constexpr double fix_to_double(const int32_t c) noexcept { - return static_cast<double>(c) / coordinate_precision; + return static_cast<double>(c) / detail::coordinate_precision; } /** @@ -155,10 +289,10 @@ namespace osmium { * usual bounds (-180<=lon<=180, -90<=lat<=90). */ constexpr bool valid() const noexcept { - return m_x >= -180 * coordinate_precision - && m_x <= 180 * coordinate_precision - && m_y >= -90 * coordinate_precision - && m_y <= 90 * coordinate_precision; + return m_x >= -180 * detail::coordinate_precision + && m_x <= 180 * detail::coordinate_precision + && m_y >= -90 * detail::coordinate_precision + && m_y <= 90 * detail::coordinate_precision; } constexpr int32_t x() const noexcept { @@ -227,11 +361,24 @@ namespace osmium { return *this; } + Location& set_lon(const char* str) noexcept { + m_x = detail::string_to_location_coordinate(str); + return *this; + } + + Location& set_lat(const char* str) noexcept { + m_y = detail::string_to_location_coordinate(str); + return *this; + } + template <typename T> - T as_string(T iterator, const char separator) const { - iterator = osmium::util::double2string(iterator, lon(), 7); + T as_string(T iterator, const char separator = ',') const { + if (!valid()) { + throw osmium::invalid_location("invalid location"); + } + iterator = detail::append_location_coordinate_to_string(iterator, x()); *iterator++ = separator; - return osmium::util::double2string(iterator, lat(), 7); + return detail::append_location_coordinate_to_string(iterator, y()); } }; // class Location diff --git a/include/osmium/version.hpp b/include/osmium/version.hpp index a0851f1..7ff931e 100644 --- a/include/osmium/version.hpp +++ b/include/osmium/version.hpp @@ -35,8 +35,8 @@ DEALINGS IN THE SOFTWARE. #define LIBOSMIUM_VERSION_MAJOR 2 #define LIBOSMIUM_VERSION_MINOR 7 -#define LIBOSMIUM_VERSION_PATCH 1 +#define LIBOSMIUM_VERSION_PATCH 2 -#define LIBOSMIUM_VERSION_STRING "2.7.1" +#define LIBOSMIUM_VERSION_STRING "2.7.2" #endif // OSMIUM_VERSION_HPP diff --git a/test/t/basic/test_location.cpp b/test/t/basic/test_location.cpp index c861722..2fe2bc7 100644 --- a/test/t/basic/test_location.cpp +++ b/test/t/basic/test_location.cpp @@ -166,3 +166,111 @@ TEST_CASE("Location hash") { } } +#define C(s, v) REQUIRE(osmium::detail::string_to_location_coordinate(s) == v); \ + REQUIRE(osmium::detail::string_to_location_coordinate("-" s) == -v); \ + REQUIRE(atof(s) == Approx( v / 10000000.0)); \ + REQUIRE(atof("-" s) == Approx(-v / 10000000.0)); + +#define F(s) REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate(s), osmium::invalid_location); \ + REQUIRE_THROWS_AS(osmium::detail::string_to_location_coordinate("-" s), osmium::invalid_location); + +TEST_CASE("Parsing coordinates from strings") { + F("x"); + F("."); + F("--"); + F(""); + F(" "); + F(" 123"); + F("123 "); + F("123x"); + F("1.2x"); + + C("0", 0); + + C("1", 10000000); + C("2", 20000000); + + C("9", 90000000); + C("10", 100000000); + C("11", 110000000); + + C("90", 900000000); + C("100", 1000000000); + C("101", 1010000000); + + C("00", 0); + C("01", 10000000); + C("001", 10000000); + + F("0001"); + F("1234"); + F("1234."); + + C("0.", 0); + C("0.0", 0); + C("1.", 10000000); + C("1.0", 10000000); + C("1.2", 12000000); + C("0.1", 1000000); + C("0.01", 100000); + C("0.001", 10000); + C("0.0001", 1000); + C("0.00001", 100); + C("0.000001", 10); + C("0.0000001", 1); + + C("1.1234567", 11234567); + C("1.12345670", 11234567); + C("1.12345674", 11234567); + C("1.12345675", 11234568); + C("1.12345679", 11234568); + C("1.12345680", 11234568); + C("1.12345681", 11234568); + + C("180.0000000", 1800000000); + C("180.0000001", 1800000001); + C("179.9999999", 1799999999); + C("179.99999999", 1800000000); + C("200.123", 2001230000); + +} + +#undef C +#undef F + +#define CW(v, s) buffer.clear(); \ + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), v); \ + CHECK(buffer == s); \ + buffer.clear(); \ + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), -v); \ + CHECK(buffer == "-" s); + +TEST_CASE("Writing coordinates into string") { + std::string buffer; + + osmium::detail::append_location_coordinate_to_string(std::back_inserter(buffer), 0); + CHECK(buffer == "0"); + + CW( 10000000, "1"); + CW( 90000000, "9"); + CW( 100000000, "10"); + CW(1000000000, "100"); + CW(2000000000, "200"); + + CW( 1000000, "0.1"); + CW( 100000, "0.01"); + CW( 10000, "0.001"); + CW( 1000, "0.0001"); + CW( 100, "0.00001"); + CW( 10, "0.000001"); + CW( 1, "0.0000001"); + + CW( 1230000, "0.123"); + CW( 9999999, "0.9999999"); + CW( 40101010, "4.010101"); + CW( 494561234, "49.4561234"); + CW(1799999999, "179.9999999"); +} + +#undef CW + -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/libosmium.git _______________________________________________ Pkg-grass-devel mailing list Pkg-grass-devel@lists.alioth.debian.org http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/pkg-grass-devel