[libosmium] 01/05: Imported Upstream version 2.1.0

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Tue Mar 31 21:16:59 UTC 2015


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository libosmium.

commit 25f004d9a7a19697585c38b683a35b12651af114
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Tue Mar 31 22:41:11 2015 +0200

    Imported Upstream version 2.1.0
---
 .gitignore                                         |  2 -
 .ycm_extra_conf.py                                 | 10 ++-
 CHANGELOG.md                                       | 31 +++++++
 CMakeLists.txt                                     |  2 +-
 doc/Doxyfile.in                                    |  6 +-
 include/osmium/area/multipolygon_collector.hpp     |  2 +-
 include/osmium/builder/builder.hpp                 |  3 +-
 include/osmium/handler/node_locations_for_ways.hpp |  6 +-
 include/osmium/index/map.hpp                       |  3 +-
 include/osmium/index/multimap.hpp                  |  3 +-
 include/osmium/io/detail/pbf_input_format.hpp      | 51 ++++++++----
 include/osmium/io/detail/pbf_output_format.hpp     | 36 +++++++-
 include/osmium/io/detail/pbf_stringtable.hpp       | 42 ++++++----
 include/osmium/io/detail/xml_input_format.hpp      | 20 ++++-
 include/osmium/io/reader.hpp                       |  2 +-
 include/osmium/memory/item.hpp                     |  3 +-
 include/osmium/osm/area.hpp                        | 39 +++++++--
 include/osmium/osm/box.hpp                         | 77 ++++++++++++++----
 include/osmium/osm/changeset.hpp                   |  3 +-
 include/osmium/osm/node_ref_list.hpp               | 95 ++++++++++++++++------
 include/osmium/osm/way.hpp                         |  6 +-
 test/data-tests/testdata-xml.cpp                   | 95 +++++++++++++++++++++-
 test/t/basic/test_box.cpp                          | 16 +++-
 23 files changed, 434 insertions(+), 119 deletions(-)

diff --git a/.gitignore b/.gitignore
index 36118e1..5013903 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,2 @@
-core
 *.swp
-build*
 .ycm_extra_conf.pyc
diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py
index b0f9d3e..2b87306 100644
--- a/.ycm_extra_conf.py
+++ b/.ycm_extra_conf.py
@@ -6,6 +6,10 @@
 #
 #-----------------------------------------------------------------------------
 
+from os.path import realpath, dirname
+
+basedir = dirname(realpath(__file__))
+
 # some default flags
 # for more information install clang-3.2-doc package and
 # check UsersManual.html
