[libosmium] 01/04: Imported Upstream version 2.7.2

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Wed Jun 8 18:58:08 UTC 2016


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 <sebastic at 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



More information about the Pkg-grass-devel mailing list