[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