@@ -26,9 +30,9 @@ flags = [
 'c++',
 
 # libosmium include dirs
-'-Iinclude',
-'-Itest/include',
-'-Itest/data-test/include',
+'-I%s/include' % basedir,
+'-I%s/test/include' % basedir,
+'-I%s/test/data-test/include' % basedir,
 
 # include third party libraries
 '-I/usr/include/gdal',
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..4c345a4
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,31 @@
+
+# Change Log
+
+All notable changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](http://semver.org/).
+
+## [unreleased] -
+
+## [2.1.0] - 2015-03-31
+
+### Added
+
+- When writing PBF files, sorting the PBF stringtables is now optional.
+- More tests and documentation.
+
+### Changed
+
+- Some functions are now declared `noexcept`.
+- XML parser fails now if the top-level element is not `osm` or `osmChange`.
+
+### Fixed
+
+- Race condition in PBF reader.
+- Multipolygon collector was accessing non-existent NodeRef.
+- Doxygen documentation wan't showing all classes/functions due to a bug in
+  Doxygen (up to version 1.8.8). This version contains a workaround to fix
+  this.
+
+[unreleased]: https://github.com/osmcode/libosmium/compare/v2.1.0...HEAD
+[2.1.0]: https://github.com/osmcode/libosmium/compare/v2.0.0...v2.1.0
+
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 00d48d4..a277765 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,7 +24,7 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev"
 project(libosmium)
 
 set(LIBOSMIUM_VERSION_MAJOR 2)
-set(LIBOSMIUM_VERSION_MINOR 0)
+set(LIBOSMIUM_VERSION_MINOR 1)
 set(LIBOSMIUM_VERSION_PATCH 0)
 
 set(LIBOSMIUM_VERSION
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index 6535259..6bc0185 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -694,7 +694,7 @@ CITE_BIB_FILES         =
 # messages are off.
 # The default value is: NO.
 
-QUIET                  = NO
+QUIET                  = YES
 
 # The WARNINGS tag can be used to turn on/off the warning messages that are
 # generated to standard error ( stderr) by doxygen. If WARNINGS is set to YES
@@ -860,7 +860,7 @@ IMAGE_PATH             =
 # code is scanned, but not when the output code is generated. If lines are added
 # or removed, the anchors will not be placed correctly.
 
-INPUT_FILTER           =
+INPUT_FILTER           = "grep -v static_assert"
 
 # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
 # basis. Doxygen will compare the file name with each pattern and apply the
@@ -876,7 +876,7 @@ FILTER_PATTERNS        =
 # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
 # The default value is: NO.
 
-FILTER_SOURCE_FILES    = NO
+FILTER_SOURCE_FILES    = YES
 
 # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
 # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
diff --git a/include/osmium/area/multipolygon_collector.hpp b/include/osmium/area/multipolygon_collector.hpp
index 0bd10f7..84a5262 100644
--- a/include/osmium/area/multipolygon_collector.hpp
+++ b/include/osmium/area/multipolygon_collector.hpp
@@ -142,7 +142,7 @@ namespace osmium {
              * Overwritten from the base class.
              */
             void way_not_in_any_relation(const osmium::Way& way) {
-                if (way.ends_have_same_location() && way.nodes().size() > 3) {
+                if (way.nodes().size() > 3 && way.ends_have_same_location()) {
                     // way is closed and has enough nodes, build simple multipolygon
                     try {
                         TAssembler assembler(m_assembler_config);
diff --git a/include/osmium/builder/builder.hpp b/include/osmium/builder/builder.hpp
index bf9cdcc..6606025 100644
--- a/include/osmium/builder/builder.hpp
+++ b/include/osmium/builder/builder.hpp
@@ -171,8 +171,7 @@ namespace osmium {
         template <class TItem>
         class ObjectBuilder : public Builder {
 
-            static_assert(std::is_base_of<osmium::memory::Item, TItem>::value,
-                "ObjectBuilder can only build objects derived from osmium::memory::Item");
+            static_assert(std::is_base_of<osmium::memory::Item, TItem>::value, "ObjectBuilder can only build objects derived from osmium::memory::Item");
 
         public:
 
diff --git a/include/osmium/handler/node_locations_for_ways.hpp b/include/osmium/handler/node_locations_for_ways.hpp
index d1224a0..9b9fcbf 100644
--- a/include/osmium/handler/node_locations_for_ways.hpp
+++ b/include/osmium/handler/node_locations_for_ways.hpp
@@ -63,11 +63,9 @@ namespace osmium {
         template <class TStoragePosIDs, class TStorageNegIDs = dummy_type>
         class NodeLocationsForWays : public osmium::handler::Handler {
 
-            static_assert(std::is_base_of<osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>, TStoragePosIDs>::value,
-                "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>");
+            static_assert(std::is_base_of<osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>, TStoragePosIDs>::value, "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>");
 
-            static_assert(std::is_base_of<osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>, TStorageNegIDs>::value,
-                "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>");
+            static_assert(std::is_base_of<osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>, TStorageNegIDs>::value, "Index class must be derived from osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location>");
 
         public:
 
diff --git a/include/osmium/index/map.hpp b/include/osmium/index/map.hpp
index a9fe0c3..7b44b8e 100644
--- a/include/osmium/index/map.hpp
+++ b/include/osmium/index/map.hpp
@@ -84,8 +84,7 @@ namespace osmium {
             template <typename TId, typename TValue>
             class Map {
 
-                static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value,
-                              "TId template parameter for class Map must be unsigned integral type");
+                static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value, "TId template parameter for class Map must be unsigned integral type");
 
                 Map(const Map&) = delete;
                 Map& operator=(const Map&) = delete;
diff --git a/include/osmium/index/multimap.hpp b/include/osmium/index/multimap.hpp
index e5c6b4f..c817b6f 100644
--- a/include/osmium/index/multimap.hpp
+++ b/include/osmium/index/multimap.hpp
@@ -50,8 +50,7 @@ namespace osmium {
             template <typename TId, typename TValue>
             class Multimap {
 
-                static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value,
-                              "TId template parameter for class Multimap must be unsigned integral type");
+                static_assert(std::is_integral<TId>::value && std::is_unsigned<TId>::value, "TId template parameter for class Multimap must be unsigned integral type");
 
                 typedef typename std::pair<TId, TValue> element_type;
 
diff --git a/include/osmium/io/detail/pbf_input_format.hpp b/include/osmium/io/detail/pbf_input_format.hpp
index 8b997ec..ac69cbd 100644
--- a/include/osmium/io/detail/pbf_input_format.hpp
+++ b/include/osmium/io/detail/pbf_input_format.hpp
@@ -84,8 +84,9 @@ namespace osmium {
             class PBFInputFormat : public osmium::io::detail::InputFormat {
 
                 bool m_use_thread_pool;
+                bool m_eof { false };
                 queue_type m_queue;
-                std::atomic<bool> m_done;
+                std::atomic<bool> m_quit_input_thread;
                 std::thread m_reader;
                 osmium::thread::Queue<std::string>& m_input_queue;
                 std::string m_input_buffer;
@@ -164,11 +165,20 @@ namespace osmium {
                         }
                         ++n;
 
-                        if (m_done) {
+                        if (m_quit_input_thread) {
                             return;
                         }
                     }
-                    m_done = true;
+
+                    // Send an empty buffer to signal the reader that we are
+                    // done.
+                    std::promise<osmium::memory::Buffer> promise;
+                    m_queue.push(promise.get_future());
+                    promise.set_value(osmium::memory::Buffer{});
+                }
+
+                void signal_input_thread_to_quit() {
+                    m_quit_input_thread = true;
                 }
 
             public:
@@ -184,7 +194,7 @@ namespace osmium {
                     osmium::io::detail::InputFormat(file, read_which_entities),
                     m_use_thread_pool(osmium::config::use_pool_threads_for_pbf_parsing()),
                     m_queue(20, "pbf_parser_results"), // XXX
-                    m_done(false),
+                    m_quit_input_thread(false),
                     m_input_queue(input_queue),
                     m_input_buffer() {
                     GOOGLE_PROTOBUF_VERIFY_VERSION;
@@ -199,30 +209,37 @@ namespace osmium {
                 }
 
                 ~PBFInputFormat() {
-                    m_done = true;
+                    signal_input_thread_to_quit();
                     if (m_reader.joinable()) {
                         m_reader.join();
                     }
                 }
 
                 /**
-                 * Returns the next buffer with OSM data read from the PBF file.
-                 * Blocks if data is not available yet.
+                 * Returns the next buffer with OSM data read from the PBF
+                 * file. Blocks if data is not available yet.
                  * Returns an empty buffer at end of input.
                  */
                 osmium::memory::Buffer read() override {
-                    if (!m_done || !m_queue.empty()) {
-                        std::future<osmium::memory::Buffer> buffer_future;
-                        m_queue.wait_and_pop(buffer_future);
-                        try {
-                            return buffer_future.get();
-                        } catch (...) {
-                            m_done = true;
-                            throw;
-                        }
+                    osmium::memory::Buffer buffer;
+                    if (m_eof) {
+                        return buffer;
                     }
 
-                    return osmium::memory::Buffer();
+                    std::future<osmium::memory::Buffer> buffer_future;
+                    m_queue.wait_and_pop(buffer_future);
+
+                    try {
+                        buffer = std::move(buffer_future.get());
+                        if (!buffer) {
+                            m_eof = true;
+                        }
+                        return buffer;
+                    } catch (...) {
+                        m_eof = true;
+                        signal_input_thread_to_quit();
+                        throw;
+                    }
                 }
 
             }; // class PBFInputFormat
diff --git a/include/osmium/io/detail/pbf_output_format.hpp b/include/osmium/io/detail/pbf_output_format.hpp
index 73bc616..acce67d 100644
--- a/include/osmium/io/detail/pbf_output_format.hpp
+++ b/include/osmium/io/detail/pbf_output_format.hpp
@@ -295,6 +295,14 @@ namespace osmium {
                 bool m_use_compression {true};
 
                 /**
+                 * Should the string tables in the data blocks be sorted?
+                 *
+                 * Not sorting the string tables makes writing PBF files
+                 * slightly faster.
+                 */
+                bool m_sort_stringtables { true };
+
+                /**
                  * While the .osm.pbf-format is able to carry all meta information, it is
                  * also able to omit this information to reduce size.
                  */
@@ -340,6 +348,21 @@ namespace osmium {
 
                 ///// Blob writing /////
 
+                void delta_encode_string_ids() {
+                    if (pbf_nodes && pbf_nodes->has_dense()) {
+                        OSMPBF::DenseNodes* dense = pbf_nodes->mutable_dense();
+
+                        if (dense->has_denseinfo()) {
+                            OSMPBF::DenseInfo* denseinfo = dense->mutable_denseinfo();
+
+                            for (int i=0, l=denseinfo->user_sid_size(); i<l; ++i) {
+                                auto user_sid = denseinfo->user_sid(i);
+                                denseinfo->set_user_sid(i, m_delta_user_sid.update(user_sid));
+                            }
+                        }
+                    }
+                }
+
                 /**
                  * Before a PrimitiveBlock gets serialized, all interim StringTable-ids needs to be
                  * mapped to the associated real StringTable ids. This is done in this function.
@@ -518,11 +541,13 @@ namespace osmium {
                     pbf_primitive_block.set_granularity(location_granularity());
                     pbf_primitive_block.set_date_granularity(date_granularity());
 
-                    // store the interim StringTable into the protobuf object
-                    string_table.store_stringtable(pbf_primitive_block.mutable_stringtable());
+                    string_table.store_stringtable(pbf_primitive_block.mutable_stringtable(), m_sort_stringtables);
 
-                    // map all interim string ids to real ids
-                    map_string_ids();
+                    if (m_sort_stringtables) {
+                        map_string_ids();
+                    } else {
+                        delta_encode_string_ids();
+                    }
 
                     std::promise<std::string> promise;
                     m_output_queue.push(promise.get_future());
@@ -743,6 +768,9 @@ namespace osmium {
                     if (file.get("pbf_compression") == "none" || file.get("pbf_compression") == "false") {
                         m_use_compression = false;
                     }
+                    if (file.get("pbf_sort_stringtables") == "false") {
+                        m_sort_stringtables = false;
+                    }
                     if (file.get("pbf_add_metadata") == "false") {
                         m_should_add_metadata = false;
                     }
diff --git a/include/osmium/io/detail/pbf_stringtable.hpp b/include/osmium/io/detail/pbf_stringtable.hpp
index c56d549..e3d6fe3 100644
--- a/include/osmium/io/detail/pbf_stringtable.hpp
+++ b/include/osmium/io/detail/pbf_stringtable.hpp
@@ -145,30 +145,44 @@ namespace osmium {
                  * implementation) is that the string table is sorted first by reverse count (ie descending)
                  * and then by reverse lexicographic order.
                  */
-                void store_stringtable(OSMPBF::StringTable* st) {
+                void store_stringtable(OSMPBF::StringTable* st, bool sort) {
                     // add empty StringTable entry at index 0
                     // StringTable index 0 is reserved as delimiter in the densenodes key/value list
                     // this line also ensures that there's always a valid StringTable
                     st->add_s("");
 
-                    std::multimap<string_info, std::string> sortedbycount;
+                    if (sort) {
+                        std::multimap<string_info, std::string> sortedbycount;
 
-                    m_id2id_map.resize(m_size+1);
+                        m_id2id_map.resize(m_size+1);
 
-                    std::transform(m_strings.begin(), m_strings.end(),
-                                std::inserter(sortedbycount, sortedbycount.begin()),
-                                [](const std::pair<std::string, string_info>& p) {
-                                        return std::pair<string_info, std::string>(p.second, p.first);
-                                });
+                        std::transform(m_strings.begin(), m_strings.end(),
+                                    std::inserter(sortedbycount, sortedbycount.begin()),
+                                    [](const std::pair<std::string, string_info>& p) {
+                                            return std::pair<string_info, std::string>(p.second, p.first);
+                                    });
 
-                    string_id_type n=0;
+                        string_id_type n=0;
 
-                    for (const auto& mapping : sortedbycount) {
-                        // add the string of the current item to the pbf StringTable
-                        st->add_s(mapping.second);
+                        for (const auto& mapping : sortedbycount) {
+                            // add the string of the current item to the pbf StringTable
+                            st->add_s(mapping.second);
 
-                        // store the mapping from the interim-id to the real id
-                        m_id2id_map[mapping.first.interim_id] = ++n;
+                            // store the mapping from the interim-id to the real id
+                            m_id2id_map[mapping.first.interim_id] = ++n;
+                        }
+                    } else {
+                        std::vector<std::pair<string_id_type, const char*>> sortedbyid;
+                        sortedbyid.reserve(m_strings.size());
+
+                        for (const auto& p : m_strings) {
+                            sortedbyid.emplace_back(p.second.interim_id, p.first.c_str());
+                        }
+
+                        std::sort(sortedbyid.begin(), sortedbyid.end());
+                        for (const auto& mapping : sortedbyid) {
+                            st->add_s(mapping.second);
+                        }
                     }
                 }
 
diff --git a/include/osmium/io/detail/xml_input_format.hpp b/include/osmium/io/detail/xml_input_format.hpp
index 96003d8..c03f84d 100644
--- a/include/osmium/io/detail/xml_input_format.hpp
+++ b/include/osmium/io/detail/xml_input_format.hpp
@@ -74,8 +74,8 @@ namespace osmium {
 
     /**
      * Exception thrown when the XML parser failed. The exception contains
-     * information about the place where the error happened and the type of
-     * error.
+     * (if available) information about the place where the error happened
+     * and the type of error.
      */
     struct xml_error : public io_error {
 
@@ -84,7 +84,7 @@ namespace osmium {
         XML_Error error_code;
         std::string error_string;
 
-        xml_error(XML_Parser parser) :
+        explicit xml_error(XML_Parser parser) :
             io_error(std::string("XML parsing error at line ")
                     + std::to_string(XML_GetCurrentLineNumber(parser))
                     + ", column "
@@ -97,8 +97,20 @@ namespace osmium {
             error_string(XML_ErrorString(error_code)) {
         }
 
+        explicit xml_error(const std::string& message) :
+            io_error(message),
+            line(0),
+            column(0),
+            error_code(),
+            error_string(message) {
+        }
+
     }; // struct xml_error
 
+    /**
+     * Exception thrown when an OSM XML files contains no version attribute
+     * on the 'osm' element or if the version is unknown.
+     */
     struct format_version_error : public io_error {
 
         std::string version;
@@ -434,6 +446,8 @@ namespace osmium {
                                 if (m_header.get("version") == "") {
                                     throw osmium::format_version_error();
                                 }
+                            } else {
+                                throw osmium::xml_error(std::string("Unknown top-level element: ") + element);
                             }
                             m_context = context::top;
                             break;
diff --git a/include/osmium/io/reader.hpp b/include/osmium/io/reader.hpp
index 0bb4f1c..8d24652 100644
--- a/include/osmium/io/reader.hpp
+++ b/include/osmium/io/reader.hpp
@@ -260,7 +260,7 @@ namespace osmium {
 
                 // m_input->read() can return an invalid buffer to signal EOF,
                 // or a valid buffer with or without data. A valid buffer
-                // without data is not an error, it just means we have to get
+                // without data is not an error, it just means we have to
                 // keep getting the next buffer until there is one with data.
                 while (true) {
                     osmium::memory::Buffer buffer = m_input->read();
diff --git a/include/osmium/memory/item.hpp b/include/osmium/memory/item.hpp
index df15d01..58d63df 100644
--- a/include/osmium/memory/item.hpp
+++ b/include/osmium/memory/item.hpp
@@ -55,8 +55,7 @@ namespace osmium {
 
         template <typename T>
         inline T padded_length(T length) noexcept {
-            static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value,
-                          "Template parameter must be unsigned integral type");
+            static_assert(std::is_integral<T>::value && std::is_unsigned<T>::value, "Template parameter must be unsigned integral type");
             return (length + align_bytes - 1) & ~(align_bytes - 1);
         }
 
diff --git a/include/osmium/osm/area.hpp b/include/osmium/osm/area.hpp
index afd127e..3e129d0 100644
--- a/include/osmium/osm/area.hpp
+++ b/include/osmium/osm/area.hpp
@@ -53,12 +53,14 @@ namespace osmium {
     /**
      * An outer ring of an Area.
      */
-    class OuterRing : public NodeRefList<osmium::item_type::outer_ring> {
+    class OuterRing : public NodeRefList {
 
     public:
 
+        static constexpr osmium::item_type itemtype = osmium::item_type::outer_ring;
+
         OuterRing():
-            NodeRefList<osmium::item_type::outer_ring>() {
+            NodeRefList(itemtype) {
         }
 
     }; // class OuterRing
@@ -68,12 +70,14 @@ namespace osmium {
     /**
      * An inner ring of an Area.
      */
-    class InnerRing : public NodeRefList<osmium::item_type::inner_ring> {
+    class InnerRing : public NodeRefList {
 
     public:
 
+        static constexpr osmium::item_type itemtype = osmium::item_type::inner_ring;
+
         InnerRing():
-            NodeRefList<osmium::item_type::inner_ring>() {
+            NodeRefList(itemtype) {
         }
 
     }; // class InnerRing
@@ -87,7 +91,7 @@ namespace osmium {
      * @param type Type of object (way or relation)
      * @returns Area id
      */
-    inline osmium::object_id_type object_id_to_area_id(osmium::object_id_type id, osmium::item_type type) {
+    inline osmium::object_id_type object_id_to_area_id(osmium::object_id_type id, osmium::item_type type) noexcept {
         osmium::object_id_type area_id = std::abs(id) * 2;
         if (type == osmium::item_type::relation) {
             ++area_id;
@@ -101,7 +105,7 @@ namespace osmium {
      * @param id Area id
      * @returns Way or Relation id.
      */
-    inline osmium::object_id_type area_id_to_object_id(osmium::object_id_type id) {
+    inline osmium::object_id_type area_id_to_object_id(osmium::object_id_type id) noexcept {
         return id / 2;
     }
 
@@ -131,15 +135,17 @@ namespace osmium {
         /**
          * Return the Id of the way or relation this area was created from.
          */
-        osmium::object_id_type orig_id() const {
+        osmium::object_id_type orig_id() const noexcept {
             return osmium::area_id_to_object_id(id());
         }
 
         /**
          * Count the number of outer and inner rings of this area.
+         *
+         * @returns Pair (number outer rings, number inner rings)
          */
         std::pair<int, int> num_rings() const {
-            std::pair<int, int> counter;
+            std::pair<int, int> counter { 0, 0 };
 
             for (auto it = cbegin(); it != cend(); ++it) {
                 switch (it->type()) {
@@ -170,16 +176,31 @@ namespace osmium {
         }
 
         /**
-         * Is this area a multipolygon, ie. has it more than one outer ring?
+         * Check whether this area is a multipolygon, ie. whether it has more
+         * than one outer ring?
          */
         bool is_multipolygon() const {
             return num_rings().first > 1;
         }
 
+        /**
+         * Get iterator for iterating over all inner rings in a specified outer
+         * ring.
+         *
+         * @param it Iterator specifying outer ring.
+         * @returns Iterator to first inner ring in specified outer ring.
+         */
         osmium::memory::ItemIterator<const osmium::InnerRing> inner_ring_cbegin(const osmium::memory::ItemIterator<const osmium::OuterRing>& it) const {
             return it.cast<const osmium::InnerRing>();
         }
 
+        /**
+         * Get iterator for iterating over all inner rings in a specified outer
+         * ring.
+         *
+         * @param it Iterator specifying outer ring.
+         * @returns Iterator one past last inner ring in specified outer ring.
+         */
         osmium::memory::ItemIterator<const osmium::InnerRing> inner_ring_cend(const osmium::memory::ItemIterator<const osmium::OuterRing>& it) const {
             return std::next(it).cast<const osmium::InnerRing>();
         }
diff --git a/include/osmium/osm/box.hpp b/include/osmium/osm/box.hpp
index 37baf7e..631f919 100644
--- a/include/osmium/osm/box.hpp
+++ b/include/osmium/osm/box.hpp
@@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
+#include <cassert>
 #include <iosfwd>
 
 #include <osmium/util/compatibility.hpp>
@@ -41,7 +42,10 @@ DEALINGS IN THE SOFTWARE.
 namespace osmium {
 
     /**
-     * Bounding box.
+     * Bounding box. A box is defined by two locations (bottom left location
+     * and top right location) or, alternatively by four coordinates (minx,
+     * miny, maxx, and maxy). If both locations are undefined, the box is
+     * undefined, too.
      */
     class Box {
 
@@ -59,14 +63,33 @@ namespace osmium {
             m_top_right() {
         }
 
+        /**
+         * Create box from minimum and maximum coordinates.
+         *
+         * @pre @code minx <= maxx && miny <= maxy @endcode
+         */
         Box(double minx, double miny, double maxx, double maxy) :
             m_bottom_left(minx, miny),
             m_top_right(maxx, maxy) {
+            assert(minx <= maxx && miny <= maxy);
         }
 
+        /**
+         * Create box from bottom left and top right locations.
+         *
+         * @pre Either both locations must be defined or neither.
+         * @pre If both locations are defined, the
+         *      bottom left location must actually be to the left and below
+         *      the top right location. Same coordinates for bottom/top or
+         *      left/right are also okay.
+         */
         Box(const osmium::Location& bottom_left, const osmium::Location& top_right) :
             m_bottom_left(bottom_left),
             m_top_right(top_right) {
+            assert(
+                (!!bottom_left && !!top_right) ||
+                (bottom_left.x() <= top_right.x() && bottom_left.y() <= top_right.y())
+            );
         }
 
         Box(const Box&) = default;
@@ -76,8 +99,13 @@ namespace osmium {
         ~Box() = default;
 
         /**
-         * Extend this bounding box by the given location. If the
-         * location is undefined, the bounding box is unchanged.
+         * Extend this bounding box by the specified location. If the
+         * location is undefined, the bounding box is unchanged. If
+         * the box is undefined it will only contain the location after
+         * this call.
+         *
+         * @param location The location we want to extend the box by.
+         * @returns A reference to this box.
          */
         Box& extend(const Location& location) noexcept {
             if (location) {
@@ -103,8 +131,11 @@ namespace osmium {
         }
 
         /**
-         * Extend this bounding box by the given box. If the
-         * box is undefined, the bounding box is unchanged.
+         * Extend this bounding box by the specified box. If the
+         * specified box is undefined, the bounding box is unchanged.
+         *
+         * @param box The box to extend by.
+         * @returns A reference to this box.
          */
         Box& extend(const Box& box) noexcept {
             extend(box.bottom_left());
@@ -113,14 +144,14 @@ namespace osmium {
         }
 
         /**
-         * Box are defined, ie. contains defined coordinates.
+         * Box is defined, ie. contains defined locations.
          */
         explicit constexpr operator bool() const noexcept {
             return static_cast<bool>(m_bottom_left);
         }
 
         /**
-         * Box are valid, ie. defined and inside usual bounds
+         * Box is valid, ie. defined and inside usual bounds
          * (-180<=lon<=180, -90<=lat<=90).
          */
         OSMIUM_CONSTEXPR bool valid() const noexcept {
@@ -128,37 +159,43 @@ namespace osmium {
         }
 
         /**
-         * Bottom-left location.
+         * Access bottom-left location.
          */
         OSMIUM_CONSTEXPR Location bottom_left() const noexcept {
             return m_bottom_left;
         }
 
         /**
-         * Bottom-left location.
+         * Access bottom-left location.
          */
         Location& bottom_left() noexcept {
             return m_bottom_left;
         }
 
         /**
-         * Top-right location.
+         * Access top-right location.
          */
         OSMIUM_CONSTEXPR Location top_right() const noexcept {
             return m_top_right;
         }
 
         /**
-         * Top-right location.
+         * Access top-right location.
          */
         Location& top_right() noexcept {
             return m_top_right;
         }
 
         /**
-         * Is the location inside the box?
+         * Check whether the location is inside the box.
+         *
+         * @pre Location must be defined.
+         * @pre Box must be defined.
          */
-        bool contains(const osmium::Location& location) const {
+        bool contains(const osmium::Location& location) const noexcept {
+            assert(bottom_left());
+            assert(top_right());
+            assert(location);
             return location.x() >= bottom_left().x() && location.y() >= bottom_left().y() &&
                    location.x() <= top_right().x() && location.y() <= top_right().y();
         }
@@ -166,7 +203,7 @@ namespace osmium {
         /**
          * Calculate size of the box in square degrees.
          *
-         * @throws osmium::invalid_location unless all coordinates are valid
+         * @throws osmium::invalid_location unless all coordinates are valid.
          */
         double size() const {
             return (m_top_right.lon() - m_bottom_left.lon()) *
@@ -176,14 +213,19 @@ namespace osmium {
     }; // class Box
 
     /**
-     * Boxes are equal if both locations are equal.
+     * Boxes are equal if both locations are equal. Undefined boxes will
+     * compare equal.
      */
     inline OSMIUM_CONSTEXPR bool operator==(const Box& lhs, const Box& rhs) noexcept {
-        return lhs.bottom_left() == rhs.bottom_left() && lhs.top_right() == rhs.top_right();
+        return lhs.bottom_left() == rhs.bottom_left() &&
+               lhs.top_right() == rhs.top_right();
     }
 
     /**
-     * Output a box to a stream.
+     * Output a box to a stream. The format is "(LON, LAT, LON, LAT)" or
+     * "(undefined)" if the box is undefined.
+     *
+     * @returns Reference to basic_ostream given as first parameter.
      */
     template <typename TChar, typename TTraits>
     inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Box& box) {
@@ -202,6 +244,7 @@ namespace osmium {
         }
         return out;
     }
+
 } // namespace osmium
 
 #endif // OSMIUM_OSM_BOX_HPP
diff --git a/include/osmium/osm/changeset.hpp b/include/osmium/osm/changeset.hpp
index cc1c52d..0ab4e9b 100644
--- a/include/osmium/osm/changeset.hpp
+++ b/include/osmium/osm/changeset.hpp
@@ -298,8 +298,7 @@ namespace osmium {
 
     }; // class Changeset
 
-    static_assert(sizeof(Changeset) % osmium::memory::align_bytes == 0,
-        "Class osmium::Changeset has wrong size to be aligned properly!");
+    static_assert(sizeof(Changeset) % osmium::memory::align_bytes == 0, "Class osmium::Changeset has wrong size to be aligned properly!");
 
     /**
      * Changesets are equal if their IDs are equal.
diff --git a/include/osmium/osm/node_ref_list.hpp b/include/osmium/osm/node_ref_list.hpp
index f45bf6b..f0dfedb 100644
--- a/include/osmium/osm/node_ref_list.hpp
+++ b/include/osmium/osm/node_ref_list.hpp
@@ -44,51 +44,92 @@ DEALINGS IN THE SOFTWARE.
 namespace osmium {
 
     /**
-     * A vector of NodeRef objects. Usually this is not instatiated directly,
+     * A vector of NodeRef objects. Usually this is not instantiated directly,
      * but one of its subclasses are used.
      */
-    template <osmium::item_type TItemType>
     class NodeRefList : public osmium::memory::Item {
 
     public:
 
-        static constexpr osmium::item_type itemtype = TItemType;
-
-        NodeRefList() noexcept :
-            osmium::memory::Item(sizeof(NodeRefList), TItemType) {
+        NodeRefList(osmium::item_type itemtype) noexcept :
+            osmium::memory::Item(sizeof(NodeRefList), itemtype) {
         }
 
+        /**
+         * Checks whether the node list is empty.
+         */
         bool empty() const noexcept {
             return sizeof(NodeRefList) == byte_size();
         }
 
+        /**
+         * Returns the number of nodes in the list.
+         */
         size_t size() const noexcept {
-            assert((osmium::memory::Item::byte_size() - sizeof(NodeRefList)) % sizeof(NodeRef) == 0);
-            return (osmium::memory::Item::byte_size() - sizeof(NodeRefList)) / sizeof(NodeRef);
-        }
-
-        const NodeRef& operator[](size_t n) const {
+            auto size_node_refs = osmium::memory::Item::byte_size() - sizeof(NodeRefList);
+            assert(size_node_refs % sizeof(NodeRef) == 0);
+            return size_node_refs / sizeof(NodeRef);
+        }
+
+        /**
+         * Access specified element.
+         *
+         * @param n Get this element of the list.
+         * @pre @code n < size() @endcode
+         */
+        const NodeRef& operator[](size_t n) const noexcept {
+            assert(n < size());
             const NodeRef* node_ref = &*(cbegin());
             return node_ref[n];
         }
 
-        const NodeRef& front() const {
+        /**
+         * Access the first element.
+         *
+         * @pre @code !empty() @endcode
+         */
+        const NodeRef& front() const noexcept {
+            assert(!empty());
             return operator[](0);
         }
 
-        const NodeRef& back() const {
+        /**
+         * Access the last element.
+         *
+         * @pre @code !empty() @endcode
+         */
+        const NodeRef& back() const noexcept {
+            assert(!empty());
             return operator[](size()-1);
         }
 
-        bool is_closed() const {
+        /**
+         * Checks whether the first and last node in the list have the same ID.
+         *
+         * @pre @code !empty() @endcode
+         */
+        bool is_closed() const noexcept {
             return front().ref() == back().ref();
         }
 
-        bool ends_have_same_id() const {
+        /**
+         * Checks whether the first and last node in the list have the same ID.
+         *
+         * @pre @code !empty() @endcode
+         */
+        bool ends_have_same_id() const noexcept {
             return front().ref() == back().ref();
         }
 
+        /**
+         * Checks whether the first and last node in the list have the same
+         * location. The ID is not checked.
+         *
+         * @pre @code !empty() @endcode
+         * @pre @code front().location() && back().location() @endcode
+         */
         bool ends_have_same_location() const {
+            assert(front().location() && back().location());
             return front().location() == back().location();
         }
 
@@ -96,35 +137,43 @@ namespace osmium {
         typedef const NodeRef* const_iterator;
         typedef std::reverse_iterator<const NodeRef*> const_reverse_iterator;
 
-        iterator begin() {
+        /// Returns an iterator to the beginning.
+        iterator begin() noexcept {
             return iterator(data() + sizeof(NodeRefList));
         }
 
-        iterator end() {
+        /// Returns an iterator to the end.
+        iterator end() noexcept {
             return iterator(data() + byte_size());
         }
 
-        const_iterator cbegin() const {
+        /// Returns an iterator to the beginning.
+        const_iterator cbegin() const noexcept {
             return const_iterator(data() + sizeof(NodeRefList));
         }
 
-        const_iterator cend() const {
+        /// Returns an iterator to the end.
+        const_iterator cend() const noexcept {
             return const_iterator(data() + byte_size());
         }
 
-        const_iterator begin() const {
+        /// Returns an iterator to the beginning.
+        const_iterator begin() const noexcept {
             return cbegin();
         }
 
-        const_iterator end() const {
+        /// Returns an iterator to the end.
+        const_iterator end() const noexcept {
             return cend();
         }
 
-        const_reverse_iterator crbegin() const {
+        /// Returns a reverse_iterator to the beginning.
+        const_reverse_iterator crbegin() const noexcept {
             return const_reverse_iterator(cend());
         }
 
-        const_reverse_iterator crend() const {
+        /// Returns a reverse_iterator to the end.
+        const_reverse_iterator crend() const noexcept {
             return const_reverse_iterator(cbegin());
         }
 
diff --git a/include/osmium/osm/way.hpp b/include/osmium/osm/way.hpp
index 6d3e2de..3c5f1f6 100644
--- a/include/osmium/osm/way.hpp
+++ b/include/osmium/osm/way.hpp
@@ -49,12 +49,14 @@ namespace osmium {
     /**
      * List of node references (id and location) in a way.
      */
-    class WayNodeList : public NodeRefList<osmium::item_type::way_node_list> {
+    class WayNodeList : public NodeRefList {
 
     public:
 
+        static constexpr osmium::item_type itemtype = osmium::item_type::way_node_list;
+
         WayNodeList():
-            NodeRefList<osmium::item_type::way_node_list>() {
+            NodeRefList(itemtype) {
         }
 
     }; // class WayNodeList
diff --git a/test/data-tests/testdata-xml.cpp b/test/data-tests/testdata-xml.cpp
index 94af6f9..1969171 100644
--- a/test/data-tests/testdata-xml.cpp
+++ b/test/data-tests/testdata-xml.cpp
@@ -285,7 +285,6 @@ TEST_CASE("Reading OSM XML 121") {
         }, osmium::gzip_error);
     }
 
-#if 0
     SECTION("Using Reader") {
         REQUIRE_THROWS_AS({
             osmium::io::Reader reader(filename("121-truncated_gzip_file", "osm.gz"));
@@ -294,12 +293,104 @@ TEST_CASE("Reading OSM XML 121") {
             reader.close();
         }, osmium::gzip_error);
     }
-#endif
 
 }
 
 // =============================================
 
+TEST_CASE("Reading OSM XML 122") {
+
+    SECTION("Direct") {
+        REQUIRE_THROWS_AS( {
+            read_xml("122-no_osm_element");
+        }, osmium::xml_error);
+    }
+
+    SECTION("Using Reader") {
+        REQUIRE_THROWS_AS({
+            osmium::io::Reader reader(filename("122-no_osm_element"));
+            osmium::io::Header header = reader.header();
+            osmium::memory::Buffer buffer = reader.read();
+            reader.close();
+        }, osmium::xml_error);
+    }
+
+}
+
+// =============================================
+
+TEST_CASE("Reading OSM XML 140") {
+
+    SECTION("Using Reader") {
+        osmium::io::Reader reader(filename("140-unicode"));
+        osmium::memory::Buffer buffer = reader.read();
+        reader.close();
+
+        int count = 0;
+        for (auto it = buffer.begin<osmium::Node>(); it != buffer.end<osmium::Node>(); ++it) {
+            ++count;
+            REQUIRE(it->id() == count);
+            const osmium::TagList& t = it->tags();
+
+            const char* uc = t["unicode_char"];
+
+            auto len = atoi(t["unicode_utf8_length"]);
+            REQUIRE(len == strlen(uc));
+
+            REQUIRE(!strcmp(uc, t["unicode_xml"]));
+
+            switch (count) {
+                case 1:
+                    REQUIRE(!strcmp(uc, u8"a"));
+                    break;
+                case 2:
+                    REQUIRE(!strcmp(uc, u8"\u00e4"));
+                    break;
+                case 3:
+                    REQUIRE(!strcmp(uc, u8"\u30dc"));
+                    break;
+                case 4:
+                    REQUIRE(!strcmp(uc, u8"\U0001d11e"));
+                    break;
+                case 5:
+                    REQUIRE(!strcmp(uc, u8"\U0001f6eb"));
+                    break;
+                default:
+                    REQUIRE(false); // should not be here
+            }
+        }
+        REQUIRE(count == 5);
+    }
+
+}
+
+
+// =============================================
+
+TEST_CASE("Reading OSM XML 141") {
+
+    SECTION("Using Reader") {
+        osmium::io::Reader reader(filename("141-entities"));
+        osmium::memory::Buffer buffer = reader.read();
+        reader.close();
+        REQUIRE(buffer.committed() > 0);
+        REQUIRE(buffer.get<osmium::memory::Item>(0).type() == osmium::item_type::node);
+
+        const osmium::Node& node = buffer.get<osmium::Node>(0);
+        const osmium::TagList& tags = node.tags();
+
+        REQUIRE(!strcmp(tags["less-than"],    "<"));
+        REQUIRE(!strcmp(tags["greater-than"], ">"));
+        REQUIRE(!strcmp(tags["apostrophe"],   "'"));
+        REQUIRE(!strcmp(tags["ampersand"],    "&"));
+        REQUIRE(!strcmp(tags["quote"],        "\""));
+    }
+
+}
+
+
+// =============================================
+
 TEST_CASE("Reading OSM XML 200") {
 
     SECTION("Direct") {
diff --git a/test/t/basic/test_box.cpp b/test/t/basic/test_box.cpp
index 73a45f4..6ebf95c 100644
--- a/test/t/basic/test_box.cpp
+++ b/test/t/basic/test_box.cpp
@@ -25,12 +25,18 @@ SECTION("instantiation_and_extend_with_undefined") {
 
 SECTION("instantiation_and_extend") {
     osmium::Box b;
-    b.extend(osmium::Location(1.2, 3.4));
+    osmium::Location loc1 { 1.2, 3.4 };
+    b.extend(loc1);
     REQUIRE(!!b);
     REQUIRE(!!b.bottom_left());
     REQUIRE(!!b.top_right());
-    b.extend(osmium::Location(3.4, 4.5));
-    b.extend(osmium::Location(5.6, 7.8));
+    REQUIRE(b.contains(loc1));
+
+    osmium::Location loc2 { 3.4, 4.5 };
+    osmium::Location loc3 { 5.6, 7.8 };
+
+    b.extend(loc2);
+    b.extend(loc3);
     REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4));
     REQUIRE(b.top_right() == osmium::Location(5.6, 7.8));
 
@@ -38,6 +44,10 @@ SECTION("instantiation_and_extend") {
     b.extend(osmium::Location());
     REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4));
     REQUIRE(b.top_right() == osmium::Location(5.6, 7.8));
+
+    REQUIRE(b.contains(loc1));
+    REQUIRE(b.contains(loc2));
+    REQUIRE(b.contains(loc3));
 }
 
 SECTION("output_defined") {

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