[libosmium] 01/06: Imported Upstream version 2.7.0

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Wed Jun 1 15:19:20 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 5a6d9f85bb898ee2ac047ec8732477b15d75fbb9
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Wed Jun 1 13:32:09 2016 +0200

    Imported Upstream version 2.7.0
---
 .travis.yml                                        |    3 +-
 CHANGELOG.md                                       |   47 +-
 CMakeLists.txt                                     |    4 +-
 include/gdalcpp.hpp                                |   91 +-
 include/osmium/area/assembler.hpp                  | 1530 +++++++++++++++-----
 include/osmium/area/detail/node_ref_segment.hpp    |  309 ++--
 include/osmium/area/detail/proto_ring.hpp          |  235 ++-
 include/osmium/area/detail/segment_list.hpp        |  164 ++-
 include/osmium/area/detail/vector.hpp              |  120 ++
 include/osmium/area/multipolygon_collector.hpp     |   26 +-
 include/osmium/area/problem_reporter.hpp           |   76 +-
 include/osmium/area/problem_reporter_exception.hpp |   30 +-
 include/osmium/area/problem_reporter_ogr.hpp       |  143 +-
 include/osmium/area/problem_reporter_stream.hpp    |   33 +-
 include/osmium/area/stats.hpp                      |  128 ++
 include/osmium/builder/osm_object_builder.hpp      |   12 +-
 include/osmium/diff_iterator.hpp                   |    8 +-
 include/osmium/dynamic_handler.hpp                 |    2 +-
 include/osmium/geom/factory.hpp                    |   18 +-
 include/osmium/geom/geojson.hpp                    |   10 +-
 include/osmium/geom/geos.hpp                       |   10 +-
 include/osmium/geom/ogr.hpp                        |   35 +-
 include/osmium/geom/rapid_geojson.hpp              |   10 +-
 include/osmium/geom/wkb.hpp                        |   13 +-
 include/osmium/geom/wkt.hpp                        |   10 +-
 include/osmium/handler/chain.hpp                   |    2 +-
 include/osmium/handler/disk_store.hpp              |    2 +-
 include/osmium/handler/node_locations_for_ways.hpp |   16 +-
 include/osmium/handler/object_relations.hpp        |    2 +-
 include/osmium/index/detail/mmap_vector_base.hpp   |   31 +-
 include/osmium/index/detail/mmap_vector_file.hpp   |   17 +-
 include/osmium/index/detail/vector_map.hpp         |   16 +-
 include/osmium/index/detail/vector_multimap.hpp    |    8 +-
 include/osmium/index/map.hpp                       |   12 +-
 include/osmium/index/map/sparse_mem_map.hpp        |    2 +-
 include/osmium/index/multimap.hpp                  |    8 +-
 include/osmium/index/multimap/hybrid.hpp           |   14 +-
 .../osmium/index/multimap/sparse_mem_multimap.hpp  |   11 +-
 include/osmium/io/compression.hpp                  |   14 +-
 include/osmium/io/detail/debug_output_format.hpp   |   38 +
 include/osmium/io/detail/input_format.hpp          |   14 +-
 include/osmium/io/detail/opl_output_format.hpp     |   38 +-
 include/osmium/io/detail/output_format.hpp         |    4 +-
 include/osmium/io/detail/pbf_decoder.hpp           |   24 +-
 include/osmium/io/detail/pbf_output_format.hpp     |   35 +-
 include/osmium/io/detail/protobuf_tags.hpp         |    4 +-
 include/osmium/io/detail/queue_util.hpp            |   19 +-
 include/osmium/io/detail/string_table.hpp          |   13 +-
 include/osmium/io/detail/xml_input_format.hpp      |  126 +-
 include/osmium/io/detail/xml_output_format.hpp     |   30 +-
 include/osmium/io/input_iterator.hpp               |   20 +-
 include/osmium/io/output_iterator.hpp              |    8 +-
 include/osmium/memory/buffer.hpp                   |   10 +
 include/osmium/memory/collection.hpp               |   16 +-
 include/osmium/memory/item.hpp                     |    2 +-
 include/osmium/memory/item_iterator.hpp            |  110 +-
 include/osmium/object_pointer_collection.hpp       |    4 +-
 include/osmium/osm/area.hpp                        |   33 +-
 include/osmium/osm/changeset.hpp                   |   18 +-
 include/osmium/osm/crc.hpp                         |    1 +
 include/osmium/osm/entity_bits.hpp                 |   18 +-
 include/osmium/osm/location.hpp                    |   42 +-
 include/osmium/osm/object.hpp                      |   68 +-
 include/osmium/osm/object_comparisons.hpp          |   26 +-
 include/osmium/osm/relation.hpp                    |    2 +-
 include/osmium/osm/tag.hpp                         |   59 +-
 include/osmium/osm/types.hpp                       |   20 +-
 include/osmium/osm/types_from_string.hpp           |   18 +-
 include/osmium/relations/collector.hpp             |   32 +-
 include/osmium/relations/detail/member_meta.hpp    |    2 +-
 include/osmium/relations/detail/relation_meta.hpp  |    4 +-
 include/osmium/tags/filter.hpp                     |   18 +-
 include/osmium/tags/regex_filter.hpp               |    2 +-
 include/osmium/thread/pool.hpp                     |    4 +-
 include/osmium/thread/sorted_queue.hpp             |  159 --
 include/osmium/util/delta.hpp                      |   11 +-
 include/osmium/util/iterator.hpp                   |   15 +-
 include/osmium/util/memory_mapping.hpp             |   16 +-
 .../{tags/regex_filter.hpp => util/misc.hpp}       |   32 +-
 include/osmium/util/{iterator.hpp => timer.hpp}    |   64 +-
 .../osmium/{tags/regex_filter.hpp => version.hpp}  |   30 +-
 test/CMakeLists.txt                                |    1 +
 test/README                                        |    8 -
 test/data-tests/testdata-multipolygon.cpp          |    4 +-
 test/data-tests/testdata-xml.cpp                   |   28 +-
 test/include/catch.hpp                             |  382 ++++-
 test/t/area/test_node_ref_segment.cpp              |  185 ++-
 test/t/basic/test_entity_bits.cpp                  |    1 +
 test/t/basic/test_location.cpp                     |   16 +-
 test/t/basic/test_object_comparisons.cpp           |  132 +-
 test/t/basic/test_types_from_string.cpp            |   14 +-
 test/t/basic/test_way.cpp                          |    2 +-
 test/t/builder/test_attr.cpp                       |    4 +-
 test/t/geom/test_geojson.cpp                       |   22 +-
 test/t/geom/test_geos.cpp                          |    4 +
 test/t/geom/test_ogr.cpp                           |    4 +
 test/t/index/test_file_based_index.cpp             |  155 ++
 test/t/index/test_id_to_location.cpp               |   10 +
 test/t/io/test_writer.cpp                          |    9 +-
 test/t/io/test_writer_with_mock_compression.cpp    |    3 +-
 test/t/io/test_writer_with_mock_encoder.cpp        |    3 +-
 test/t/tags/test_tag_list.cpp                      |    8 +-
 test/t/util/test_memory_mapping.cpp                |   13 +-
 103 files changed, 3894 insertions(+), 1545 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index ac0d270..9da37fa 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -154,6 +154,5 @@ before_script:
   - CXX=${COMPILER} CXXFLAGS=${COMPILER_FLAGS} cmake -LA .. -DCMAKE_BUILD_TYPE=${BUILD_TYPE} -DOSM_TESTDATA="${TRAVIS_BUILD_DIR}/deps/osm-testdata"
 
 script:
-  - make VERBOSE=1
-  - ctest --output-on-failure
+  - make VERBOSE=1 && ctest --output-on-failure
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 3946448..d60b548 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,50 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 ### Fixed
 
 
+## [2.7.0] - 2016-06-01
+
+### Added
+
+- New functions for iterating over specific item types in buffers
+  (`osmium::memory::Buffer::select()`), over specific subitems
+  (`osmium::OSMObject::subitems()`), and for iterating over all rings of
+  an area (`osmium::Areas::outer_rings(`), `inner_rings()`).
+- Debug output optionally prints CRC32 when `add_crc32` file option is set.
+
+### Changed
+
+- XML parser will not allow any XML entities which are usually not used in OSM
+  files anyway. This can help avoiding DOS attacks.
+- Removed SortedQueue implementation which was never used.
+- Also incorporate Locations in NodeRefs into CRC32 checksums. This means
+  all checksums will be different compared to earlier versions of libosmium.
+- The completely new algorithm for assembling multipolygons is much faster,
+  has better error reporting, generates statistics and can build more complex
+  multipolygons correctly. The ProblemReporter classes have changed to make
+  this happen, if you have written your own, you have to fix it.
+- Sparse node location stores are now only sorted if needed, ie. when nodes
+  come in unordered.
+
+### Fixed
+
+- Output operator for Location shows full precision.
+- Undefined behaviour in WKB writer and `types_from_string()` function.
+- Fix unsigned overflow in pool.hpp.
+- OSM objects are now ordered by type (nodes, then ways, then relations),
+  then ID, then version, then timestamp. Ordering by timestamp is normally
+  not necessary, because there can't be two objects with same type, ID, and
+  version but different timestamp. But this can happen when diffs are
+  created from OSM extracts, so we check for this here. This change also
+  makes sure IDs are always ordered by absolute IDs, positives first, so
+  order is 0, 1, -1, 2, -2, ...
+- Data corruption bug fixed in disk based indexes (used for the node
+  location store for instance). This only affected you, if you created
+  and index, closed it, and re-opened it (possibly in a different process)
+  and if there were missing nodes. If you looked up those nodes, you got
+  location (0,0) back instead of an error.
+- Memory corruption bug showing up with GDAL 2.
+
+
 ## [2.6.1] - 2016-02-22
 
 ### Added
@@ -276,7 +320,8 @@ 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.6.1...HEAD
+[unreleased]: https://github.com/osmcode/libosmium/compare/v2.7.0...HEAD
+[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
 [2.5.4]: https://github.com/osmcode/libosmium/compare/v2.5.3...v2.5.4
diff --git a/CMakeLists.txt b/CMakeLists.txt
index bc9b12e..5f74fab 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -24,8 +24,8 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Cover
 project(libosmium)
 
 set(LIBOSMIUM_VERSION_MAJOR 2)
-set(LIBOSMIUM_VERSION_MINOR 6)
-set(LIBOSMIUM_VERSION_PATCH 1)
+set(LIBOSMIUM_VERSION_MINOR 7)
+set(LIBOSMIUM_VERSION_PATCH 0)
 
 set(LIBOSMIUM_VERSION
     "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}")
diff --git a/include/gdalcpp.hpp b/include/gdalcpp.hpp
index 1502f2f..4f3d480 100644
--- a/include/gdalcpp.hpp
+++ b/include/gdalcpp.hpp
@@ -110,8 +110,12 @@ namespace gdalcpp {
     namespace detail {
 
         struct init_wrapper {
+#if GDAL_VERSION_MAJOR >= 2
+            init_wrapper() { GDALAllRegister(); }
+#else
             init_wrapper() { OGRRegisterAll(); }
             ~init_wrapper() { OGRCleanupAll(); }
+#endif
         };
 
         struct init_library {
@@ -237,6 +241,8 @@ namespace gdalcpp {
         detail::Options m_options;
         SRS m_srs;
         std::unique_ptr<gdal_dataset_type, gdal_dataset_deleter> m_dataset;
+        uint64_t m_edit_count = 0;
+        uint64_t m_max_edit_count = 0;
 
     public:
 
@@ -255,6 +261,15 @@ namespace gdalcpp {
             }
         }
 
+        ~Dataset() {
+            try {
+                if (m_edit_count > 0) {
+                    commit_transaction();
+                }
+            } catch (...) {
+            }
+        }
+
         const std::string& driver_name() const {
             return m_driver_name;
         }
@@ -282,10 +297,14 @@ namespace gdalcpp {
             exec(sql.c_str());
         }
 
-
         Dataset& start_transaction() {
 #if GDAL_VERSION_MAJOR >= 2
             m_dataset->StartTransaction();
+#else
+            OGRLayer* layer = m_dataset->GetLayer(0);
+            if (layer) {
+                layer->StartTransaction();
+            }
 #endif
             return *this;
         }
@@ -293,7 +312,38 @@ namespace gdalcpp {
         Dataset& commit_transaction() {
 #if GDAL_VERSION_MAJOR >= 2
             m_dataset->CommitTransaction();
+#else
+            OGRLayer* layer = m_dataset->GetLayer(0);
+            if (layer) {
+                layer->CommitTransaction();
+            }
 #endif
+            m_edit_count = 0;
+            return *this;
+        }
+
+        void prepare_edit() {
+            if (m_max_edit_count != 0 && m_edit_count == 0) {
+                start_transaction();
+            }
+        }
+
+        void finalize_edit() {
+            if (m_max_edit_count != 0 && ++m_edit_count > m_max_edit_count) {
+                commit_transaction();
+            }
+        }
+
+        Dataset& enable_auto_transactions(uint64_t edits = 100000) {
+            m_max_edit_count = edits;
+            return *this;
+        }
+
+        Dataset& disable_auto_transactions() {
+            if (m_max_edit_count != 0 && m_edit_count > 0) {
+                commit_transaction();
+            }
+            m_max_edit_count = 0;
             return *this;
         }
 
@@ -346,19 +396,32 @@ namespace gdalcpp {
             return *this;
         }
 
+        void create_feature(OGRFeature* feature) {
+            dataset().prepare_edit();
+            OGRErr result = m_layer->CreateFeature(feature);
+            if (result != OGRERR_NONE) {
+                throw gdal_error(std::string("creating feature in layer '") + name() + "' failed", result, dataset().driver_name(), dataset().dataset_name());
+            }
+            dataset().finalize_edit();
+        }
+
         Layer& start_transaction() {
+#if GDAL_VERSION_MAJOR < 2
             OGRErr result = m_layer->StartTransaction();
             if (result != OGRERR_NONE) {
                 throw gdal_error(std::string("starting transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name());
             }
+#endif
             return *this;
         }
 
         Layer& commit_transaction() {
+#if GDAL_VERSION_MAJOR < 2
             OGRErr result = m_layer->CommitTransaction();
             if (result != OGRERR_NONE) {
                 throw gdal_error(std::string("committing transaction on layer '") + name() + "' failed", result, m_dataset.driver_name(), m_dataset.dataset_name(), name());
             }
+#endif
             return *this;
          }
 
@@ -366,36 +429,44 @@ namespace gdalcpp {
 
     class Feature {
 
+        struct ogr_feature_deleter {
+
+            void operator()(OGRFeature* feature) {
+                 OGRFeature::DestroyFeature(feature);
+            }
+
+        }; // struct ogr_feature_deleter
+
         Layer& m_layer;
-        OGRFeature m_feature;
+        std::unique_ptr<OGRFeature, ogr_feature_deleter> m_feature;
 
     public:
 
         Feature(Layer& layer, std::unique_ptr<OGRGeometry>&& geometry) :
             m_layer(layer),
-            m_feature(m_layer.get().GetLayerDefn()) {
-            OGRErr result = m_feature.SetGeometryDirectly(geometry.release());
+            m_feature(OGRFeature::CreateFeature(m_layer.get().GetLayerDefn())) {
+            if (!m_feature) {
+                throw std::bad_alloc();
+            }
+            OGRErr result = m_feature->SetGeometryDirectly(geometry.release());
             if (result != OGRERR_NONE) {
                 throw gdal_error(std::string("setting feature geometry in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name());
             }
         }
 
         void add_to_layer() {
-            OGRErr result = m_layer.get().CreateFeature(&m_feature);
-            if (result != OGRERR_NONE) {
-                throw gdal_error(std::string("creating feature in layer '") + m_layer.name() + "' failed", result, m_layer.dataset().driver_name(), m_layer.dataset().dataset_name());
-            }
+            m_layer.create_feature(m_feature.get());
         }
 
         template <class T>
         Feature& set_field(int n, T&& arg) {
-            m_feature.SetField(n, std::forward<T>(arg));
+            m_feature->SetField(n, std::forward<T>(arg));
             return *this;
         }
 
         template <class T>
         Feature& set_field(const char* name, T&& arg) {
-            m_feature.SetField(name, std::forward<T>(arg));
+            m_feature->SetField(name, std::forward<T>(arg));
             return *this;
         }
 
diff --git a/include/osmium/area/assembler.hpp b/include/osmium/area/assembler.hpp
index a2d1c8e..f964d4b 100644
--- a/include/osmium/area/assembler.hpp
+++ b/include/osmium/area/assembler.hpp
@@ -42,6 +42,9 @@ DEALINGS IN THE SOFTWARE.
 #include <set>
 #include <string>
 #include <map>
+#include <numeric>
+#include <unordered_map>
+#include <unordered_set>
 #include <vector>
 
 #include <osmium/builder/osm_object_builder.hpp>
@@ -50,87 +53,232 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/osm/location.hpp>
 #include <osmium/osm/relation.hpp>
 #include <osmium/tags/filter.hpp>
+#include <osmium/util/compatibility.hpp>
+#include <osmium/util/iterator.hpp>
+#include <osmium/util/timer.hpp>
 
 #include <osmium/area/detail/proto_ring.hpp>
 #include <osmium/area/detail/node_ref_segment.hpp>
 #include <osmium/area/detail/segment_list.hpp>
 #include <osmium/area/problem_reporter.hpp>
+#include <osmium/area/stats.hpp>
 
 namespace osmium {
 
     namespace area {
 
-        using osmium::area::detail::ProtoRing;
-
+        /**
+         * Configuration for osmium::area::Assembler objects. Create this
+         * once, set the options you want and then re-use it every time you
+         * create an Assembler object.
+         */
         struct AssemblerConfig {
 
-            osmium::area::ProblemReporter* problem_reporter;
+            /**
+             * Optional pointer to problem reporter.
+             */
+            osmium::area::ProblemReporter* problem_reporter = nullptr;
+
+            /**
+             * Debug level. If this is greater than zero, debug messages will
+             * be printed to stderr. Available levels are 1 to 3. Note that
+             * level 2 and above will generate a lot of messages!
+             */
+            int debug_level = 0;
+
+            /**
+             * The roles of multipolygon members are ignored when assembling
+             * multipolygons, because they are often missing or wrong. If this
+             * is set, the roles are checked after the multipolygons are built
+             * against what the assembly process decided where the inner and
+             * outer rings are. This slows down the processing, so it only
+             * makes sense if you want to get the problem reports.
+             */
+            bool check_roles = false;
+
+            /**
+             * When the assembler can't create an area, usually because its
+             * geometry would be invalid, it will create an "empty" area object
+             * without rings. This allows you to detect where an area was
+             * invalid.
+             *
+             * If this is set to false, invalid areas will simply be discarded.
+             */
+            bool create_empty_areas = true;
+
+            /**
+             * Create areas for (multi)polygons where the tags are on the
+             * relation.
+             *
+             * If this is set to false, those areas will simply be discarded.
+             */
+            bool create_new_style_polygons = true;
+
+            /**
+             * Create areas for (multi)polygons where the tags are on the
+             * outer way(s).
+             *
+             * If this is set to false, those areas will simply be discarded.
+             */
+            bool create_old_style_polygons = true;
+
+            /**
+             * Create areas for polygons created from ways.
+             *
+             * If this is set to false, those areas will simply be discarded.
+             */
+            bool create_way_polygons = true;
+
+            /**
+             * Keep the type tag from multipolygon relations on the area
+             * object. By default this is false, and the type tag will be
+             * removed.
+             */
+            bool keep_type_tag = false;
 
-            // Enables debug output to stderr
-            bool debug;
+            AssemblerConfig() noexcept = default;
 
-            explicit AssemblerConfig(osmium::area::ProblemReporter* pr = nullptr, bool d = false) :
+            /**
+             * Constructor
+             * @deprecated Use default constructor and set values afterwards.
+             */
+            explicit AssemblerConfig(osmium::area::ProblemReporter* pr, bool d = false) :
                 problem_reporter(pr),
-                debug(d) {
+                debug_level(d) {
             }
 
             /**
              * Enable or disable debug output to stderr. This is for Osmium
              * developers only.
+             *
+             * @deprecated Set debug_level directly.
              */
             void enable_debug_output(bool d = true) {
-                debug = d;
+                debug_level = d;
             }
 
         }; // struct AssemblerConfig
 
+        namespace detail {
+
+            using open_ring_its_type = std::list<std::list<detail::ProtoRing>::iterator>;
+
+            struct location_to_ring_map {
+                osmium::Location location;
+                open_ring_its_type::iterator ring_it;
+                bool start;
+
+                location_to_ring_map(const osmium::Location& l, const open_ring_its_type::iterator& r, bool s) noexcept :
+                    location(l),
+                    ring_it(r),
+                    start(s) {
+                }
+
+                explicit location_to_ring_map(const osmium::Location& l) noexcept :
+                    location(l),
+                    ring_it(),
+                    start(false) {
+                }
+
+                const detail::ProtoRing& ring() const noexcept {
+                    return **ring_it;
+                }
+
+            }; // struct location_to_ring_map
+
+            inline bool operator==(const location_to_ring_map& a, const location_to_ring_map& b) noexcept {
+                return a.location == b.location;
+            }
+
+            inline bool operator<(const location_to_ring_map& a, const location_to_ring_map& b) noexcept {
+                return a.location < b.location;
+            }
+
+        } // namespace detail
+
         /**
-         * Assembles area objects from multipolygon relations and their
-         * members. This is called by the MultipolygonCollector object
-         * after all members have been collected.
+         * Assembles area objects from closed ways or multipolygon relations
+         * and their members.
          */
         class Assembler {
 
-            const AssemblerConfig m_config;
+            using open_ring_its_type = detail::open_ring_its_type;
+            using location_to_ring_map = detail::location_to_ring_map;
+
+            struct slocation {
+
+                static constexpr const uint32_t invalid_item = 1 << 30;
+
+                uint32_t item : 31;
+                uint32_t reverse : 1;
 
-            // The way segments
+                slocation() noexcept :
+                    item(invalid_item),
+                    reverse(false) {
+                }
+
+                explicit slocation(uint32_t n, bool r = false) noexcept :
+                    item(n),
+                    reverse(r) {
+                }
+
+                osmium::Location location(const detail::SegmentList& segment_list) const noexcept {
+                    const auto& segment = segment_list[item];
+                    return reverse ? segment.second().location() : segment.first().location();
+                }
+
+                const osmium::NodeRef& node_ref(const detail::SegmentList& segment_list) const noexcept {
+                    const auto& segment = segment_list[item];
+                    return reverse ? segment.second() : segment.first();
+                }
+
+                osmium::Location location(const detail::SegmentList& segment_list, const osmium::Location& default_location) const noexcept {
+                    if (item == invalid_item) {
+                        return default_location;
+                    }
+                    return location(segment_list);
+                }
+
+            }; // struct slocation
+
+            // Configuration settings for this Assembler
+            const AssemblerConfig& m_config;
+
+            // List of segments (connection between two nodes)
             osmium::area::detail::SegmentList m_segment_list;
 
-            // The rings we are building from the way segments
-            std::list<ProtoRing> m_rings;
+            // The rings we are building from the segments
+            std::list<detail::ProtoRing> m_rings;
 
-            std::vector<ProtoRing*> m_outer_rings;
-            std::vector<ProtoRing*> m_inner_rings;
+            // All node locations
+            std::vector<slocation> m_locations;
 
-            int m_inner_outer_mismatches { 0 };
+            // All locations where more than two segments start/end
+            std::vector<Location> m_split_locations;
 
-            bool debug() const {
-                return m_config.debug;
+            // Statistics
+            area_stats m_stats;
+
+            bool debug() const noexcept {
+                return m_config.debug_level > 1;
             }
 
-            /**
-             * Checks whether the given NodeRefs have the same location.
-             * Uses the actual location for the test, not the id. If both
-             * have the same location, but not the same id, a problem
-             * point will be added to the list of problem points.
-             */
-            bool has_same_location(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) {
-                if (nr1.location() != nr2.location()) {
+            bool report_ways() const noexcept {
+                if (!m_config.problem_reporter) {
                     return false;
                 }
-                if (nr1.ref() != nr2.ref()) {
-                    if (m_config.problem_reporter) {
-                        m_config.problem_reporter->report_duplicate_node(nr1.ref(), nr2.ref(), nr1.location());
-                    }
-                }
-                return true;
+                return m_stats.duplicate_nodes ||
+                       m_stats.duplicate_segments ||
+                       m_stats.intersections ||
+                       m_stats.open_rings ||
+                       m_stats.short_ways ||
+                       m_stats.touching_rings ||
+                       m_stats.ways_in_multiple_rings ||
+                       m_stats.wrong_role;
             }
 
             void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Way& way) const {
-                osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder);
-                for (const osmium::Tag& tag : way.tags()) {
-                    tl_builder.add_tag(tag.key(), tag.value());
-                }
+                builder.add_item(&way.tags());
             }
 
             void add_common_tags(osmium::builder::TagListBuilder& tl_builder, std::set<const osmium::Way*>& ways) const {
@@ -144,13 +292,13 @@ namespace osmium {
                     }
                 }
 
-                size_t num_ways = ways.size();
+                const size_t num_ways = ways.size();
                 for (const auto& t_c : counter) {
                     if (debug()) {
                         std::cerr << "        tag " << t_c.first << " is used " << t_c.second << " times in " << num_ways << " ways\n";
                     }
                     if (t_c.second == num_ways) {
-                        size_t len = std::strlen(t_c.first.c_str());
+                        const size_t len = std::strlen(t_c.first.c_str());
                         tl_builder.add_tag(t_c.first.c_str(), t_c.first.c_str() + len + 1);
                     }
                 }
@@ -169,13 +317,22 @@ namespace osmium {
 
             }; // struct MPFilter
 
-            static MPFilter& filter() {
-                static MPFilter filter;
+            static const MPFilter& filter() noexcept {
+                static const MPFilter filter;
                 return filter;
             }
 
-            void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Relation& relation) const {
-                const auto count = std::count_if(relation.tags().begin(), relation.tags().end(), filter());
+            static void copy_tags_without_type(osmium::builder::AreaBuilder& builder, const osmium::TagList& tags) {
+                osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder);
+                for (const osmium::Tag& tag : tags) {
+                    if (std::strcmp(tag.key(), "type")) {
+                        tl_builder.add_tag(tag.key(), tag.value());
+                    }
+                }
+            }
+
+            void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Relation& relation) {
+                const auto count = std::count_if(relation.tags().cbegin(), relation.tags().cend(), filter());
 
                 if (debug()) {
                     std::cerr << "  found " << count << " tags on relation (without ignored ones)\n";
@@ -186,29 +343,27 @@ namespace osmium {
                         std::cerr << "    use tags from relation\n";
                     }
 
-                    // write out all tags except type=*
-                    osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder);
-                    for (const osmium::Tag& tag : relation.tags()) {
-                        if (strcmp(tag.key(), "type")) {
-                            tl_builder.add_tag(tag.key(), tag.value());
-                        }
+                    if (m_config.keep_type_tag) {
+                        builder.add_item(&relation.tags());
+                    } else {
+                        copy_tags_without_type(builder, relation.tags());
                     }
                 } else {
+                    ++m_stats.no_tags_on_relation;
                     if (debug()) {
                         std::cerr << "    use tags from outer ways\n";
                     }
                     std::set<const osmium::Way*> ways;
-                    for (const auto& ring : m_outer_rings) {
-                        ring->get_ways(ways);
+                    for (const auto& ring : m_rings) {
+                        if (ring.is_outer()) {
+                            ring.get_ways(ways);
+                        }
                     }
                     if (ways.size() == 1) {
                         if (debug()) {
                             std::cerr << "      only one outer way\n";
                         }
-                        osmium::builder::TagListBuilder tl_builder(builder.buffer(), &builder);
-                        for (const osmium::Tag& tag : (*ways.begin())->tags()) {
-                            tl_builder.add_tag(tag.key(), tag.value());
-                        }
+                        builder.add_item(&(*ways.cbegin())->tags());
                     } else {
                         if (debug()) {
                             std::cerr << "      multiple outer ways, get common tags\n";
@@ -219,494 +374,1065 @@ namespace osmium {
                 }
             }
 
+            template <typename TBuilder>
+            static void build_ring_from_proto_ring(osmium::builder::AreaBuilder& builder, const detail::ProtoRing& ring) {
+                TBuilder ring_builder(builder.buffer(), &builder);
+                ring_builder.add_node_ref(ring.get_node_ref_start());
+                for (const auto& segment : ring.segments()) {
+                    ring_builder.add_node_ref(segment->stop());
+                }
+            }
+
             /**
-             * Go through all the rings and find rings that are not closed.
-             * Problems are reported through the problem reporter.
-             *
-             * @returns true if any rings were not closed, false otherwise
+             * Append each outer ring together with its inner rings to the
+             * area in the buffer.
              */
-            bool check_for_open_rings() {
-                bool open_rings = false;
-
-                for (const auto& ring : m_rings) {
-                    if (!ring.closed()) {
-                        open_rings = true;
-                        if (m_config.problem_reporter) {
-                            m_config.problem_reporter->report_ring_not_closed(ring.get_node_ref_front().location(), ring.get_node_ref_back().location());
+            void add_rings_to_area(osmium::builder::AreaBuilder& builder) const {
+                for (const detail::ProtoRing& ring : m_rings) {
+                    if (ring.is_outer()) {
+                        build_ring_from_proto_ring<osmium::builder::OuterRingBuilder>(builder, ring);
+                        for (const detail::ProtoRing* inner : ring.inner_rings()) {
+                            build_ring_from_proto_ring<osmium::builder::InnerRingBuilder>(builder, *inner);
                         }
                     }
                 }
-
-                return open_rings;
             }
 
-            /**
-             * Check whether there are any rings that can be combined with the
-             * given ring to one larger ring by appending the other ring to
-             * the end of this ring.
-             * If the rings can be combined they are and the function returns
-             * true.
-             */
-            bool possibly_combine_rings_back(ProtoRing& ring) {
-                const osmium::NodeRef& nr = ring.get_node_ref_back();
-
+            void check_inner_outer_roles() {
                 if (debug()) {
-                    std::cerr << "      possibly_combine_rings_back()\n";
+                    std::cerr << "    Checking inner/outer roles\n";
                 }
-                for (auto it = m_rings.begin(); it != m_rings.end(); ++it) {
-                    if (&*it != &ring && !it->closed()) {
-                        if (has_same_location(nr, it->get_node_ref_front())) {
+
+                std::unordered_map<const osmium::Way*, const detail::ProtoRing*> way_rings;
+                std::unordered_set<const osmium::Way*> ways_in_multiple_rings;
+
+                for (const detail::ProtoRing& ring : m_rings) {
+                    for (const auto& segment : ring.segments()) {
+                        assert(segment->way());
+
+                        if (!segment->role_empty() && (ring.is_outer() ? !segment->role_outer() : !segment->role_inner())) {
+                            ++m_stats.wrong_role;
                             if (debug()) {
-                                std::cerr << "      ring.last=it->first\n";
+                                std::cerr << "      Segment " << *segment << " from way " << segment->way()->id() << " has role '" << segment->role_name()
+                                          << "', but should have role '" << (ring.is_outer() ? "outer" : "inner") << "'\n";
                             }
-                            ring.merge_ring(*it, debug());
-                            m_rings.erase(it);
-                            return true;
-                        }
-                        if (has_same_location(nr, it->get_node_ref_back())) {
-                            if (debug()) {
-                                std::cerr << "      ring.last=it->last\n";
+                            if (m_config.problem_reporter) {
+                                if (ring.is_outer()) {
+                                    m_config.problem_reporter->report_role_should_be_outer(segment->way()->id(), segment->first().location(), segment->second().location());
+                                } else {
+                                    m_config.problem_reporter->report_role_should_be_inner(segment->way()->id(), segment->first().location(), segment->second().location());
+                                }
                             }
-                            ring.merge_ring_reverse(*it, debug());
-                            m_rings.erase(it);
-                            return true;
                         }
+
+                        auto& r = way_rings[segment->way()];
+                        if (!r) {
+                            r = ˚
+                        } else if (r != &ring) {
+                            ways_in_multiple_rings.insert(segment->way());
+                        }
+
                     }
                 }
-                return false;
+
+                for (const osmium::Way* way : ways_in_multiple_rings) {
+                    ++m_stats.ways_in_multiple_rings;
+                    if (debug()) {
+                        std::cerr << "      Way " << way->id() << " is in multiple rings\n";
+                    }
+                    if (m_config.problem_reporter) {
+                        m_config.problem_reporter->report_way_in_multiple_rings(*way);
+                    }
+                }
+
             }
 
-            /**
-             * Check whether there are any rings that can be combined with the
-             * given ring to one larger ring by prepending the other ring to
-             * the start of this ring.
-             * If the rings can be combined they are and the function returns
-             * true.
-             */
-            bool possibly_combine_rings_front(ProtoRing& ring) {
-                const osmium::NodeRef& nr = ring.get_node_ref_front();
+            detail::NodeRefSegment* get_next_segment(const osmium::Location& location) {
+                auto it = std::lower_bound(m_locations.begin(), m_locations.end(), slocation{}, [this, &location](const slocation& a, const slocation& b) {
+                    return a.location(m_segment_list, location) < b.location(m_segment_list, location);
+                });
+
+                assert(it != m_locations.end());
+                if (m_segment_list[it->item].is_done()) {
+                    ++it;
+                }
+                assert(it != m_locations.end());
+
+                assert(!m_segment_list[it->item].is_done());
+                return &m_segment_list[it->item];
+            }
+
+            class rings_stack_element {
+
+                int32_t m_y;
+                detail::ProtoRing* m_ring_ptr;
+
+            public:
+
+                rings_stack_element(int32_t y, detail::ProtoRing* ring_ptr) :
+                    m_y(y),
+                    m_ring_ptr(ring_ptr) {
+                }
+
+                int32_t y() const noexcept {
+                    return m_y;
+                }
+
+                const detail::ProtoRing& ring() const noexcept {
+                    return *m_ring_ptr;
+                }
+
+                detail::ProtoRing* ring_ptr() noexcept {
+                    return m_ring_ptr;
+                }
+
+                bool operator==(const rings_stack_element& rhs) const noexcept {
+                    return m_ring_ptr == rhs.m_ring_ptr;
+                }
+
+                bool operator<(const rings_stack_element& rhs) const noexcept {
+                    return m_y < rhs.m_y;
+                }
+
+            }; // class ring_stack_element
+
+            using rings_stack = std::vector<rings_stack_element>;
+
+            void remove_duplicates(rings_stack& outer_rings) {
+                while (true) {
+                    const auto it = std::adjacent_find(outer_rings.begin(), outer_rings.end());
+                    if (it == outer_rings.end()) {
+                        return;
+                    }
+                    outer_rings.erase(it, std::next(it, 2));
+                }
+            }
 
+            detail::ProtoRing* find_enclosing_ring(detail::NodeRefSegment* segment) {
                 if (debug()) {
-                    std::cerr << "      possibly_combine_rings_front()\n";
+                    std::cerr << "    Looking for ring enclosing " << *segment << "\n";
                 }
-                for (auto it = m_rings.begin(); it != m_rings.end(); ++it) {
-                    if (&*it != &ring && !it->closed()) {
-                        if (has_same_location(nr, it->get_node_ref_back())) {
+
+                const auto location = segment->first().location();
+                const auto end_location = segment->second().location();
+
+                while (segment->first().location() == location) {
+                    if (segment == &m_segment_list.back()) {
+                        break;
+                    }
+                    ++segment;
+                }
+
+                int nesting = 0;
+
+                rings_stack outer_rings;
+                while (segment >= &m_segment_list.front()) {
+                    if (!segment->is_direction_done()) {
+                        --segment;
+                        continue;
+                    }
+                    if (debug()) {
+                        std::cerr << "      Checking against " << *segment << "\n";
+                    }
+                    const osmium::Location& a = segment->first().location();
+                    const osmium::Location& b = segment->second().location();
+
+                    if (segment->first().location() == location) {
+                        const int64_t ax = a.x();
+                        const int64_t bx = b.x();
+                        const int64_t lx = end_location.x();
+                        const int64_t ay = a.y();
+                        const int64_t by = b.y();
+                        const int64_t ly = end_location.y();
+                        const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax);
+                        if (debug()) {
+                            std::cerr << "      Segment XXXX z=" << z << "\n";
+                        }
+                        if (z > 0) {
+                            nesting += segment->is_reverse() ? -1 : 1;
                             if (debug()) {
-                                std::cerr << "      ring.first=it->last\n";
+                                std::cerr << "        Segment is below (nesting=" << nesting << ")\n";
                             }
-                            ring.swap_segments(*it);
-                            ring.merge_ring(*it, debug());
-                            m_rings.erase(it);
-                            return true;
+                            if (segment->ring()->is_outer()) {
+                                if (debug()) {
+                                    std::cerr << "        Segment belongs to outer ring\n";
+                                }
+                                outer_rings.emplace_back(a.y(), segment->ring());
+                            }
+                        }
+                    } else if (a.x() <= location.x() && location.x() < b.x()) {
+                        if (debug()) {
+                            std::cerr << "        Is in x range\n";
                         }
-                        if (has_same_location(nr, it->get_node_ref_front())) {
+
+                        const int64_t ax = a.x();
+                        const int64_t bx = b.x();
+                        const int64_t lx = location.x();
+                        const int64_t ay = a.y();
+                        const int64_t by = b.y();
+                        const int64_t ly = location.y();
+                        const auto z = (bx - ax)*(ly - ay) - (by - ay)*(lx - ax);
+
+//                        std::cerr << "z=" << z << "\n";
+
+                        if (z >= 0) {
+                            nesting += segment->is_reverse() ? -1 : 1;
                             if (debug()) {
-                                std::cerr << "      ring.first=it->first\n";
+                                std::cerr << "        Segment is below (nesting=" << nesting << ")\n";
+                            }
+                            if (segment->ring()->is_outer()) {
+                                if (debug()) {
+                                    std::cerr << "        Segment belongs to outer ring\n";
+                                }
+                                int32_t y = int32_t(ay + (by - ay) * (lx - ax) / (bx - ax));
+                                outer_rings.emplace_back(y, segment->ring());
                             }
-                            ring.reverse();
-                            ring.merge_ring(*it, debug());
-                            m_rings.erase(it);
-                            return true;
                         }
                     }
+                    --segment;
                 }
-                return false;
-            }
 
-            void split_off_subring(osmium::area::detail::ProtoRing& ring, osmium::area::detail::ProtoRing::segments_type::iterator it, osmium::area::detail::ProtoRing::segments_type::iterator it_begin, osmium::area::detail::ProtoRing::segments_type::iterator it_end) {
-                if (debug()) {
-                    std::cerr << "        subring found at: " << *it << "\n";
+                if (nesting % 2 == 0) {
+                    if (debug()) {
+                        std::cerr << "    Decided that this is an outer ring\n";
+                    }
+                    return nullptr;
+                } else {
+                    if (debug()) {
+                        std::cerr << "    Decided that this is an inner ring\n";
+                    }
+                    assert(!outer_rings.empty());
+
+                    std::sort(outer_rings.rbegin(), outer_rings.rend());
+                    if (debug()) {
+                        for (const auto& o : outer_rings) {
+                            std::cerr << "        y=" << o.y() << " " << o.ring() << "\n";
+                        }
+                    }
+
+                    remove_duplicates(outer_rings);
+                    if (debug()) {
+                        std::cerr << "      after remove duplicates:\n";
+                        for (const auto& o : outer_rings) {
+                            std::cerr << "        y=" << o.y() << " " << o.ring() << "\n";
+                        }
+                    }
+
+                    assert(!outer_rings.empty());
+                    return outer_rings.front().ring_ptr();
                 }
-                ProtoRing new_ring(it_begin, it_end);
-                ring.remove_segments(it_begin, it_end);
+            }
+
+            bool is_split_location(const osmium::Location& location) const noexcept {
+                return std::find(m_split_locations.cbegin(), m_split_locations.cend(), location) != m_split_locations.cend();
+            }
+
+            uint32_t add_new_ring(slocation& node) {
+                detail::NodeRefSegment* segment = &m_segment_list[node.item];
+                assert(!segment->is_done());
+
                 if (debug()) {
-                    std::cerr << "        split into two rings:\n";
-                    std::cerr << "          " << new_ring << "\n";
-                    std::cerr << "          " << ring << "\n";
+                    std::cerr << "  Starting new ring at location " << node.location(m_segment_list) << " with segment " << *segment << "\n";
                 }
-                m_rings.push_back(std::move(new_ring));
-            }
 
-            bool has_closed_subring_back(ProtoRing& ring, const NodeRef& nr) {
-                if (ring.segments().size() < 3) {
-                    return false;
+                if (node.reverse) {
+                    segment->reverse();
                 }
-                if (debug()) {
-                    std::cerr << "      has_closed_subring_back()\n";
+
+                detail::ProtoRing* outer_ring = nullptr;
+
+                if (segment != &m_segment_list.front()) {
+                    outer_ring = find_enclosing_ring(segment);
                 }
-                const auto end = ring.segments().end();
-                for (auto it = ring.segments().begin() + 1; it != end - 1; ++it) {
-                    if (has_same_location(nr, it->first())) {
-                        split_off_subring(ring, it, it, end);
-                        return true;
+                segment->mark_direction_done();
+
+                m_rings.emplace_back(segment);
+                detail::ProtoRing* ring = &m_rings.back();
+                if (outer_ring) {
+                    if (debug()) {
+                        std::cerr << "    This is an inner ring. Outer ring is " << *outer_ring << "\n";
                     }
+                    outer_ring->add_inner_ring(ring);
+                    ring->set_outer_ring(outer_ring);
+                } else if (debug()) {
+                    std::cerr << "    This is an outer ring\n";
                 }
-                return false;
-            }
 
-            bool has_closed_subring_front(ProtoRing& ring, const NodeRef& nr) {
-                if (ring.segments().size() < 3) {
-                    return false;
+                const osmium::Location& first_location = node.location(m_segment_list);
+                osmium::Location last_location = segment->stop().location();
+
+                uint32_t nodes = 1;
+                while (first_location != last_location) {
+                    ++nodes;
+                    detail::NodeRefSegment* next_segment = get_next_segment(last_location);
+                    next_segment->mark_direction_done();
+                    if (next_segment->start().location() != last_location) {
+                        next_segment->reverse();
+                    }
+                    ring->add_segment_back(next_segment);
+                    if (debug()) {
+                        std::cerr << "    Next segment is " << *next_segment << "\n";
+                    }
+                    last_location = next_segment->stop().location();
                 }
+
+                ring->fix_direction();
+
                 if (debug()) {
-                    std::cerr << "      has_closed_subring_front()\n";
+                    std::cerr << "    Completed ring: " << *ring << "\n";
                 }
-                const auto end = ring.segments().end();
-                for (auto it = ring.segments().begin() + 1; it != end - 1; ++it) {
-                    if (has_same_location(nr, it->second())) {
-                        split_off_subring(ring, it, ring.segments().begin(), it+1);
-                        return true;
-                    }
-                }
-                return false;
+
+                return nodes;
             }
 
-            bool check_for_closed_subring(ProtoRing& ring) {
+            uint32_t add_new_ring_complex(slocation& node) {
+                detail::NodeRefSegment* segment = &m_segment_list[node.item];
+                assert(!segment->is_done());
+
                 if (debug()) {
-                    std::cerr << "      check_for_closed_subring()\n";
+                    std::cerr << "  Starting new ring at location " << node.location(m_segment_list) << " with segment " << *segment << "\n";
                 }
 
-                osmium::area::detail::ProtoRing::segments_type segments(ring.segments().size());
-                std::copy(ring.segments().cbegin(), ring.segments().cend(), segments.begin());
-                std::sort(segments.begin(), segments.end());
-                const auto it = std::adjacent_find(segments.begin(), segments.end(), [this](const osmium::area::detail::NodeRefSegment& s1, const osmium::area::detail::NodeRefSegment& s2) {
-                    return has_same_location(s1.first(), s2.first());
-                });
-                if (it == segments.end()) {
-                    return false;
+                if (node.reverse) {
+                    segment->reverse();
+                }
+
+                m_rings.emplace_back(segment);
+                detail::ProtoRing* ring = &m_rings.back();
+
+                const osmium::Location& first_location = node.location(m_segment_list);
+                osmium::Location last_location = segment->stop().location();
+
+                uint32_t nodes = 1;
+                while (first_location != last_location && !is_split_location(last_location)) {
+                    ++nodes;
+                    detail::NodeRefSegment* next_segment = get_next_segment(last_location);
+                    if (next_segment->start().location() != last_location) {
+                        next_segment->reverse();
+                    }
+                    ring->add_segment_back(next_segment);
+                    if (debug()) {
+                        std::cerr << "    Next segment is " << *next_segment << "\n";
+                    }
+                    last_location = next_segment->stop().location();
                 }
-                const auto r1 = std::find_first_of(ring.segments().begin(), ring.segments().end(), it, it+1);
-                assert(r1 != ring.segments().end());
-                const auto r2 = std::find_first_of(ring.segments().begin(), ring.segments().end(), it+1, it+2);
-                assert(r2 != ring.segments().end());
 
                 if (debug()) {
-                    std::cerr << "      found subring in ring " << ring << " at " << it->first() << "\n";
+                    if (first_location == last_location) {
+                        std::cerr << "    Completed ring: " << *ring << "\n";
+                    } else {
+                        std::cerr << "    Completed partial ring: " << *ring << "\n";
+                    }
                 }
 
-                const auto m = std::minmax(r1, r2);
+                return nodes;
+            }
 
-                ProtoRing new_ring(m.first, m.second);
-                ring.remove_segments(m.first, m.second);
+            void create_locations_list() {
+                m_locations.reserve(m_segment_list.size() * 2);
 
-                if (debug()) {
-                    std::cerr << "        split ring1=" << new_ring << "\n";
-                    std::cerr << "        split ring2=" << ring << "\n";
+                for (uint32_t n = 0; n < m_segment_list.size(); ++n) {
+                    m_locations.emplace_back(n, false);
+                    m_locations.emplace_back(n, true);
                 }
 
-                m_rings.emplace_back(new_ring);
+                std::stable_sort(m_locations.begin(), m_locations.end(), [this](const slocation& a, const slocation& b) {
+                    return a.location(m_segment_list) < b.location(m_segment_list);
+                });
+            }
 
-                return true;
+            void find_inner_outer_complex(detail::ProtoRing* ring) {
+                detail::ProtoRing* outer_ring = find_enclosing_ring(ring->min_segment());
+                if (outer_ring) {
+                    outer_ring->add_inner_ring(ring);
+                    ring->set_outer_ring(outer_ring);
+                }
+                ring->fix_direction();
+                ring->mark_direction_done();
             }
 
-            void combine_rings_front(const osmium::area::detail::NodeRefSegment& segment, ProtoRing& ring) {
+            void find_inner_outer_complex() {
                 if (debug()) {
-                    std::cerr << " => match at front of ring\n";
+                    std::cerr << "  Finding inner/outer rings\n";
+                }
+                std::vector<detail::ProtoRing*> rings;
+                rings.reserve(m_rings.size());
+                for (auto& ring : m_rings) {
+                    if (ring.closed()) {
+                        rings.push_back(&ring);
+                    }
                 }
-                ring.add_segment_front(segment);
-                has_closed_subring_front(ring, segment.first());
-                if (possibly_combine_rings_front(ring)) {
-                    check_for_closed_subring(ring);
+
+                if (rings.empty()) {
+                    return;
                 }
-            }
 
-            void combine_rings_back(const osmium::area::detail::NodeRefSegment& segment, ProtoRing& ring) {
+                std::sort(rings.begin(), rings.end(), [](detail::ProtoRing* a, detail::ProtoRing* b) {
+                    return a->min_segment() < b->min_segment();
+                });
+
+                rings.front()->fix_direction();
+                rings.front()->mark_direction_done();
                 if (debug()) {
-                    std::cerr << " => match at back of ring\n";
+                    std::cerr << "    First ring is outer: " << *rings.front() << "\n";
                 }
-                ring.add_segment_back(segment);
-                has_closed_subring_back(ring, segment.second());
-                if (possibly_combine_rings_back(ring)) {
-                    check_for_closed_subring(ring);
+                for (auto it = std::next(rings.begin()); it != rings.end(); ++it) {
+                    if (debug()) {
+                        std::cerr << "    Checking (at min segment " << *((*it)->min_segment()) << ") ring " << **it << "\n";
+                    }
+                    find_inner_outer_complex(*it);
+                    if (debug()) {
+                        std::cerr << "    Ring is " << ((*it)->is_outer() ? "OUTER: " : "INNER: ") << **it << "\n";
+                    }
                 }
             }
 
             /**
-             * Append each outer ring together with its inner rings to the
-             * area in the buffer.
+             * Finds all locations where more than two segments meet. If there
+             * are any open rings found along the way, they are reported
+             * and the function returns false.
              */
-            void add_rings_to_area(osmium::builder::AreaBuilder& builder) const {
-                for (const ProtoRing* ring : m_outer_rings) {
-                    if (debug()) {
-                        std::cerr << "    ring " << *ring << " is outer\n";
-                    }
-                    {
-                        osmium::builder::OuterRingBuilder ring_builder(builder.buffer(), &builder);
-                        ring_builder.add_node_ref(ring->get_node_ref_front());
-                        for (const auto& segment : ring->segments()) {
-                            ring_builder.add_node_ref(segment.second());
+            bool find_split_locations() {
+                osmium::Location previous_location;
+                for (auto it = m_locations.cbegin(); it != m_locations.cend(); ++it) {
+                    const osmium::NodeRef& nr = it->node_ref(m_segment_list);
+                    const osmium::Location& loc = nr.location();
+                    if (std::next(it) == m_locations.cend() || loc != std::next(it)->location(m_segment_list)) {
+                        if (debug()) {
+                            std::cerr << "  Found open ring at " << nr << "\n";
+                        }
+                        if (m_config.problem_reporter) {
+                            const auto& segment = m_segment_list[it->item];
+                            m_config.problem_reporter->report_ring_not_closed(nr, segment.way());
+                        }
+                        ++m_stats.open_rings;
+                    } else {
+                        if (loc == previous_location && (m_split_locations.empty() || m_split_locations.back() != previous_location )) {
+                            m_split_locations.push_back(previous_location);
+                        }
+                        ++it;
+                        if (it == m_locations.end()) {
+                            break;
                         }
                     }
-                    for (ProtoRing* inner : ring->inner_rings()) {
-                        osmium::builder::InnerRingBuilder ring_builder(builder.buffer(), &builder);
-                        ring_builder.add_node_ref(inner->get_node_ref_front());
-                        for (const auto& segment : inner->segments()) {
-                            ring_builder.add_node_ref(segment.second());
+                    previous_location = loc;
+                }
+                return m_stats.open_rings == 0;
+            }
+
+            void create_rings_simple_case() {
+                uint32_t count_remaining = m_segment_list.size();
+                for (slocation& sl : m_locations) {
+                    const detail::NodeRefSegment& segment = m_segment_list[sl.item];
+                    if (!segment.is_done()) {
+                        count_remaining -= add_new_ring(sl);
+                        if (count_remaining == 0) {
+                            return;
                         }
                     }
                 }
             }
 
-            bool add_to_existing_ring(osmium::area::detail::NodeRefSegment segment) {
-                int n = 0;
-                for (auto& ring : m_rings) {
+            std::vector<location_to_ring_map> create_location_to_ring_map(open_ring_its_type& open_ring_its) {
+                std::vector<location_to_ring_map> xrings;
+                xrings.reserve(open_ring_its.size() * 2);
+
+                for (auto it = open_ring_its.begin(); it != open_ring_its.end(); ++it) {
                     if (debug()) {
-                        std::cerr << "    check against ring " << n << " " << ring;
+                        std::cerr << "      Ring: " << **it << "\n";
                     }
-                    if (ring.closed()) {
-                        if (debug()) {
-                            std::cerr << " => ring CLOSED\n";
-                        }
-                    } else {
-                        if (has_same_location(ring.get_node_ref_back(), segment.first())) {
-                            combine_rings_back(segment, ring);
-                            return true;
-                        }
-                        if (has_same_location(ring.get_node_ref_back(), segment.second())) {
-                            segment.swap_locations();
-                            combine_rings_back(segment, ring);
-                            return true;
-                        }
-                        if (has_same_location(ring.get_node_ref_front(), segment.first())) {
-                            segment.swap_locations();
-                            combine_rings_front(segment, ring);
-                            return true;
-                        }
-                        if (has_same_location(ring.get_node_ref_front(), segment.second())) {
-                            combine_rings_front(segment, ring);
-                            return true;
-                        }
+                    xrings.emplace_back((*it)->get_node_ref_start().location(), it, true);
+                    xrings.emplace_back((*it)->get_node_ref_stop().location(), it, false);
+                }
+
+                std::sort(xrings.begin(), xrings.end());
+
+                return xrings;
+            }
+
+            void merge_two_rings(open_ring_its_type& open_ring_its, const location_to_ring_map& m1, const location_to_ring_map& m2) {
+                auto& r1 = *m1.ring_it;
+                auto& r2 = *m2.ring_it;
+
+                if (r1->get_node_ref_stop().location() == r2->get_node_ref_start().location()) {
+                    r1->join_forward(*r2);
+                } else if (r1->get_node_ref_stop().location() == r2->get_node_ref_stop().location()) {
+                    r1->join_backward(*r2);
+                } else if (r1->get_node_ref_start().location() == r2->get_node_ref_start().location()) {
+                    r1->reverse();
+                    r1->join_forward(*r2);
+                } else if (r1->get_node_ref_start().location() == r2->get_node_ref_stop().location()) {
+                    r1->reverse();
+                    r1->join_backward(*r2);
+                } else {
+                    assert(false);
+                }
+
+                m_rings.erase(r2);
+                open_ring_its.remove(r2);
+
+                if (r1->closed()) {
+                    open_ring_its.remove(r1);
+                }
+            }
+
+            bool try_to_merge(open_ring_its_type& open_ring_its) {
+                if (open_ring_its.empty()) {
+                    return false;
+                }
+
+                if (debug()) {
+                    std::cerr << "    Trying to merge " << open_ring_its.size() << " open rings\n";
+                }
+
+                std::vector<location_to_ring_map> xrings = create_location_to_ring_map(open_ring_its);
+
+                auto it = xrings.cbegin();
+                while (it != xrings.cend()) {
+                    it = std::adjacent_find(it, xrings.cend());
+                    if (it == xrings.cend()) {
+                        return false;
+                    }
+                    auto after = std::next(it, 2);
+                    if (after == xrings.cend() || after->location != it->location) {
                         if (debug()) {
-                            std::cerr << " => no match\n";
+                            std::cerr << "      Merging two rings\n";
                         }
+                        merge_two_rings(open_ring_its, *it, *std::next(it));
+                        return true;
+                    }
+                    while (it != xrings.cend() && it->location == after->location) {
+                        ++it;
                     }
-
-                    ++n;
                 }
+
                 return false;
             }
 
-            void check_inner_outer(ProtoRing& ring) {
-                const osmium::NodeRef& min_node = ring.min_node();
+            bool there_are_open_rings() const noexcept {
+                return std::any_of(m_rings.cbegin(), m_rings.cend(), [](const detail::ProtoRing& ring){
+                    return !ring.closed();
+                });
+            }
+
+            struct candidate {
+                int64_t sum;
+                std::vector<std::pair<location_to_ring_map, bool>> rings;
+                osmium::Location start_location;
+                osmium::Location stop_location;
+
+                explicit candidate(location_to_ring_map& ring, bool reverse) :
+                    sum(ring.ring().sum()),
+                    rings(),
+                    start_location(ring.ring().get_node_ref_start().location()),
+                    stop_location(ring.ring().get_node_ref_stop().location()) {
+                    rings.emplace_back(ring, reverse);
+                }
+
+                bool closed() const noexcept {
+                    return start_location == stop_location;
+                }
+
+            };
+
+            void find_candidates(std::vector<candidate>& candidates, std::unordered_set<osmium::Location>& loc_done, const std::vector<location_to_ring_map>& xrings, candidate& cand) {
                 if (debug()) {
-                    std::cerr << "    check_inner_outer min_node=" << min_node << "\n";
+                    std::cerr << "      find_candidates sum=" << cand.sum << " start=" << cand.start_location << " stop=" << cand.stop_location << "\n";
+                    for (const auto& ring : cand.rings) {
+                        std::cerr << "        " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n";
+                    }
                 }
 
-                int count = 0;
-                int above = 0;
+                const auto connections = make_range(std::equal_range(xrings.cbegin(),
+                                                                     xrings.cend(),
+                                                                     location_to_ring_map{cand.stop_location}));
 
-                for (auto it = m_segment_list.begin(); it != m_segment_list.end() && it->first().location().x() <= min_node.location().x(); ++it) {
-                    if (!ring.contains(*it)) {
+                assert(connections.begin() != connections.end());
+
+                assert(!cand.rings.empty());
+                const detail::ProtoRing* ring_leading_here = &cand.rings.back().first.ring();
+                for (const location_to_ring_map& m : connections) {
+                    const detail::ProtoRing& ring = m.ring();
+
+                    if (&ring != ring_leading_here) {
                         if (debug()) {
-                            std::cerr << "      segments for count: " << *it;
+                            std::cerr << "        next possible connection: " << ring << (m.start ? "" : " reverse") << "\n";
                         }
-                        if (it->to_left_of(min_node.location())) {
-                            ++count;
+
+                        candidate c = cand;
+                        if (m.start) {
+                            c.rings.emplace_back(m, false);
+                            c.stop_location = ring.get_node_ref_stop().location();
+                            c.sum += ring.sum();
+                        } else {
+                            c.rings.emplace_back(m, true);
+                            c.stop_location = ring.get_node_ref_start().location();
+                            c.sum -= ring.sum();
+                        }
+                        if (c.closed()) {
                             if (debug()) {
-                                std::cerr << " counted\n";
+                                std::cerr << "          found candidate\n";
                             }
-                        } else {
+                            candidates.push_back(c);
+                        } else if (loc_done.count(c.stop_location) == 0) {
                             if (debug()) {
-                                std::cerr << " not counted\n";
+                                std::cerr << "          recurse...\n";
                             }
-                        }
-                        if (it->first().location() == min_node.location()) {
-                            if (it->second().location().y() > min_node.location().y()) {
-                                ++above;
+                            loc_done.insert(c.stop_location);
+                            find_candidates(candidates, loc_done, xrings, c);
+                            if (debug()) {
+                                std::cerr << "          ...back\n";
                             }
+                        } else if (debug()) {
+                            std::cerr << "          loop found\n";
                         }
-                        if (it->second().location() == min_node.location()) {
-                            if (it->first().location().y() > min_node.location().y()) {
-                                ++above;
+                    }
+                }
+            }
+
+            /**
+             * If there are multiple open rings and mltiple ways to join them,
+             * this function is called. It will take the first open ring and
+             * try recursively all ways of closing it. Of all the candidates
+             * the one with the smallest area is chosen and closed. If it
+             * can't close this ring, an error is reported and the function
+             * returns false.
+             */
+            bool join_connected_rings(open_ring_its_type& open_ring_its) {
+                assert(!open_ring_its.empty());
+
+                if (debug()) {
+                    std::cerr << "    Trying to merge " << open_ring_its.size() << " open rings\n";
+                }
+
+                std::vector<location_to_ring_map> xrings = create_location_to_ring_map(open_ring_its);
+
+                auto ring_min = std::min_element(xrings.begin(), xrings.end(), [](const location_to_ring_map& a, const location_to_ring_map& b) {
+                    return a.ring().min_segment() < b.ring().min_segment();
+                });
+
+                find_inner_outer_complex();
+                detail::ProtoRing* outer_ring = find_enclosing_ring(ring_min->ring().min_segment());
+                bool ring_min_is_outer = !outer_ring;
+                if (debug()) {
+                    std::cerr << "  Open ring is " << (ring_min_is_outer ? "outer" : "inner") << " ring\n";
+                }
+                for (auto& ring : m_rings) {
+                    ring.reset();
+                }
+
+                candidate cand{*ring_min, false};
+
+                // Locations we have visited while finding candidates, used
+                // to detect loops.
+                std::unordered_set<osmium::Location> loc_done;
+
+                loc_done.insert(cand.stop_location);
+
+                std::vector<candidate> candidates;
+                find_candidates(candidates, loc_done, xrings, cand);
+
+                if (candidates.empty()) {
+                    if (debug()) {
+                        std::cerr << "    Found no candidates\n";
+                    }
+                    if (!open_ring_its.empty()) {
+                        ++m_stats.open_rings;
+                        if (m_config.problem_reporter) {
+                            for (auto& it : open_ring_its) {
+                                m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_start());
+                                m_config.problem_reporter->report_ring_not_closed(it->get_node_ref_stop());
                             }
                         }
                     }
+                    return false;
                 }
 
                 if (debug()) {
-                    std::cerr << "      count=" << count << " above=" << above << "\n";
+                    std::cerr << "    Found candidates:\n";
+                    for (const auto& cand : candidates) {
+                        std::cerr << "      sum=" << cand.sum << "\n";
+                        for (const auto& ring : cand.rings) {
+                            std::cerr << "        " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n";
+                        }
+                    }
                 }
 
-                count += above % 2;
+                // Find the candidate with the smallest/largest area
+                auto chosen_cand = ring_min_is_outer ?
+                     std::min_element(candidates.cbegin(), candidates.cend(), [](const candidate& a, const candidate& b) {
+                        return std::abs(a.sum) < std::abs(b.sum);
+                     }) :
+                     std::max_element(candidates.cbegin(), candidates.cend(), [](const candidate& a, const candidate& b) {
+                        return std::abs(a.sum) < std::abs(b.sum);
+                     });
 
-                if (count % 2) {
-                    ring.set_inner();
+                if (debug()) {
+                    std::cerr << "    Decided on: sum=" << chosen_cand->sum << "\n";
+                    for (const auto& ring : chosen_cand->rings) {
+                        std::cerr << "        " << ring.first.ring() << (ring.second ? " reverse" : "") << "\n";
+                    }
+                }
+
+                // Join all (open) rings in the candidate to get one closed ring.
+                assert(chosen_cand->rings.size() > 1);
+                const auto& first_ring = chosen_cand->rings.front().first;
+                for (auto it = chosen_cand->rings.begin() + 1; it != chosen_cand->rings.end(); ++it) {
+                    merge_two_rings(open_ring_its, first_ring, it->first);
                 }
-            }
 
-            void check_inner_outer_roles() {
                 if (debug()) {
-                    std::cerr << "    check_inner_outer_roles\n";
+                    std::cerr << "    Merged to " << first_ring.ring() << "\n";
                 }
 
-                for (const auto ringptr : m_outer_rings) {
-                    for (const auto& segment : ringptr->segments()) {
-                        if (!segment.role_outer()) {
-                            ++m_inner_outer_mismatches;
-                            if (debug()) {
-                                std::cerr << "      segment " << segment << " from way " << segment.way()->id() << " should have role 'outer'\n";
+                return true;
+            }
+
+            bool create_rings_complex_case() {
+                // First create all the (partial) rings starting at the split locations
+                uint32_t count_remaining = m_segment_list.size();
+                for (const osmium::Location& location : m_split_locations) {
+                    const auto locs = make_range(std::equal_range(m_locations.begin(),
+                                                                  m_locations.end(),
+                                                                  slocation{},
+                                                                  [this, &location](const slocation& a, const slocation& b) {
+                        return a.location(m_segment_list, location) < b.location(m_segment_list, location);
+                    }));
+                    for (auto& loc : locs) {
+                        if (!m_segment_list[loc.item].is_done()) {
+                            count_remaining -= add_new_ring_complex(loc);
+                            if (count_remaining == 0) {
+                                break;
                             }
-                            if (m_config.problem_reporter) {
-                                m_config.problem_reporter->report_role_should_be_outer(segment.way()->id(), segment.first().location(), segment.second().location());
+                        }
+                    }
+                }
+
+                // Now find all the rest of the rings (ie not starting at split locations)
+                if (count_remaining > 0) {
+                    for (slocation& sl : m_locations) {
+                        const detail::NodeRefSegment& segment = m_segment_list[sl.item];
+                        if (!segment.is_done()) {
+                            count_remaining -= add_new_ring_complex(sl);
+                            if (count_remaining == 0) {
+                                break;
                             }
                         }
                     }
                 }
-                for (const auto ringptr : m_inner_rings) {
-                    for (const auto& segment : ringptr->segments()) {
-                        if (!segment.role_inner()) {
-                            ++m_inner_outer_mismatches;
+
+                // Now all segments are in exactly one (partial) ring.
+
+                // If there are open rings, try to join them to create closed
+                // rings.
+                if (there_are_open_rings()) {
+                    ++m_stats.area_really_complex_case;
+
+                    open_ring_its_type open_ring_its;
+                    for (auto it = m_rings.begin(); it != m_rings.end(); ++it) {
+                        if (!it->closed()) {
+                            open_ring_its.push_back(it);
+                        }
+                    }
+
+                    while (!open_ring_its.empty()) {
+                        if (debug()) {
+                            std::cerr << "  There are " << open_ring_its.size() << " open rings\n";
+                        }
+                        while (try_to_merge(open_ring_its));
+
+                        if (!open_ring_its.empty()) {
                             if (debug()) {
-                                std::cerr << "      segment " << segment << " from way " << segment.way()->id() << " should have role 'inner'\n";
+                                std::cerr << "  After joining obvious cases there are still " << open_ring_its.size() << " open rings\n";
                             }
-                            if (m_config.problem_reporter) {
-                                m_config.problem_reporter->report_role_should_be_inner(segment.way()->id(), segment.first().location(), segment.second().location());
+                            if (!join_connected_rings(open_ring_its)) {
+                                return false;
                             }
                         }
                     }
+
+                    if (debug()) {
+                        std::cerr << "  Joined all open rings\n";
+                    }
                 }
+
+                // Now all rings are complete.
+
+                find_inner_outer_complex();
+
+                return true;
             }
 
             /**
              * Create rings from segments.
              */
             bool create_rings() {
+                m_stats.nodes += m_segment_list.size();
+
+                // Sort the list of segments (from left to right and bottom
+                // to top).
+                osmium::Timer timer_sort;
                 m_segment_list.sort();
-                m_segment_list.erase_duplicate_segments();
+                timer_sort.stop();
+
+                // Remove duplicate segments. Removal is in pairs, so if there
+                // are two identical segments, they will both be removed. If
+                // there are three, two will be removed and one remains.
+                osmium::Timer timer_dupl;
+                m_stats.duplicate_segments = m_segment_list.erase_duplicate_segments(m_config.problem_reporter);
+                timer_dupl.stop();
+
+                // If there are no segments left at this point, this isn't
+                // a valid area.
+                if (m_segment_list.empty()) {
+                    if (debug()) {
+                        std::cerr << "  No segments left\n";
+                    }
+                    return false;
+                }
+
+                if (m_config.debug_level >= 3) {
+                    std::cerr << "Sorted de-duplicated segment list:\n";
+                    for (const auto& s : m_segment_list) {
+                        std::cerr << "  " << s << "\n";
+                    }
+                }
 
                 // Now we look for segments crossing each other. If there are
                 // any, the multipolygon is invalid.
                 // In the future this could be improved by trying to fix those
                 // cases.
-                if (m_segment_list.find_intersections(m_config.problem_reporter)) {
+                osmium::Timer timer_intersection;
+                m_stats.intersections = m_segment_list.find_intersections(m_config.problem_reporter);
+                timer_intersection.stop();
+
+                if (m_stats.intersections) {
                     return false;
                 }
 
-                // Now iterator over all segments and add them to rings. Each segment
-                // is tacked on to either end of an existing ring if possible, or a
-                // new ring is started with it.
-                for (const auto& segment : m_segment_list) {
+                // This creates an ordered list of locations of both endpoints
+                // of all segments with pointers back to the segments. We will
+                // use this list later to quickly find which segment(s) fits
+                // onto a known segment.
+                osmium::Timer timer_locations_list;
+                create_locations_list();
+                timer_locations_list.stop();
+
+                // Find all locations where more than two segments start or
+                // end. We call those "split" locations. If there are any
+                // "spike" segments found while doing this, we know the area
+                // geometry isn't valid and return.
+                osmium::Timer timer_split;
+                if (!find_split_locations()) {
+                    return false;
+                }
+                timer_split.stop();
+
+                // Now report all split locations to the problem reporter.
+                m_stats.touching_rings += m_split_locations.size();
+                if (!m_split_locations.empty()) {
                     if (debug()) {
-                        std::cerr << "  checking segment " << segment << "\n";
+                        std::cerr << "  Found split locations:\n";
                     }
-                    if (!add_to_existing_ring(segment)) {
+                    for (const auto& location : m_split_locations) {
+                        if (m_config.problem_reporter) {
+                            auto it = std::lower_bound(m_locations.cbegin(), m_locations.cend(), slocation{}, [this, &location](const slocation& a, const slocation& b) {
+                                return a.location(m_segment_list, location) < b.location(m_segment_list, location);
+                            });
+                            assert(it != m_locations.cend());
+                            const osmium::object_id_type id = it->node_ref(m_segment_list).ref();
+                            m_config.problem_reporter->report_touching_ring(id, location);
+                        }
                         if (debug()) {
-                            std::cerr << "    new ring for segment " << segment << "\n";
+                            std::cerr << "    " << location << "\n";
                         }
-                        m_rings.emplace_back(segment);
                     }
                 }
 
-                if (debug()) {
-                    std::cerr << "  Rings:\n";
-                    for (const auto& ring : m_rings) {
-                        std::cerr << "    " << ring;
-                        if (ring.closed()) {
-                            std::cerr << " (closed)";
-                        }
-                        std::cerr << "\n";
+                // From here on we use two different algorithms depending on
+                // whether there were any split locations or not. If there
+                // are no splits, we use the faster "simple algorithm", if
+                // there are, we use the slower "complex algorithm".
+                osmium::Timer timer_simple_case;
+                osmium::Timer timer_complex_case;
+                if (m_split_locations.empty()) {
+                    if (debug()) {
+                        std::cerr << "  No split locations -> using simple algorithm\n";
                     }
-                }
+                    ++m_stats.area_simple_case;
 
-                if (check_for_open_rings()) {
+                    timer_simple_case.start();
+                    create_rings_simple_case();
+                    timer_simple_case.stop();
+                } else {
                     if (debug()) {
-                        std::cerr << "  not all rings are closed\n";
+                        std::cerr << "  Found split locations -> using complex algorithm\n";
                     }
-                    return false;
+                    ++m_stats.area_touching_rings_case;
+
+                    timer_complex_case.start();
+                    if (!create_rings_complex_case()) {
+                        return false;
+                    }
+                    timer_complex_case.stop();
                 }
 
-                if (debug()) {
-                    std::cerr << "  Find inner/outer...\n";
+                // If the assembler was so configured, now check whether the
+                // member roles are correctly tagged.
+                if (m_config.check_roles && m_stats.from_relations) {
+                    osmium::Timer timer_roles;
+                    check_inner_outer_roles();
+                    timer_roles.stop();
                 }
 
-                if (m_rings.size() == 1) {
-                    m_outer_rings.push_back(&m_rings.front());
+                m_stats.outer_rings = std::count_if(m_rings.cbegin(), m_rings.cend(), [](const detail::ProtoRing& ring){
+                    return ring.is_outer();
+                });
+                m_stats.inner_rings = m_rings.size() - m_stats.outer_rings;
+
+#ifdef OSMIUM_WITH_TIMER
+                std::cout << m_stats.nodes << ' ' << m_stats.outer_rings << ' ' << m_stats.inner_rings <<
+                                              ' ' << timer_sort.elapsed_microseconds() <<
+                                              ' ' << timer_dupl.elapsed_microseconds() <<
+                                              ' ' << timer_intersection.elapsed_microseconds() <<
+                                              ' ' << timer_locations_list.elapsed_microseconds() <<
+                                              ' ' << timer_split.elapsed_microseconds();
+
+                if (m_split_locations.empty()) {
+                    std::cout << ' ' << timer_simple_case.elapsed_microseconds() <<
+                                 " 0";
                 } else {
-                    for (auto& ring : m_rings) {
-                        check_inner_outer(ring);
-                        if (ring.outer()) {
-                            if (!ring.is_cw()) {
-                                ring.reverse();
-                            }
-                            m_outer_rings.push_back(&ring);
-                        } else {
-                            if (ring.is_cw()) {
-                                ring.reverse();
-                            }
-                            m_inner_rings.push_back(&ring);
-                        }
-                    }
-
-                    if (m_outer_rings.size() == 1) {
-                        for (auto inner : m_inner_rings) {
-                            m_outer_rings.front()->add_inner_ring(inner);
-                        }
-                    } else {
-                        // sort outer rings by size, smallest first
-                        std::sort(m_outer_rings.begin(), m_outer_rings.end(), [](ProtoRing* a, ProtoRing* b) {
-                            return a->area() < b->area();
-                        });
-                        for (auto inner : m_inner_rings) {
-                            for (auto outer : m_outer_rings) {
-                                if (inner->is_in(outer)) {
-                                    outer->add_inner_ring(inner);
-                                    break;
-                                }
-                            }
-                        }
-                    }
+                    std::cout << " 0" <<
+                                 ' ' << timer_complex_case.elapsed_microseconds();
                 }
 
-                check_inner_outer_roles();
+                std::cout <<
+# ifdef OSMIUM_AREA_CHECK_INNER_OUTER_ROLES
+                             ' ' << timer_roles.elapsed_microseconds() <<
+# else
+                             " 0" <<
+# endif
+                             '\n';
+#endif
+
+                return true;
+            }
 
+#ifdef OSMIUM_WITH_TIMER
+            static bool print_header() {
+                std::cout << "nodes outer_rings inner_rings sort dupl intersection locations split simple_case complex_case roles_check\n";
                 return true;
             }
 
+            static bool init_header() {
+                static bool printed_print_header = print_header();
+                return printed_print_header;
+            }
+#endif
+
+            bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Way& way) {
+                osmium::builder::AreaBuilder builder(out_buffer);
+                builder.initialize_from_object(way);
+
+                const bool area_okay = create_rings();
+                if (area_okay || m_config.create_empty_areas) {
+                    add_tags_to_area(builder, way);
+                }
+                if (area_okay) {
+                    add_rings_to_area(builder);
+                }
+
+                if (report_ways()) {
+                    m_config.problem_reporter->report_way(way);
+                }
+
+                return area_okay || m_config.create_empty_areas;
+            }
+
+            bool create_area(osmium::memory::Buffer& out_buffer, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) {
+                osmium::builder::AreaBuilder builder(out_buffer);
+                builder.initialize_from_object(relation);
+
+                const bool area_okay = create_rings();
+                if (area_okay || m_config.create_empty_areas) {
+                    add_tags_to_area(builder, relation);
+                }
+                if (area_okay) {
+                    add_rings_to_area(builder);
+                }
+
+                if (report_ways()) {
+                    for (const osmium::Way* way : members) {
+                        m_config.problem_reporter->report_way(*way);
+                    }
+                }
+
+                return area_okay || m_config.create_empty_areas;
+            }
+
         public:
 
-            typedef osmium::area::AssemblerConfig config_type;
+            using config_type = osmium::area::AssemblerConfig;
 
             explicit Assembler(const config_type& config) :
                 m_config(config),
-                m_segment_list(config.debug) {
+                m_segment_list(config.debug_level > 1) {
+#ifdef OSMIUM_WITH_TIMER
+                init_header();
+#endif
             }
 
-            ~Assembler() = default;
+            ~Assembler() noexcept = default;
 
             /**
              * Assemble an area from the given way.
              * The resulting area is put into the out_buffer.
              */
             void operator()(const osmium::Way& way, osmium::memory::Buffer& out_buffer) {
+                if (!m_config.create_way_polygons) {
+                    return;
+                }
+
+                if (way.tags().has_tag("area", "no")) {
+                    return;
+                }
+
                 if (m_config.problem_reporter) {
                     m_config.problem_reporter->set_object(osmium::item_type::way, way.id());
+                    m_config.problem_reporter->set_nodes(way.nodes().size());
+                }
+
+                // Ignore (but count) ways without segments.
+                if (way.nodes().size() < 2) {
+                    ++m_stats.short_ways;
+                    return;
                 }
 
                 if (!way.ends_have_same_id()) {
+                    ++m_stats.duplicate_nodes;
                     if (m_config.problem_reporter) {
                         m_config.problem_reporter->report_duplicate_node(way.nodes().front().ref(), way.nodes().back().ref(), way.nodes().front().location());
                     }
                 }
 
-                m_segment_list.extract_segments_from_way(way, "outer");
+                ++m_stats.from_ways;
+                m_stats.duplicate_nodes += m_segment_list.extract_segments_from_way(m_config.problem_reporter, way);
 
-                if (debug()) {
-                    std::cerr << "\nBuild way id()=" << way.id() << " segments.size()=" << m_segment_list.size() << "\n";
+                if (m_config.debug_level > 0) {
+                    std::cerr << "\nAssembling way " << way.id() << " containing " << m_segment_list.size() << " nodes\n";
                 }
 
                 // Now create the Area object and add the attributes and tags
                 // from the way.
-                {
-                    osmium::builder::AreaBuilder builder(out_buffer);
-                    builder.initialize_from_object(way);
+                if (create_area(out_buffer, way)) {
+                    out_buffer.commit();
+                } else {
+                    out_buffer.rollback();
+                }
 
-                    if (create_rings()) {
-                        add_tags_to_area(builder, way);
-                        add_rings_to_area(builder);
-                    }
+                if (debug()) {
+                    std::cerr << "Done: " << m_stats << "\n";
                 }
-                out_buffer.commit();
             }
 
             /**
@@ -714,32 +1440,62 @@ namespace osmium {
              * All members are to be found in the in_buffer at the offsets
              * given by the members parameter.
              * The resulting area is put into the out_buffer.
+             *
+             * @deprecated
+             * This function is deprecated. Use the other form of the function
+             * instead.
+             */
+            OSMIUM_DEPRECATED void operator()(const osmium::Relation& relation, const std::vector<size_t>& members, const osmium::memory::Buffer& in_buffer, osmium::memory::Buffer& out_buffer) {
+                std::vector<const osmium::Way*> ways;
+                for (size_t offset : members) {
+                    const osmium::Way& way = in_buffer.get<const osmium::Way>(offset);
+                    ways.push_back(&way);
+                }
+                operator()(relation, ways, out_buffer);
+            }
+
+            /**
+             * Assemble an area from the given relation and its members.
+             * The resulting area is put into the out_buffer.
              */
-            void operator()(const osmium::Relation& relation, const std::vector<size_t>& members, const osmium::memory::Buffer& in_buffer, osmium::memory::Buffer& out_buffer) {
+            void operator()(const osmium::Relation& relation, const std::vector<const osmium::Way*>& members, osmium::memory::Buffer& out_buffer) {
+                assert(relation.members().size() >= members.size());
+
                 if (m_config.problem_reporter) {
                     m_config.problem_reporter->set_object(osmium::item_type::relation, relation.id());
                 }
 
-                m_segment_list.extract_segments_from_ways(relation, members, in_buffer);
+                if (relation.members().empty()) {
+                    ++m_stats.no_way_in_mp_relation;
+                    return;
+                }
 
-                if (debug()) {
-                    std::cerr << "\nBuild relation id()=" << relation.id() << " members.size()=" << members.size() << " segments.size()=" << m_segment_list.size() << "\n";
+                ++m_stats.from_relations;
+                m_stats.duplicate_nodes += m_segment_list.extract_segments_from_ways(m_config.problem_reporter, relation, members);
+                m_stats.member_ways = members.size();
+
+                if (m_stats.member_ways == 1) {
+                    ++m_stats.single_way_in_mp_relation;
+                }
+
+                if (m_config.debug_level > 0) {
+                    std::cerr << "\nAssembling relation " << relation.id() << " containing " << members.size() << " way members with " << m_segment_list.size() << " nodes\n";
                 }
 
-                size_t area_offset = out_buffer.committed();
+                const size_t area_offset = out_buffer.committed();
 
                 // Now create the Area object and add the attributes and tags
                 // from the relation.
-                {
-                    osmium::builder::AreaBuilder builder(out_buffer);
-                    builder.initialize_from_object(relation);
-
-                    if (create_rings()) {
-                        add_tags_to_area(builder, relation);
-                        add_rings_to_area(builder);
+                if (create_area(out_buffer, relation, members)) {
+                    if ((m_config.create_new_style_polygons && m_stats.no_tags_on_relation == 0) ||
+                        (m_config.create_old_style_polygons && m_stats.no_tags_on_relation != 0)) {
+                        out_buffer.commit();
+                    } else {
+                        out_buffer.rollback();
                     }
+                } else {
+                    out_buffer.rollback();
                 }
-                out_buffer.commit();
 
                 const osmium::TagList& area_tags = out_buffer.get<osmium::Area>(area_offset).tags(); // tags of the area we just built
 
@@ -748,27 +1504,33 @@ namespace osmium {
                 // just built, add them to a list and later build areas for
                 // them, too.
                 std::vector<const osmium::Way*> ways_that_should_be_areas;
-                if (m_inner_outer_mismatches == 0) {
-                    auto memit = relation.members().begin();
-                    for (size_t offset : members) {
-                        if (!std::strcmp(memit->role(), "inner")) {
-                            const osmium::Way& way = in_buffer.get<const osmium::Way>(offset);
+                if (m_stats.wrong_role == 0) {
+                    detail::for_each_member(relation, members, [this, &ways_that_should_be_areas, &area_tags](const osmium::RelationMember& member, const osmium::Way& way) {
+                        if (!std::strcmp(member.role(), "inner")) {
                             if (!way.nodes().empty() && way.is_closed() && way.tags().size() > 0) {
-                                auto d = std::count_if(way.tags().begin(), way.tags().end(), filter());
+                                const auto d = std::count_if(way.tags().cbegin(), way.tags().cend(), filter());
                                 if (d > 0) {
-                                    osmium::tags::KeyFilter::iterator way_fi_begin(filter(), way.tags().begin(), way.tags().end());
-                                    osmium::tags::KeyFilter::iterator way_fi_end(filter(), way.tags().end(), way.tags().end());
-                                    osmium::tags::KeyFilter::iterator area_fi_begin(filter(), area_tags.begin(), area_tags.end());
-                                    osmium::tags::KeyFilter::iterator area_fi_end(filter(), area_tags.end(), area_tags.end());
+                                    osmium::tags::KeyFilter::iterator way_fi_begin(filter(), way.tags().cbegin(), way.tags().cend());
+                                    osmium::tags::KeyFilter::iterator way_fi_end(filter(), way.tags().cend(), way.tags().cend());
+                                    osmium::tags::KeyFilter::iterator area_fi_begin(filter(), area_tags.cbegin(), area_tags.cend());
+                                    osmium::tags::KeyFilter::iterator area_fi_end(filter(), area_tags.cend(), area_tags.cend());
 
                                     if (!std::equal(way_fi_begin, way_fi_end, area_fi_begin) || d != std::distance(area_fi_begin, area_fi_end)) {
                                         ways_that_should_be_areas.push_back(&way);
+                                    } else {
+                                        ++m_stats.inner_with_same_tags;
+                                        if (m_config.problem_reporter) {
+                                            m_config.problem_reporter->report_inner_with_same_tags(way);
+                                        }
                                     }
                                 }
                             }
                         }
-                        ++memit;
-                    }
+                    });
+                }
+
+                if (debug()) {
+                    std::cerr << "Done: " << m_stats << "\n";
                 }
 
                 // Now build areas for all ways found in the last step.
@@ -778,6 +1540,14 @@ namespace osmium {
                 }
             }
 
+            /**
+             * Get statistics from assembler. Call this after running the
+             * assembler to get statistics and data about errors.
+             */
+            const osmium::area::area_stats& stats() const noexcept {
+                return m_stats;
+            }
+
         }; // class Assembler
 
     } // namespace area
diff --git a/include/osmium/area/detail/node_ref_segment.hpp b/include/osmium/area/detail/node_ref_segment.hpp
index 7c1555a..bb97520 100644
--- a/include/osmium/area/detail/node_ref_segment.hpp
+++ b/include/osmium/area/detail/node_ref_segment.hpp
@@ -34,11 +34,13 @@ DEALINGS IN THE SOFTWARE.
 */
 
 #include <algorithm>
+#include <cassert>
 #include <cstdint>
 #include <cstring>
 #include <iosfwd>
 #include <utility>
 
+#include <osmium/area/detail/vector.hpp>
 #include <osmium/osm/location.hpp>
 #include <osmium/osm/node_ref.hpp>
 
@@ -53,108 +55,178 @@ namespace osmium {
          */
         namespace detail {
 
+            class ProtoRing;
+
+            enum class role_type : uint8_t {
+                unknown = 0,
+                outer   = 1,
+                inner   = 2,
+                empty   = 3
+            };
+
             /**
-             * This helper class for the Assembler class models a segment.
-             * Segments are the connection between
-             * two nodes and they all have their smaller coordinate at the
-             * beginning of the segment. Smaller, in this case, means smaller x
-             * coordinate, and if they are the same smaller y coordinate.
+             * This helper class for the Assembler class models a segment,
+             * the connection between two nodes.
+             *
+             * Internally segments have their smaller coordinate at the
+             * beginning of the segment. Smaller, in this case, means smaller
+             * x coordinate, and, if they are the same, smaller y coordinate.
              */
             class NodeRefSegment {
 
+                // First node in order described above.
                 osmium::NodeRef m_first;
-                osmium::NodeRef m_second;
 
-                /// Role of the member this segment was from.
-                const char* m_role;
+                // Second node in order described above.
+                osmium::NodeRef m_second;
 
-                /// Way this segment was from.
+                // Way this segment was from.
                 const osmium::Way* m_way;
 
-            public:
+                // The ring this segment is part of. Initially nullptr, this
+                // will be filled in once we know which ring the segment is in.
+                ProtoRing* m_ring;
 
-                void swap_locations() {
-                    using std::swap;
-                    swap(m_first, m_second);
-                }
+                // The role of this segment from the member role.
+                role_type m_role;
+
+                // Nodes have to be reversed to get the intended order.
+                bool m_reverse = false;
 
-                explicit NodeRefSegment() noexcept :
+                // We found the right direction for this segment in the ring.
+                // (This depends on whether it is an inner or outer ring.)
+                bool m_direction_done = false;
+
+            public:
+
+                NodeRefSegment() noexcept :
                     m_first(),
                     m_second(),
-                    m_role(nullptr),
-                    m_way(nullptr) {
+                    m_way(nullptr),
+                    m_ring(nullptr),
+                    m_role(role_type::unknown) {
                 }
 
-                explicit NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, const char* role, const osmium::Way* way) :
+                NodeRefSegment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2, role_type role = role_type::unknown, const osmium::Way* way = nullptr) noexcept :
                     m_first(nr1),
                     m_second(nr2),
-                    m_role(role),
-                    m_way(way) {
+                    m_way(way),
+                    m_ring(nullptr),
+                    m_role(role) {
                     if (nr2.location() < nr1.location()) {
-                        swap_locations();
+                        using std::swap;
+                        swap(m_first, m_second);
                     }
                 }
 
-                NodeRefSegment(const NodeRefSegment&) = default;
-                NodeRefSegment(NodeRefSegment&&) = default;
+                /**
+                 * The ring this segment is a part of. nullptr if we don't
+                 * have the ring yet.
+                 */
+                ProtoRing* ring() const noexcept {
+                    return m_ring;
+                }
 
-                NodeRefSegment& operator=(const NodeRefSegment&) = default;
-                NodeRefSegment& operator=(NodeRefSegment&&) = default;
+                /**
+                 * Returns true if the segment has already been placed in a
+                 * ring.
+                 */
+                bool is_done() const noexcept {
+                    return m_ring != nullptr;
+                }
 
-                ~NodeRefSegment() = default;
+                void set_ring(ProtoRing* ring) noexcept {
+                    assert(ring);
+                    m_ring = ring;
+                }
 
-                /// Return first NodeRef of Segment according to sorting order (bottom left to top right).
+                bool is_reverse() const noexcept {
+                    return m_reverse;
+                }
+
+                void reverse() noexcept {
+                    m_reverse = !m_reverse;
+                }
+
+                bool is_direction_done() const noexcept {
+                    return m_direction_done;
+                }
+
+                void mark_direction_done() noexcept {
+                    m_direction_done = true;
+                }
+
+                void mark_direction_not_done() noexcept {
+                    m_direction_done = false;
+                }
+
+                /**
+                 * Return first NodeRef of Segment according to sorting
+                 * order (bottom left to top right).
+                 */
                 const osmium::NodeRef& first() const noexcept {
                     return m_first;
                 }
 
-                /// Return second NodeRef of Segment according to sorting order (bottom left to top right).
+                /**
+                 * Return second NodeRef of Segment according to sorting
+                 * order (bottom left to top right).
+                 */
                 const osmium::NodeRef& second() const noexcept {
                     return m_second;
                 }
 
-                bool to_left_of(const osmium::Location& location) const {
-    //                std::cerr << "segment " << first() << "--" << second() << " to_left_of(" << location << "\n";
-
-                    if (first().location() == location || second().location() == location) {
-                        return false;
-                    }
-
-                    const std::pair<osmium::Location, osmium::Location> mm = std::minmax(first().location(), second().location(), [](const osmium::Location a, const osmium::Location b) {
-                        return a.y() < b.y();
-                    });
-
-                    if (mm.first.y() >= location.y() || mm.second.y() < location.y() || first().location().x() > location.x()) {
-    //                    std::cerr << "  false\n";
-                        return false;
-                    }
+                /**
+                 * Return real first NodeRef of Segment.
+                 */
+                const osmium::NodeRef& start() const noexcept {
+                    return m_reverse ? m_second : m_first;
+                }
 
-                    int64_t ax = mm.first.x();
-                    int64_t bx = mm.second.x();
-                    int64_t lx = location.x();
-                    int64_t ay = mm.first.y();
-                    int64_t by = mm.second.y();
-                    int64_t ly = location.y();
-                    return ((bx - ax)*(ly - ay) - (by - ay)*(lx - ax)) <= 0;
+                /**
+                 * Return real second NodeRef of Segment.
+                 */
+                const osmium::NodeRef& stop() const noexcept {
+                    return m_reverse ? m_first : m_second;
                 }
 
                 bool role_outer() const noexcept {
-                    return !strcmp(m_role, "outer");
+                    return m_role == role_type::outer;
                 }
 
                 bool role_inner() const noexcept {
-                    return !strcmp(m_role, "inner");
+                    return m_role == role_type::inner;
+                }
+
+                bool role_empty() const noexcept {
+                    return m_role == role_type::empty;
+                }
+
+                const char* role_name() const noexcept {
+                    static const char* names[] = { "unknown", "outer", "inner", "empty" };
+                    return names[int(m_role)];
                 }
 
                 const osmium::Way* way() const noexcept {
                     return m_way;
                 }
 
+                /**
+                 * The "determinant" of this segment. Used for calculating
+                 * the winding order or a ring.
+                 */
+                int64_t det() const noexcept {
+                    const vec a{start()};
+                    const vec b{stop()};
+                    return a * b;
+                }
+
             }; // class NodeRefSegment
 
             /// NodeRefSegments are equal if both their locations are equal
             inline bool operator==(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
-                return lhs.first().location() == rhs.first().location() && lhs.second().location() == rhs.second().location();
+                return lhs.first().location() == rhs.first().location() &&
+                       lhs.second().location() == rhs.second().location();
             }
 
             inline bool operator!=(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
@@ -162,12 +234,33 @@ namespace osmium {
             }
 
             /**
-             * NodeRefSegments are "smaller" if they are to the left and down of another
-             * segment. The first() location is checked first() and only if they have the
-             * same first() location the second() location is taken into account.
+             * A NodeRefSegment is "smaller" if the first point is to the
+             * left and down of the first point of the second segment.
+             * If both first points are the same, the segment with the higher
+             * slope comes first. If the slope is the same, the shorter
+             * segment comes first.
              */
             inline bool operator<(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
-                return (lhs.first().location() == rhs.first().location() && lhs.second().location() < rhs.second().location()) || lhs.first().location() < rhs.first().location();
+                if (lhs.first().location() == rhs.first().location()) {
+                    vec p0{lhs.first().location()};
+                    vec p1{lhs.second().location()};
+                    vec q0{rhs.first().location()};
+                    vec q1{rhs.second().location()};
+                    vec p = p1 - p0;
+                    vec q = q1 - q0;
+
+                    if (p.x == 0 && q.x == 0) {
+                        return p.y < q.y;
+                    }
+
+                    auto a = p.y * q.x;
+                    auto b = q.y * p.x;
+                    if (a == b) {
+                        return p.x < q.x;
+                    }
+                    return a > b;
+                }
+                return lhs.first().location() < rhs.first().location();
             }
 
             inline bool operator>(const NodeRefSegment& lhs, const NodeRefSegment& rhs) noexcept {
@@ -184,7 +277,10 @@ namespace osmium {
 
             template <typename TChar, typename TTraits>
             inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const NodeRefSegment& segment) {
-                return out << segment.first() << "--" << segment.second();
+                return out << segment.start() << "--" << segment.stop()
+                           << "[" << (segment.is_reverse() ? 'R' : '_')
+                                  << (segment.is_done()    ? 'd' : '_')
+                                  << (segment.is_direction_done() ? 'D' : '_') << "]";
             }
 
             inline bool outside_x_range(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
@@ -194,7 +290,7 @@ namespace osmium {
                 return false;
             }
 
-            inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) {
+            inline bool y_range_overlap(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
                 const std::pair<int32_t, int32_t> m1 = std::minmax(s1.first().location().y(), s1.second().location().y());
                 const std::pair<int32_t, int32_t> m2 = std::minmax(s2.first().location().y(), s2.second().location().y());
                 if (m1.first > m2.second || m2.first > m1.second) {
@@ -210,55 +306,94 @@ namespace osmium {
              * might be slightly different than the numerically correct
              * location.
              *
-             * This function uses integer arithmentic as much as possible and
+             * This function uses integer arithmetic as much as possible and
              * will not work if the segments are longer than about half the
              * planet. This shouldn't happen with real data, so it isn't a big
              * problem.
              *
-             * If the segments touch in one of their endpoints, it doesn't
-             * count as an intersection.
+             * If the segments touch in one or both of their endpoints, it
+             * doesn't count as an intersection.
              *
              * If the segments intersect not in a single point but in multiple
-             * points, ie if they overlap, this is NOT detected.
+             * points, ie if they are collinear and overlap, the smallest
+             * of the endpoints that is in the overlapping section is returned.
              *
              * @returns Undefined osmium::Location if there is no intersection
              *          or a defined Location if the segments intersect.
              */
-            inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) {
-                if (s1.first().location()  == s2.first().location()  ||
-                    s1.first().location()  == s2.second().location() ||
-                    s1.second().location() == s2.first().location()  ||
-                    s1.second().location() == s2.second().location()) {
+            inline osmium::Location calculate_intersection(const NodeRefSegment& s1, const NodeRefSegment& s2) noexcept {
+                // See http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect
+                // for some hints about how the algorithm works.
+                const vec p0{s1.first()};
+                const vec p1{s1.second()};
+                const vec q0{s2.first()};
+                const vec q1{s2.second()};
+
+                if ((p0 == q0 && p1 == q1) ||
+                    (p0 == q1 && p1 == q0)) {
+                    // segments are the same
                     return osmium::Location();
                 }
 
-                int64_t s1ax = s1.first().x();
-                int64_t s1ay = s1.first().y();
-                int64_t s1bx = s1.second().x();
-                int64_t s1by = s1.second().y();
-                int64_t s2ax = s2.first().x();
-                int64_t s2ay = s2.first().y();
-                int64_t s2bx = s2.second().x();
-                int64_t s2by = s2.second().y();
-
-                int64_t d = (s2by - s2ay) * (s1bx - s1ax) -
-                            (s2bx - s2ax) * (s1by - s1ay);
+                const vec pd = p1 - p0;
+                const int64_t d = pd * (q1 - q0);
 
                 if (d != 0) {
-                    int64_t na = (s2bx - s2ax) * (s1ay - s2ay) -
-                                 (s2by - s2ay) * (s1ax - s2ax);
+                    // segments are not collinear
+
+                    if (p0 == q0 || p0 == q1 || p1 == q0 || p1 == q1) {
+                        // touching at an end point
+                        return osmium::Location();
+                    }
+
+                    // intersection in a point
 
-                    int64_t nb = (s1bx - s1ax) * (s1ay - s2ay) -
-                                 (s1by - s1ay) * (s1ax - s2ax);
+                    const int64_t na = (q1.x - q0.x) * (p0.y - q0.y) -
+                                       (q1.y - q0.y) * (p0.x - q0.x);
+
+                    const int64_t nb = (p1.x - p0.x) * (p0.y - q0.y) -
+                                       (p1.y - p0.y) * (p0.x - q0.x);
 
                     if ((d > 0 && na >= 0 && na <= d && nb >= 0 && nb <= d) ||
                         (d < 0 && na <= 0 && na >= d && nb <= 0 && nb >= d)) {
+                        const double ua = double(na) / d;
+                        const vec i = p0 + ua * (p1 - p0);
+                        return osmium::Location(int32_t(i.x), int32_t(i.y));
+                    }
+
+                    return osmium::Location();
+                }
 
-                        double ua = double(na) / d;
-                        int32_t ix = int32_t(s1ax + ua*(s1bx - s1ax));
-                        int32_t iy = int32_t(s1ay + ua*(s1by - s1ay));
+                // segments are collinear
+
+                if (pd * (q0 - p0) == 0) {
+                    // segments are on the same line
+
+                    struct seg_loc {
+                        int segment;
+                        osmium::Location location;
+                    };
+
+                    seg_loc sl[4];
+                    sl[0] = {0, s1.first().location() };
+                    sl[1] = {0, s1.second().location()};
+                    sl[2] = {1, s2.first().location() };
+                    sl[3] = {1, s2.second().location()};
+
+                    std::sort(sl, sl+4, [](const seg_loc& a, const seg_loc& b) {
+                        return a.location < b.location;
+                    });
+
+                    if (sl[1].location == sl[2].location) {
+                        return osmium::Location();
+                    }
 
-                        return osmium::Location(ix, iy);
+                    if (sl[0].segment != sl[1].segment) {
+                        if (sl[0].location == sl[1].location) {
+                            return sl[2].location;
+                        } else {
+                            return sl[1].location;
+                        }
                     }
                 }
 
diff --git a/include/osmium/area/detail/proto_ring.hpp b/include/osmium/area/detail/proto_ring.hpp
index c4edf40..c71a5d3 100644
--- a/include/osmium/area/detail/proto_ring.hpp
+++ b/include/osmium/area/detail/proto_ring.hpp
@@ -58,214 +58,155 @@ namespace osmium {
 
             public:
 
-                typedef std::vector<NodeRefSegment> segments_type;
+                using segments_type = std::vector<NodeRefSegment*>;
 
             private:
 
-                // segments in this ring
+                // Segments in this ring.
                 segments_type m_segments;
 
-                bool m_outer {true};
-
-                // if this is an outer ring, these point to it's inner rings (if any)
+                // If this is an outer ring, these point to it's inner rings
+                // (if any).
                 std::vector<ProtoRing*> m_inner;
 
-            public:
-
-                explicit ProtoRing(const NodeRefSegment& segment) noexcept :
-                    m_segments() {
-                    add_segment_back(segment);
-                }
-
-                explicit ProtoRing(segments_type::const_iterator sbegin, segments_type::const_iterator send) :
-                    m_segments(static_cast<size_t>(std::distance(sbegin, send))) {
-                    std::copy(sbegin, send, m_segments.begin());
-                }
-
-                bool outer() const noexcept {
-                    return m_outer;
-                }
+                // The smallest segment. Will be kept current whenever a new
+                // segment is added to the ring.
+                NodeRefSegment* m_min_segment;
 
-                void set_inner() noexcept {
-                    m_outer = false;
-                }
+                // If this is an inner ring, points to the outer ring.
+                ProtoRing* m_outer_ring;
 
-                segments_type& segments() noexcept {
-                    return m_segments;
-                }
+                int64_t m_sum;
 
-                const segments_type& segments() const noexcept {
-                    return m_segments;
-                }
-
-                void remove_segments(segments_type::iterator sbegin, segments_type::iterator send) {
-                    m_segments.erase(sbegin, send);
-                }
+            public:
 
-                void add_segment_front(const NodeRefSegment& segment) {
-                    m_segments.insert(m_segments.begin(), segment);
+                explicit ProtoRing(NodeRefSegment* segment) noexcept :
+                    m_segments(),
+                    m_inner(),
+                    m_min_segment(segment),
+                    m_outer_ring(nullptr),
+                    m_sum(0) {
+                    add_segment_back(segment);
                 }
 
-                void add_segment_back(const NodeRefSegment& segment) {
+                void add_segment_back(NodeRefSegment* segment) {
+                    assert(segment);
+                    if (*segment < *m_min_segment) {
+                        m_min_segment = segment;
+                    }
                     m_segments.push_back(segment);
+                    segment->set_ring(this);
+                    m_sum += segment->det();
                 }
 
-                const NodeRefSegment& get_segment_front() const {
-                    return m_segments.front();
+                NodeRefSegment* min_segment() const noexcept {
+                    return m_min_segment;
                 }
 
-                NodeRefSegment& get_segment_front() {
-                    return m_segments.front();
+                ProtoRing* outer_ring() const noexcept {
+                    return m_outer_ring;
                 }
 
-                const NodeRef& get_node_ref_front() const {
-                    return get_segment_front().first();
+                void set_outer_ring(ProtoRing* outer_ring) noexcept {
+                    assert(outer_ring);
+                    assert(m_inner.empty());
+                    m_outer_ring = outer_ring;
                 }
 
-                const NodeRefSegment& get_segment_back() const {
-                    return m_segments.back();
+                const std::vector<ProtoRing*>& inner_rings() const noexcept {
+                    return m_inner;
                 }
 
-                NodeRefSegment& get_segment_back() {
-                    return m_segments.back();
+                void add_inner_ring(ProtoRing* ring) {
+                    assert(ring);
+                    assert(!m_outer_ring);
+                    m_inner.push_back(ring);
                 }
 
-                const NodeRef& get_node_ref_back() const {
-                    return get_segment_back().second();
+                bool is_outer() const noexcept {
+                    return !m_outer_ring;
                 }
 
-                bool closed() const {
-                    return m_segments.front().first().location() == m_segments.back().second().location();
+                const segments_type& segments() const noexcept {
+                    return m_segments;
                 }
 
-                int64_t sum() const {
-                    int64_t sum = 0;
-
-                    for (const auto& segment : m_segments) {
-                        sum += static_cast<int64_t>(segment.first().location().x()) * static_cast<int64_t>(segment.second().location().y()) -
-                               static_cast<int64_t>(segment.second().location().x()) * static_cast<int64_t>(segment.first().location().y());
-                    }
+                const NodeRef& get_node_ref_start() const noexcept {
+                    return m_segments.front()->start();
+                }
 
-                    return sum;
+                const NodeRef& get_node_ref_stop() const noexcept {
+                    return m_segments.back()->stop();
                 }
 
-                bool is_cw() const {
-                    return sum() <= 0;
+                bool closed() const noexcept {
+                    return get_node_ref_start().location() == get_node_ref_stop().location();
                 }
 
-                int64_t area() const {
-                    return std::abs(sum()) / 2;
+                void reverse() {
+                    std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
+                        segment->reverse();
+                    });
+                    std::reverse(m_segments.begin(), m_segments.end());
+                    m_sum = -m_sum;
                 }
 
-                void swap_segments(ProtoRing& other) {
-                    using std::swap;
-                    swap(m_segments, other.m_segments);
+                void mark_direction_done() {
+                    std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
+                        segment->mark_direction_done();
+                    });
                 }
 
-                void add_inner_ring(ProtoRing* ring) {
-                    m_inner.push_back(ring);
+                bool is_cw() const noexcept {
+                    return m_sum <= 0;
                 }
 
-                const std::vector<ProtoRing*>& inner_rings() const {
-                    return m_inner;
+                int64_t sum() const noexcept {
+                    return m_sum;
                 }
 
-                void print(std::ostream& out) const {
-                    out << "[";
-                    bool first = true;
-                    for (const auto& segment : m_segments) {
-                        if (first) {
-                            out << segment.first().ref();
-                        }
-                        out << ',' << segment.second().ref();
-                        first = false;
+                void fix_direction() noexcept {
+                    if (is_cw() == is_outer()) {
+                        reverse();
                     }
-                    out << "]";
                 }
 
-                void reverse() {
-                    std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment& segment) {
-                        segment.swap_locations();
+                void reset() {
+                    m_inner.clear();
+                    m_outer_ring = nullptr;
+                    std::for_each(m_segments.begin(), m_segments.end(), [](NodeRefSegment* segment) {
+                        segment->mark_direction_not_done();
                     });
-                    std::reverse(m_segments.begin(), m_segments.end());
                 }
 
-                /**
-                 * Merge other ring to end of this ring.
-                 */
-                void merge_ring(const ProtoRing& other, bool debug) {
-                    if (debug) {
-                        std::cerr << "        MERGE rings ";
-                        print(std::cerr);
-                        std::cerr << " to ";
-                        other.print(std::cerr);
-                        std::cerr << "\n";
-                    }
-                    m_segments.insert(m_segments.end(), other.m_segments.begin(), other.m_segments.end());
-                    if (debug) {
-                        std::cerr << "          result ring: ";
-                        print(std::cerr);
-                        std::cerr << "\n";
+                void get_ways(std::set<const osmium::Way*>& ways) const {
+                    for (const auto& segment : m_segments) {
+                        ways.insert(segment->way());
                     }
                 }
 
-                void merge_ring_reverse(const ProtoRing& other, bool debug) {
-                    if (debug) {
-                        std::cerr << "        MERGE rings (reverse) ";
-                        print(std::cerr);
-                        std::cerr << " to ";
-                        other.print(std::cerr);
-                        std::cerr << "\n";
-                    }
-                    size_t n = m_segments.size();
-                    m_segments.resize(n + other.m_segments.size());
-                    std::transform(other.m_segments.rbegin(), other.m_segments.rend(), m_segments.begin() + static_cast<segments_type::difference_type>(n), [](NodeRefSegment segment) {
-                        segment.swap_locations();
-                        return segment;
-                    });
-                    if (debug) {
-                        std::cerr << "          result ring: ";
-                        print(std::cerr);
-                        std::cerr << "\n";
+                void join_forward(ProtoRing& other) {
+                    for (NodeRefSegment* segment : other.m_segments) {
+                        add_segment_back(segment);
                     }
                 }
 
-                const NodeRef& min_node() const {
-                    auto it = std::min_element(m_segments.begin(), m_segments.end());
-                    if (location_less()(it->first(), it->second())) {
-                        return it->first();
-                    } else {
-                        return it->second();
+                void join_backward(ProtoRing& other) {
+                    for (auto it = other.m_segments.rbegin(); it != other.m_segments.rend(); ++it) {
+                        (*it)->reverse();
+                        add_segment_back(*it);
                     }
                 }
 
-                bool is_in(ProtoRing* outer) {
-                    osmium::Location testpoint = segments().front().first().location();
-                    bool is_in = false;
-
-                    for (size_t i = 0, j = outer->segments().size()-1; i < outer->segments().size(); j = i++) {
-                        if (((outer->segments()[i].first().location().y() > testpoint.y()) != (outer->segments()[j].first().location().y() > testpoint.y())) &&
-                            (testpoint.x() < (outer->segments()[j].first().location().x() - outer->segments()[i].first().location().x()) * (testpoint.y() - outer->segments()[i].first().location().y()) / (outer->segments()[j].first().location().y() - outer->segments()[i].first().location().y()) + outer->segments()[i].first().location().x()) ) {
-                            is_in = !is_in;
-                        }
+                void print(std::ostream& out) const {
+                    out << "[";
+                    if (!m_segments.empty()) {
+                        out << m_segments.front()->start().ref();
                     }
-
-                    return is_in;
-                }
-
-                void get_ways(std::set<const osmium::Way*>& ways) {
                     for (const auto& segment : m_segments) {
-                        ways.insert(segment.way());
-                    }
-                }
-
-                bool contains(const NodeRefSegment& segment) const {
-                    for (const auto& s : m_segments) {
-                        if (s == segment || (s.first() == segment.second() && s.second() == segment.first())) {
-                            return true;
-                        }
+                        out << ',' << segment->stop().ref();
                     }
-                    return false;
+                    out << "]-" << (is_outer() ? "OUTER" : "INNER");
                 }
 
             }; // class ProtoRing
diff --git a/include/osmium/area/detail/segment_list.hpp b/include/osmium/area/detail/segment_list.hpp
index 05e0cd8..d7c5927 100644
--- a/include/osmium/area/detail/segment_list.hpp
+++ b/include/osmium/area/detail/segment_list.hpp
@@ -36,6 +36,7 @@ DEALINGS IN THE SOFTWARE.
 #include <algorithm>
 #include <cassert>
 #include <iostream>
+#include <numeric>
 #include <vector>
 
 #include <osmium/area/problem_reporter.hpp>
@@ -53,6 +54,24 @@ namespace osmium {
         namespace detail {
 
             /**
+             * Iterate over all relation members and the vector of ways at the
+             * same time and call given function with the relation member and
+             * way as parameter. This takes into account that there might be
+             * non-way members in the relation.
+             */
+            template <typename F>
+            inline void for_each_member(const osmium::Relation& relation, const std::vector<const osmium::Way*> ways, F&& func) {
+                auto way_it = ways.cbegin();
+                for (const osmium::RelationMember& member : relation.members()) {
+                    if (member.type() == osmium::item_type::way) {
+                        assert(way_it != ways.cend());
+                        func(member, **way_it);
+                        ++way_it;
+                    }
+                }
+            }
+
+            /**
              * This is a helper class for the area assembler. It models
              * a list of segments.
              */
@@ -64,6 +83,51 @@ namespace osmium {
 
                 bool m_debug;
 
+                static role_type parse_role(const char* role) noexcept {
+                    if (role[0] == '\0') {
+                        return role_type::empty;
+                    } else if (!std::strcmp(role, "outer")) {
+                        return role_type::outer;
+                    } else if (!std::strcmp(role, "inner")) {
+                        return role_type::inner;
+                    }
+                    return role_type::unknown;
+                }
+
+                /**
+                 * Calculate the number of segments in all the ways together.
+                 */
+                static size_t get_num_segments(const std::vector<const osmium::Way*>& members) noexcept {
+                    return std::accumulate(members.cbegin(), members.cend(), 0, [](size_t sum, const osmium::Way* way) {
+                        if (way->nodes().empty()) {
+                            return sum;
+                        } else {
+                            return sum + way->nodes().size() - 1;
+                        }
+                    });
+                }
+
+                uint32_t extract_segments_from_way_impl(osmium::area::ProblemReporter* problem_reporter, const osmium::Way& way, role_type role) {
+                    uint32_t duplicate_nodes = 0;
+
+                    osmium::NodeRef previous_nr;
+                    for (const osmium::NodeRef& nr : way.nodes()) {
+                        if (previous_nr.location()) {
+                            if (previous_nr.location() != nr.location()) {
+                                m_segments.emplace_back(previous_nr, nr, role, &way);
+                            } else {
+                                ++duplicate_nodes;
+                                if (problem_reporter) {
+                                    problem_reporter->report_duplicate_node(previous_nr.ref(), nr.ref(), nr.location());
+                                }
+                            }
+                        }
+                        previous_nr = nr;
+                    }
+
+                    return duplicate_nodes;
+                }
+
             public:
 
                 explicit SegmentList(bool debug) noexcept :
@@ -84,12 +148,31 @@ namespace osmium {
                     return m_segments.size();
                 }
 
+                /// Is the segment list empty?
                 bool empty() const noexcept {
                     return m_segments.empty();
                 }
 
-                typedef slist_type::const_iterator const_iterator;
-                typedef slist_type::iterator iterator;
+                using const_iterator = slist_type::const_iterator;
+                using iterator = slist_type::iterator;
+
+                NodeRefSegment& front() {
+                    return m_segments.front();
+                }
+
+                NodeRefSegment& back() {
+                    return m_segments.back();
+                }
+
+                const NodeRefSegment& operator[](size_t n) const noexcept {
+                    assert(n < m_segments.size());
+                    return m_segments[n];
+                }
+
+                NodeRefSegment& operator[](size_t n) noexcept {
+                    assert(n < m_segments.size());
+                    return m_segments[n];
+                }
 
                 iterator begin() noexcept {
                     return m_segments.begin();
@@ -115,11 +198,6 @@ namespace osmium {
                     m_debug = debug;
                 }
 
-                /// Clear the list of segments. All segments are removed.
-                void clear() {
-                    m_segments.clear();
-                }
-
                 /// Sort the list of segments.
                 void sort() {
                     std::sort(m_segments.begin(), m_segments.end());
@@ -128,32 +206,37 @@ namespace osmium {
                 /**
                  * Extract segments from given way and add them to the list.
                  *
-                 * Segments connecting two nodes with the same location (ie same
-                 * node or different node with same location) are removed.
-                 *
-                 * XXX should two nodes with same location be reported?
+                 * Segments connecting two nodes with the same location (ie
+                 * same node or different nodes with same location) are
+                 * removed after reporting the duplicate node.
                  */
-                void extract_segments_from_way(const osmium::Way& way, const char* role) {
-                    osmium::NodeRef last_nr;
-                    for (const osmium::NodeRef& nr : way.nodes()) {
-                        if (last_nr.location() && last_nr.location() != nr.location()) {
-                            m_segments.emplace_back(last_nr, nr, role, &way);
-                        }
-                        last_nr = nr;
+                uint32_t extract_segments_from_way(osmium::area::ProblemReporter* problem_reporter, const osmium::Way& way) {
+                    if (way.nodes().empty()) {
+                        return 0;
                     }
+                    m_segments.reserve(way.nodes().size() - 1);
+                    return extract_segments_from_way_impl(problem_reporter, way, role_type::outer);
                 }
 
                 /**
                  * Extract all segments from all ways that make up this
                  * multipolygon relation and add them to the list.
                  */
-                void extract_segments_from_ways(const osmium::Relation& relation, const std::vector<size_t>& members, const osmium::memory::Buffer& in_buffer) {
-                    auto member_it = relation.members().begin();
-                    for (size_t offset : members) {
-                        const osmium::Way& way = in_buffer.get<const osmium::Way>(offset);
-                        extract_segments_from_way(way, member_it->role());
-                        ++member_it;
+                uint32_t extract_segments_from_ways(osmium::area::ProblemReporter* problem_reporter, const osmium::Relation& relation, const std::vector<const osmium::Way*>& members) {
+                    assert(relation.members().size() >= members.size());
+
+                    size_t num_segments = get_num_segments(members);
+                    if (problem_reporter) {
+                        problem_reporter->set_nodes(num_segments);
                     }
+                    m_segments.reserve(num_segments);
+
+                    uint32_t duplicate_nodes = 0;
+                    for_each_member(relation, members, [this, &problem_reporter, &duplicate_nodes](const osmium::RelationMember& member, const osmium::Way& way) {
+                        duplicate_nodes += extract_segments_from_way_impl(problem_reporter, way, parse_role(member.role()));
+                    });
+
+                    return duplicate_nodes;
                 }
 
                 /**
@@ -162,17 +245,33 @@ namespace osmium {
                  * same segment. So if there are three, for instance, two will
                  * be removed and one will be left.
                  */
-                void erase_duplicate_segments() {
+                uint32_t erase_duplicate_segments(osmium::area::ProblemReporter* problem_reporter) {
+                    uint32_t duplicate_segments = 0;
+
                     while (true) {
                         auto it = std::adjacent_find(m_segments.begin(), m_segments.end());
                         if (it == m_segments.end()) {
-                            return;
+                            break;
                         }
                         if (m_debug) {
                             std::cerr << "  erase duplicate segment: " << *it << "\n";
                         }
+
+                        // Only count and report duplicate segments if they
+                        // belong to the same way. Those cases are definitely
+                        // wrong. If the duplicate segments belong to
+                        // different ways, they could be touching inner rings
+                        // which are perfectly okay.
+                        if (it->way() == std::next(it)->way()) {
+                            ++duplicate_segments;
+                            if (problem_reporter) {
+                                problem_reporter->report_duplicate_segment(it->first(), it->second());
+                            }
+                        }
                         m_segments.erase(it, it+2);
                     }
+
+                    return duplicate_segments;
                 }
 
                 /**
@@ -182,14 +281,14 @@ namespace osmium {
                  *                         reported to this object.
                  * @returns true if there are intersections.
                  */
-                bool find_intersections(osmium::area::ProblemReporter* problem_reporter) const {
+                uint32_t find_intersections(osmium::area::ProblemReporter* problem_reporter) const {
                     if (m_segments.empty()) {
-                        return false;
+                        return 0;
                     }
 
-                    bool found_intersections = false;
+                    uint32_t found_intersections = 0;
 
-                    for (auto it1 = m_segments.begin(); it1 != m_segments.end()-1; ++it1) {
+                    for (auto it1 = m_segments.cbegin(); it1 != m_segments.cend()-1; ++it1) {
                         const NodeRefSegment& s1 = *it1;
                         for (auto it2 = it1+1; it2 != m_segments.end(); ++it2) {
                             const NodeRefSegment& s2 = *it2;
@@ -203,12 +302,13 @@ namespace osmium {
                             if (y_range_overlap(s1, s2)) {
                                 osmium::Location intersection = calculate_intersection(s1, s2);
                                 if (intersection) {
-                                    found_intersections = true;
+                                    ++found_intersections;
                                     if (m_debug) {
                                         std::cerr << "  segments " << s1 << " and " << s2 << " intersecting at " << intersection << "\n";
                                     }
                                     if (problem_reporter) {
-                                        problem_reporter->report_intersection(s1.way()->id(), s1.first().location(), s1.second().location(), s2.way()->id(), s2.first().location(), s2.second().location(), intersection);
+                                        problem_reporter->report_intersection(s1.way()->id(), s1.first().location(), s1.second().location(),
+                                                                              s2.way()->id(), s2.first().location(), s2.second().location(), intersection);
                                     }
                                 }
                             }
diff --git a/include/osmium/area/detail/vector.hpp b/include/osmium/area/detail/vector.hpp
new file mode 100644
index 0000000..283801a
--- /dev/null
+++ b/include/osmium/area/detail/vector.hpp
@@ -0,0 +1,120 @@
+#ifndef OSMIUM_AREA_DETAIL_VECTOR_HPP
+#define OSMIUM_AREA_DETAIL_VECTOR_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include <cstdint>
+
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/node_ref.hpp>
+
+namespace osmium {
+
+    namespace area {
+
+        namespace detail {
+
+            /**
+             * This helper class models a 2D vector in the mathematical sense.
+             * It uses 64 bit integers internally which has enough precision
+             * for most operations with inputs based on 32 bit locations.
+             */
+            struct vec {
+
+                int64_t x;
+                int64_t y;
+
+                constexpr vec(int64_t a, int64_t b) noexcept :
+                    x(a),
+                    y(b) {
+                }
+
+                constexpr explicit vec(const osmium::Location& l) noexcept :
+                    x(l.x()),
+                    y(l.y()) {
+                }
+
+                constexpr explicit vec(const osmium::NodeRef& nr) noexcept :
+                    x(nr.x()),
+                    y(nr.y()) {
+                }
+
+            }; // struct vec
+
+            // addition
+            constexpr inline vec operator+(const vec& a, const vec& b) noexcept {
+                return vec{a.x + b.x, a.y + b.y};
+            }
+
+            // subtraction
+            constexpr inline vec operator-(const vec& a, const vec& b) noexcept {
+                return vec{a.x - b.x, a.y - b.y};
+            }
+
+            // cross product
+            constexpr inline int64_t operator*(const vec& a, const vec& b) noexcept {
+                return a.x * b.y - a.y * b.x;
+            }
+
+            // scale vector
+            constexpr inline vec operator*(double s, const vec& v) noexcept {
+                return vec{int64_t(s * v.x), int64_t(s * v.y)};
+            }
+
+            // scale vector
+            constexpr inline vec operator*(const vec& v, double s) noexcept {
+                return vec{int64_t(s * v.x), int64_t(s * v.y)};
+            }
+
+            // equality
+            constexpr inline bool operator==(const vec& a, const vec& b) noexcept {
+                return a.x == b.x && a.y == b.y;
+            }
+
+            // inequality
+            constexpr inline bool operator!=(const vec& a, const vec& b) noexcept {
+                return !(a == b);
+            }
+
+            template <typename TChar, typename TTraits>
+            inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const vec& v) {
+                return out << '(' << v.x << ',' << v.y << ')';
+            }
+
+        } // namespace detail
+
+    } // namespace area
+
+} // namespace osmium
+
+#endif //  OSMIUM_AREA_DETAIL_VECTOR_HPP
diff --git a/include/osmium/area/multipolygon_collector.hpp b/include/osmium/area/multipolygon_collector.hpp
index 81a28b2..0f5e4d7 100644
--- a/include/osmium/area/multipolygon_collector.hpp
+++ b/include/osmium/area/multipolygon_collector.hpp
@@ -34,11 +34,11 @@ DEALINGS IN THE SOFTWARE.
 */
 
 #include <algorithm>
-#include <cassert>
 #include <cstddef>
 #include <cstring>
 #include <vector>
 
+#include <osmium/area/stats.hpp>
 #include <osmium/memory/buffer.hpp>
 #include <osmium/osm/item_type.hpp>
 #include <osmium/osm/location.hpp>
@@ -74,13 +74,15 @@ namespace osmium {
         template <typename TAssembler>
         class MultipolygonCollector : public osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false> {
 
-            typedef typename osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false> collector_type;
+            using collector_type = osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false>;
 
-            typedef typename TAssembler::config_type assembler_config_type;
+            using assembler_config_type = typename TAssembler::config_type;
             const assembler_config_type m_assembler_config;
 
             osmium::memory::Buffer m_output_buffer;
 
+            osmium::area::area_stats m_stats;
+
             static constexpr size_t initial_output_buffer_size = 1024 * 1024;
             static constexpr size_t max_buffer_size_for_flush = 100 * 1024;
 
@@ -107,6 +109,10 @@ namespace osmium {
                 m_output_buffer(initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes) {
             }
 
+            const osmium::area::area_stats& stats() const noexcept {
+                return m_stats;
+            }
+
             /**
              * We are interested in all relations tagged with type=multipolygon
              * or type=boundary.
@@ -121,7 +127,7 @@ namespace osmium {
                     return false;
                 }
 
-                if ((!strcmp(type, "multipolygon")) || (!strcmp(type, "boundary"))) {
+                if ((!std::strcmp(type, "multipolygon")) || (!std::strcmp(type, "boundary"))) {
                     return true;
                 }
 
@@ -155,6 +161,7 @@ namespace osmium {
                         // way is closed and has enough nodes, build simple multipolygon
                         TAssembler assembler(m_assembler_config);
                         assembler(way, m_output_buffer);
+                        m_stats += assembler.stats();
                         possibly_flush_output_buffer();
                     }
                 } catch (osmium::invalid_location&) {
@@ -164,15 +171,20 @@ namespace osmium {
 
             void complete_relation(osmium::relations::RelationMeta& relation_meta) {
                 const osmium::Relation& relation = this->get_relation(relation_meta);
-                std::vector<size_t> offsets;
+                const osmium::memory::Buffer& buffer = this->members_buffer();
+
+                std::vector<const osmium::Way*> ways;
                 for (const auto& member : relation.members()) {
                     if (member.ref() != 0) {
-                        offsets.push_back(this->get_offset(member.type(), member.ref()));
+                        size_t offset = this->get_offset(member.type(), member.ref());
+                        ways.push_back(&buffer.get<const osmium::Way>(offset));
                     }
                 }
+
                 try {
                     TAssembler assembler(m_assembler_config);
-                    assembler(relation, offsets, this->members_buffer(), m_output_buffer);
+                    assembler(relation, ways, m_output_buffer);
+                    m_stats += assembler.stats();
                     possibly_flush_output_buffer();
                 } catch (osmium::invalid_location&) {
                     // XXX ignore
diff --git a/include/osmium/area/problem_reporter.hpp b/include/osmium/area/problem_reporter.hpp
index 1dde85b..13fb096 100644
--- a/include/osmium/area/problem_reporter.hpp
+++ b/include/osmium/area/problem_reporter.hpp
@@ -39,6 +39,9 @@ DEALINGS IN THE SOFTWARE.
 
 namespace osmium {
 
+    class NodeRef;
+    class Way;
+
     namespace area {
 
         /**
@@ -62,6 +65,9 @@ namespace osmium {
             // ID of the relation/way we are currently working on
             osmium::object_id_type m_object_id;
 
+            // Number of nodes in the area
+            size_t m_nodes;
+
         public:
 
             ProblemReporter() = default;
@@ -79,6 +85,10 @@ namespace osmium {
                 m_object_id = object_id;
             }
 
+            void set_nodes(size_t nodes) noexcept {
+                m_nodes = nodes;
+            }
+
 // Disable "unused-parameter" warning, so that the compiler will not complain.
 // We can't remove the parameter names, because then doxygen will complain.
 #pragma GCC diagnostic push
@@ -87,14 +97,24 @@ namespace osmium {
             /**
              * Report a duplicate node, ie. two nodes with the same location.
              *
-             * @param node_id1       ID of the first node.
-             * @param node_id2       ID of the second node.
-             * @param location       Location of both nodes.
+             * @param node_id1  ID of the first node.
+             * @param node_id2  ID of the second node.
+             * @param location  Location of both nodes.
              */
             virtual void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) {
             }
 
             /**
+             * Report a node/location where rings touch. This is often wrong,
+             * but not necessarily so.
+             *
+             * @param node_id   ID of the node.
+             * @param location  Location of the node.
+             */
+            virtual void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) {
+            }
+
+            /**
              * Report an intersection between two segments.
              *
              * @param way1_id        ID of the first involved way.
@@ -110,20 +130,32 @@ namespace osmium {
             }
 
             /**
+             * Report a duplicate segments. Two or more segments are directly
+             * on top of each other. This can be a problem, if there is a
+             * spike for instance, or it could be okay, if there are touching
+             * inner rings.
+             *
+             * @param nr1  NodeRef of one end of the segment.
+             * @param nr2  NodeRef of the other end of the segment.
+             */
+            virtual void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) {
+            }
+
+            /**
              * Report an open ring.
              *
-             * @param end1           Location of the first open end.
-             * @param end2           Location of the second open end.
+             * @param nr   NodeRef of one end of the ring.
+             * @param way  Optional pointer to way the end node is in.
              */
-            virtual void report_ring_not_closed(osmium::Location end1, osmium::Location end2) {
+            virtual void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) {
             }
 
             /**
              * Report a segment that should have role "outer", but has a different role.
              *
-             * @param way_id         ID of the way this segment is in.
-             * @param seg_start      Start of the segment with the wrong role.
-             * @param seg_end        End of the segment with the wrong role.
+             * @param way_id     ID of the way this segment is in.
+             * @param seg_start  Start of the segment with the wrong role.
+             * @param seg_end    End of the segment with the wrong role.
              */
             virtual void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) {
             }
@@ -138,6 +170,32 @@ namespace osmium {
             virtual void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) {
             }
 
+            /**
+             * Report a way that is in multiple rings.
+             *
+             * @param way The way.
+             */
+            virtual void report_way_in_multiple_rings(const osmium::Way& way) {
+            }
+
+            /**
+             * Report a way with role inner that has the same tags as the
+             * relation or outer ways.
+             *
+             * @param way The way.
+             */
+            virtual void report_inner_with_same_tags(const osmium::Way& way) {
+            }
+
+            /**
+             * In addition to reporting specific problems, this is used to
+             * report all ways belonging to areas having problems.
+             *
+             * @param way The way
+             */
+            virtual void report_way(const osmium::Way& way) {
+            }
+
 #pragma GCC diagnostic pop
 
         }; // class ProblemReporter
diff --git a/include/osmium/area/problem_reporter_exception.hpp b/include/osmium/area/problem_reporter_exception.hpp
index abadd96..8acd181 100644
--- a/include/osmium/area/problem_reporter_exception.hpp
+++ b/include/osmium/area/problem_reporter_exception.hpp
@@ -42,6 +42,8 @@ DEALINGS IN THE SOFTWARE.
 
 namespace osmium {
 
+    class NodeRef;
+
     namespace area {
 
         class ProblemReporterException : public ProblemReporterStream {
@@ -62,6 +64,12 @@ namespace osmium {
                 throw std::runtime_error(m_sstream.str());
             }
 
+            void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
+                m_sstream.str();
+                ProblemReporterStream::report_touching_ring(node_id, location);
+                throw std::runtime_error(m_sstream.str());
+            }
+
             void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
                                      osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
                 m_sstream.str();
@@ -69,9 +77,15 @@ namespace osmium {
                 throw std::runtime_error(m_sstream.str());
             }
 
-            void report_ring_not_closed(osmium::Location end1, osmium::Location end2) override {
+            void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
+                m_sstream.str();
+                ProblemReporterStream::report_duplicate_segment(nr1, nr2);
+                throw std::runtime_error(m_sstream.str());
+            }
+
+            void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
                 m_sstream.str();
-                ProblemReporterStream::report_ring_not_closed(end1, end2);
+                ProblemReporterStream::report_ring_not_closed(nr, way);
                 throw std::runtime_error(m_sstream.str());
             }
 
@@ -87,6 +101,18 @@ namespace osmium {
                 throw std::runtime_error(m_sstream.str());
             }
 
+            void report_way_in_multiple_rings(const osmium::Way& way) override {
+                m_sstream.str();
+                ProblemReporterStream::report_way_in_multiple_rings(way);
+                throw std::runtime_error(m_sstream.str());
+            }
+
+            void report_inner_with_same_tags(const osmium::Way& way) override {
+                m_sstream.str();
+                ProblemReporterStream::report_inner_with_same_tags(way);
+                throw std::runtime_error(m_sstream.str());
+            }
+
         }; // class ProblemReporterException
 
     } // namespace area
diff --git a/include/osmium/area/problem_reporter_ogr.hpp b/include/osmium/area/problem_reporter_ogr.hpp
index d58fe55..b7704a7 100644
--- a/include/osmium/area/problem_reporter_ogr.hpp
+++ b/include/osmium/area/problem_reporter_ogr.hpp
@@ -42,6 +42,7 @@ DEALINGS IN THE SOFTWARE.
  * @attention If you include this file, you'll need to link with `libgdal`.
  */
 
+#include <cstdint>
 #include <memory>
 
 #include <gdalcpp.hpp>
@@ -50,7 +51,10 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/geom/factory.hpp>
 #include <osmium/geom/ogr.hpp>
 #include <osmium/osm/location.hpp>
+#include <osmium/osm/node_ref.hpp>
+#include <osmium/osm/node_ref_list.hpp>
 #include <osmium/osm/types.hpp>
+#include <osmium/osm/way.hpp>
 
 namespace osmium {
 
@@ -66,26 +70,34 @@ namespace osmium {
 
             gdalcpp::Layer m_layer_perror;
             gdalcpp::Layer m_layer_lerror;
+            gdalcpp::Layer m_layer_ways;
+
+            void set_object(gdalcpp::Feature& feature) {
+                const char t[2] = { osmium::item_type_to_char(m_object_type), '\0' };
+                feature.set_field("obj_type", t);
+                feature.set_field("obj_id", int32_t(m_object_id));
+                feature.set_field("nodes", int32_t(m_nodes));
+            }
 
             void write_point(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location location) {
                 gdalcpp::Feature feature(m_layer_perror, m_ogr_factory.create_point(location));
-                feature.set_field("id1", static_cast<double>(id1));
-                feature.set_field("id2", static_cast<double>(id2));
-                feature.set_field("problem_type", problem_type);
+                set_object(feature);
+                feature.set_field("id1", double(id1));
+                feature.set_field("id2", double(id2));
+                feature.set_field("problem", problem_type);
                 feature.add_to_layer();
             }
 
             void write_line(const char* problem_type, osmium::object_id_type id1, osmium::object_id_type id2, osmium::Location loc1, osmium::Location loc2) {
-                std::unique_ptr<OGRPoint> ogr_point1 = m_ogr_factory.create_point(loc1);
-                std::unique_ptr<OGRPoint> ogr_point2 = m_ogr_factory.create_point(loc2);
-                std::unique_ptr<OGRLineString> ogr_linestring = std::unique_ptr<OGRLineString>(new OGRLineString());
-                ogr_linestring->addPoint(ogr_point1.get());
-                ogr_linestring->addPoint(ogr_point2.get());
+                auto ogr_linestring = std::unique_ptr<OGRLineString>{new OGRLineString{}};
+                ogr_linestring->addPoint(loc1.lon(), loc1.lat());
+                ogr_linestring->addPoint(loc2.lon(), loc2.lat());
 
                 gdalcpp::Feature feature(m_layer_lerror, std::move(ogr_linestring));
+                set_object(feature);
                 feature.set_field("id1", static_cast<double>(id1));
                 feature.set_field("id2", static_cast<double>(id2));
-                feature.set_field("problem_type", problem_type);
+                feature.set_field("problem", problem_type);
                 feature.add_to_layer();
             }
 
@@ -93,15 +105,36 @@ namespace osmium {
 
             explicit ProblemReporterOGR(gdalcpp::Dataset& dataset) :
                 m_layer_perror(dataset, "perrors", wkbPoint),
-                m_layer_lerror(dataset, "lerrors", wkbLineString) {
-
-                m_layer_perror.add_field("id1", OFTReal, 10);
-                m_layer_perror.add_field("id2", OFTReal, 10);
-                m_layer_perror.add_field("problem_type", OFTString, 30);
-
-                m_layer_lerror.add_field("id1", OFTReal, 10);
-                m_layer_lerror.add_field("id2", OFTReal, 10);
-                m_layer_lerror.add_field("problem_type", OFTString, 30);
+                m_layer_lerror(dataset, "lerrors", wkbLineString),
+                m_layer_ways(dataset, "ways", wkbLineString) {
+
+                // 64bit integers are not supported in GDAL < 2, so we
+                // are using a workaround here in fields where we expect
+                // node IDs, we use real numbers.
+                m_layer_perror
+                    .add_field("obj_type", OFTString, 1)
+                    .add_field("obj_id", OFTInteger, 10)
+                    .add_field("nodes", OFTInteger, 8)
+                    .add_field("id1", OFTReal, 12, 1)
+                    .add_field("id2", OFTReal, 12, 1)
+                    .add_field("problem", OFTString, 30)
+                ;
+
+                m_layer_lerror
+                    .add_field("obj_type", OFTString, 1)
+                    .add_field("obj_id", OFTInteger, 10)
+                    .add_field("nodes", OFTInteger, 8)
+                    .add_field("id1", OFTReal, 12, 1)
+                    .add_field("id2", OFTReal, 12, 1)
+                    .add_field("problem", OFTString, 30)
+                ;
+
+                m_layer_ways
+                    .add_field("obj_type", OFTString, 1)
+                    .add_field("obj_id", OFTInteger, 10)
+                    .add_field("way_id", OFTInteger, 10)
+                    .add_field("nodes", OFTInteger, 8)
+                ;
             }
 
             ~ProblemReporterOGR() override = default;
@@ -110,24 +143,82 @@ namespace osmium {
                 write_point("duplicate_node", node_id1, node_id2, location);
             }
 
+            void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
+                write_point("touching_ring", node_id, 0, location);
+            }
+
             void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
                                      osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
-                write_point("intersection", m_object_id, 0, intersection);
-                write_line("intersection", m_object_id, way1_id, way1_seg_start, way1_seg_end);
-                write_line("intersection", m_object_id, way2_id, way2_seg_start, way2_seg_end);
+                write_point("intersection", way1_id, way2_id, intersection);
+                write_line("intersection", way1_id, way2_id, way1_seg_start, way1_seg_end);
+                write_line("intersection", way2_id, way1_id, way2_seg_start, way2_seg_end);
             }
 
-            void report_ring_not_closed(osmium::Location end1, osmium::Location end2) override {
-                write_point("ring_not_closed", m_object_id, 0, end1);
-                write_point("ring_not_closed", m_object_id, 0, end2);
+            void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
+                write_line("duplicate_segment", nr1.ref(), nr2.ref(), nr1.location(), nr2.location());
+            }
+
+            void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
+                write_point("ring_not_closed", nr.ref(), way ? way->id() : 0, nr.location());
             }
 
             void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
-                write_line("role_should_be_outer", m_object_id, way_id, seg_start, seg_end);
+                write_line("role_should_be_outer", way_id, 0, seg_start, seg_end);
             }
 
             void report_role_should_be_inner(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
-                write_line("role_should_be_inner", m_object_id, way_id, seg_start, seg_end);
+                write_line("role_should_be_inner", way_id, 0, seg_start, seg_end);
+            }
+
+            void report_way_in_multiple_rings(const osmium::Way& way) override {
+                if (way.nodes().size() < 2) {
+                    return;
+                }
+                try {
+                    gdalcpp::Feature feature(m_layer_lerror, m_ogr_factory.create_linestring(way));
+                    set_object(feature);
+                    feature.set_field("id1", int32_t(way.id()));
+                    feature.set_field("id2", 0);
+                    feature.set_field("problem", "way_in_multiple_rings");
+                    feature.add_to_layer();
+                } catch (osmium::geometry_error& e) {
+                    // XXX
+                }
+            }
+
+            void report_inner_with_same_tags(const osmium::Way& way) override {
+                if (way.nodes().size() < 2) {
+                    return;
+                }
+                try {
+                    gdalcpp::Feature feature(m_layer_lerror, m_ogr_factory.create_linestring(way));
+                    set_object(feature);
+                    feature.set_field("id1", int32_t(way.id()));
+                    feature.set_field("id2", 0);
+                    feature.set_field("problem", "inner_with_same_tags");
+                    feature.add_to_layer();
+                } catch (osmium::geometry_error& e) {
+                    // XXX
+                }
+            }
+
+            void report_way(const osmium::Way& way) override {
+                if (way.nodes().empty()) {
+                    return;
+                }
+                if (way.nodes().size() == 1) {
+                    const auto& first_nr = way.nodes()[0];
+                    write_point("single_node_in_way", way.id(), first_nr.ref(), first_nr.location());
+                    return;
+                }
+                try {
+                    gdalcpp::Feature feature(m_layer_ways, m_ogr_factory.create_linestring(way));
+                    set_object(feature);
+                    feature.set_field("way_id", int32_t(way.id()));
+                    feature.add_to_layer();
+                } catch (osmium::geometry_error& e) {
+                    // XXX
+                }
             }
 
         }; // class ProblemReporterOGR
diff --git a/include/osmium/area/problem_reporter_stream.hpp b/include/osmium/area/problem_reporter_stream.hpp
index ffd94a6..c601867 100644
--- a/include/osmium/area/problem_reporter_stream.hpp
+++ b/include/osmium/area/problem_reporter_stream.hpp
@@ -38,7 +38,9 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/area/problem_reporter.hpp>
 #include <osmium/osm/item_type.hpp>
 #include <osmium/osm/location.hpp>
+#include <osmium/osm/node_ref.hpp>
 #include <osmium/osm/types.hpp>
+#include <osmium/osm/way.hpp>
 
 namespace osmium {
 
@@ -57,7 +59,7 @@ namespace osmium {
             ~ProblemReporterStream() override = default;
 
             void header(const char* msg) {
-                *m_out << "DATA PROBLEM: " << msg << " on " << item_type_to_char(m_object_type) << m_object_id << ": ";
+                *m_out << "DATA PROBLEM: " << msg << " on " << item_type_to_char(m_object_type) << m_object_id << " (with " << m_nodes << " nodes): ";
             }
 
             void report_duplicate_node(osmium::object_id_type node_id1, osmium::object_id_type node_id2, osmium::Location location) override {
@@ -65,6 +67,11 @@ namespace osmium {
                 *m_out << "node_id1=" << node_id1 << " node_id2=" << node_id2 << " location=" << location << "\n";
             }
 
+            void report_touching_ring(osmium::object_id_type node_id, osmium::Location location) override {
+                header("touching ring");
+                *m_out << "node_id=" << node_id << " location=" << location << "\n";
+            }
+
             void report_intersection(osmium::object_id_type way1_id, osmium::Location way1_seg_start, osmium::Location way1_seg_end,
                                      osmium::object_id_type way2_id, osmium::Location way2_seg_start, osmium::Location way2_seg_end, osmium::Location intersection) override {
                 header("intersection");
@@ -72,9 +79,19 @@ namespace osmium {
                        << " way2_id=" << way2_id << " way2_seg_start=" << way2_seg_start << " way2_seg_end=" << way2_seg_end << " intersection=" << intersection << "\n";
             }
 
-            void report_ring_not_closed(osmium::Location end1, osmium::Location end2) override {
+            void report_duplicate_segment(const osmium::NodeRef& nr1, const osmium::NodeRef& nr2) override {
+                header("duplicate segment");
+                *m_out << "node_id1=" << nr1.ref() << " location1=" << nr1.location()
+                       << " node_id2=" << nr2.ref() << " location2=" << nr2.location() << "\n";
+            }
+
+            void report_ring_not_closed(const osmium::NodeRef& nr, const osmium::Way* way = nullptr) override {
                 header("ring not closed");
-                *m_out << "end1=" << end1 << " end2=" << end2 << "\n";
+                *m_out << "node_id=" << nr.ref() << " location=" << nr.location();
+                if (way) {
+                    *m_out << " on way " << way->id();
+                }
+                *m_out << "\n";
             }
 
             void report_role_should_be_outer(osmium::object_id_type way_id, osmium::Location seg_start, osmium::Location seg_end) override {
@@ -87,6 +104,16 @@ namespace osmium {
                 *m_out << "way_id=" << way_id << " seg_start=" << seg_start << " seg_end=" << seg_end << "\n";
             }
 
+            void report_way_in_multiple_rings(const osmium::Way& way) override {
+                header("way in multiple rings");
+                *m_out << "way_id=" << way.id() << '\n';
+            }
+
+            void report_inner_with_same_tags(const osmium::Way& way) override {
+                header("inner way with same tags as relation or outer");
+                *m_out << "way_id=" << way.id() << '\n';
+            }
+
         }; // class ProblemReporterStream
 
     } // namespace area
diff --git a/include/osmium/area/stats.hpp b/include/osmium/area/stats.hpp
new file mode 100644
index 0000000..79cc002
--- /dev/null
+++ b/include/osmium/area/stats.hpp
@@ -0,0 +1,128 @@
+#ifndef OSMIUM_AREA_STATS_HPP
+#define OSMIUM_AREA_STATS_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include <cstdint>
+#include <ostream>
+
+namespace osmium {
+
+    namespace area {
+
+        /**
+         * These statistics are generated by the area assembler code. They
+         * tell the user of the assembler a lot about the objects this area
+         * is made out of, what happened during the assembly, and what errors
+         * there were.
+         */
+        struct area_stats {
+            uint64_t area_really_complex_case = 0; ///< Most difficult case with rings touching in multiple points
+            uint64_t area_simple_case = 0; ///< Simple case, no touching rings
+            uint64_t area_touching_rings_case = 0; ///< More difficult case with touching rings
+            uint64_t duplicate_nodes = 0; ///< Consecutive identical nodes or consecutive nodes with same location
+            uint64_t duplicate_segments = 0; ///< Segments duplicated (going back and forth)
+            uint64_t from_relations = 0; ///< Area created from multipolygon relation
+            uint64_t from_ways = 0; ///< Area created from way
+            uint64_t inner_rings = 0; ///< Number of inner rings
+            uint64_t inner_with_same_tags = 0; ///< Number of inner ways with same tags as area
+            uint64_t intersections = 0; ///< Number of intersections between segments
+            uint64_t member_ways = 0; ///< Number of ways in the area
+            uint64_t no_tags_on_relation = 0; ///< No tags on relation (old-style multipolygon with tags on outer ways)
+            uint64_t no_way_in_mp_relation = 0; ///< Multipolygon relation with no way members
+            uint64_t nodes = 0; ///< Number of nodes in the area
+            uint64_t open_rings = 0; ///< Number of open rings in the area
+            uint64_t outer_rings = 0; ///< Number of outer rings in the area
+            uint64_t short_ways = 0; ///< Number of ways with less than two nodes
+            uint64_t single_way_in_mp_relation = 0; ///< Multipolygon relation containing a single way
+            uint64_t touching_rings = 0; ///< Rings touching in a node
+            uint64_t ways_in_multiple_rings = 0; ///< Different segments of a way ended up in different rings
+            uint64_t wrong_role = 0; ///< Member has wrong role (not "outer", "inner", or empty)
+
+            area_stats& operator+=(const area_stats& other) noexcept {
+                area_really_complex_case += other.area_really_complex_case;
+                area_simple_case += other.area_simple_case;
+                area_touching_rings_case += other.area_touching_rings_case;
+                duplicate_nodes += other.duplicate_nodes;
+                duplicate_segments += other.duplicate_segments;
+                from_relations += other.from_relations;
+                from_ways += other.from_ways;
+                inner_rings += other.inner_rings;
+                inner_with_same_tags += other.inner_with_same_tags;
+                intersections += other.intersections;
+                member_ways += other.member_ways;
+                no_tags_on_relation += other.no_tags_on_relation;
+                no_way_in_mp_relation += other.no_way_in_mp_relation;
+                nodes += other.nodes;
+                open_rings += other.open_rings;
+                outer_rings += other.outer_rings;
+                short_ways += other.short_ways;
+                single_way_in_mp_relation += other.single_way_in_mp_relation;
+                touching_rings += other.touching_rings;
+                ways_in_multiple_rings += other.ways_in_multiple_rings;
+                wrong_role += other.wrong_role;
+                return *this;
+            }
+
+        }; // struct area_stats
+
+        template <typename TChar, typename TTraits>
+        inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const area_stats& s) {
+            return out << " area_really_complex_case=" << s.area_really_complex_case
+                       << " area_simple_case=" << s.area_simple_case
+                       << " area_touching_rings_case=" << s.area_touching_rings_case
+                       << " duplicate_nodes=" << s.duplicate_nodes
+                       << " duplicate_segments=" << s.duplicate_segments
+                       << " from_relations=" << s.from_relations
+                       << " from_ways=" << s.from_ways
+                       << " inner_rings=" << s.inner_rings
+                       << " inner_with_same_tags=" << s.inner_with_same_tags
+                       << " intersections=" << s.intersections
+                       << " member_ways=" << s.member_ways
+                       << " no_tags_on_relation=" << s.no_tags_on_relation
+                       << " no_way_in_mp_relation=" << s.no_way_in_mp_relation
+                       << " nodes=" << s.nodes
+                       << " open_rings=" << s.open_rings
+                       << " outer_rings=" << s.outer_rings
+                       << " short_ways=" << s.short_ways
+                       << " single_way_in_mp_relation=" << s.single_way_in_mp_relation
+                       << " touching_rings=" << s.touching_rings
+                       << " ways_in_multiple_rings=" << s.ways_in_multiple_rings
+                       << " wrong_role=" << s.wrong_role;
+        }
+
+    } // namespace area
+
+} // namespace osmium
+
+#endif // OSMIUM_AREA_STATS_HPP
diff --git a/include/osmium/builder/osm_object_builder.hpp b/include/osmium/builder/osm_object_builder.hpp
index d4b54f8..2ab0e7f 100644
--- a/include/osmium/builder/osm_object_builder.hpp
+++ b/include/osmium/builder/osm_object_builder.hpp
@@ -186,9 +186,9 @@ namespace osmium {
 
         }; // class NodeRefListBuilder
 
-        typedef NodeRefListBuilder<WayNodeList> WayNodeListBuilder;
-        typedef NodeRefListBuilder<OuterRing> OuterRingBuilder;
-        typedef NodeRefListBuilder<InnerRing> InnerRingBuilder;
+        using WayNodeListBuilder = NodeRefListBuilder<WayNodeList>;
+        using OuterRingBuilder   = NodeRefListBuilder<OuterRing>;
+        using InnerRingBuilder   = NodeRefListBuilder<InnerRing>;
 
         class RelationMemberListBuilder : public ObjectBuilder<RelationMemberList> {
 
@@ -353,8 +353,8 @@ namespace osmium {
 
         }; // class OSMObjectBuilder
 
-        typedef OSMObjectBuilder<osmium::Node> NodeBuilder;
-        typedef OSMObjectBuilder<osmium::Relation> RelationBuilder;
+        using NodeBuilder     = OSMObjectBuilder<osmium::Node>;
+        using RelationBuilder = OSMObjectBuilder<osmium::Relation>;
 
         class WayBuilder : public OSMObjectBuilder<osmium::Way> {
 
@@ -398,7 +398,7 @@ namespace osmium {
 
         }; // class AreaBuilder
 
-        typedef ObjectBuilder<osmium::Changeset> ChangesetBuilder;
+        using ChangesetBuilder = ObjectBuilder<osmium::Changeset>;
 
     } // namespace builder
 
diff --git a/include/osmium/diff_iterator.hpp b/include/osmium/diff_iterator.hpp
index 6e0ba4b..0814fef 100644
--- a/include/osmium/diff_iterator.hpp
+++ b/include/osmium/diff_iterator.hpp
@@ -49,7 +49,7 @@ namespace osmium {
      * underlying OSMObjects.
      */
     template <typename TBasicIterator>
-    class DiffIterator : public std::iterator<std::input_iterator_tag, const osmium::DiffObject> {
+    class DiffIterator {
 
         static_assert(std::is_base_of<osmium::OSMObject, typename TBasicIterator::value_type>::value, "TBasicIterator::value_type must derive from osmium::OSMObject");
 
@@ -76,6 +76,12 @@ namespace osmium {
 
     public:
 
+        using iterator_category = std::input_iterator_tag;
+        using value_type        = const osmium::DiffObject;
+        using difference_type   = std::ptrdiff_t;
+        using pointer           = value_type*;
+        using reference         = value_type&;
+
         DiffIterator(TBasicIterator begin, TBasicIterator end) :
             m_prev(begin),
             m_curr(begin),
diff --git a/include/osmium/dynamic_handler.hpp b/include/osmium/dynamic_handler.hpp
index e7c9900..b250b57 100644
--- a/include/osmium/dynamic_handler.hpp
+++ b/include/osmium/dynamic_handler.hpp
@@ -143,7 +143,7 @@ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) ->
 
         class DynamicHandler : public osmium::handler::Handler {
 
-            typedef std::unique_ptr<osmium::handler::detail::HandlerWrapperBase> impl_ptr;
+            using impl_ptr = std::unique_ptr<osmium::handler::detail::HandlerWrapperBase>;
             impl_ptr m_impl;
 
         public:
diff --git a/include/osmium/geom/factory.hpp b/include/osmium/geom/factory.hpp
index 394228e..6737278 100644
--- a/include/osmium/geom/factory.hpp
+++ b/include/osmium/geom/factory.hpp
@@ -148,7 +148,7 @@ namespace osmium {
             /**
              * Add all points of an outer or inner ring to a multipolygon.
              */
-            void add_points(const osmium::OuterRing& nodes) {
+            void add_points(const osmium::NodeRefList& nodes) {
                 osmium::Location last_location;
                 for (const osmium::NodeRef& node_ref : nodes) {
                     if (last_location != node_ref.location()) {
@@ -182,12 +182,13 @@ namespace osmium {
                 m_impl(std::forward<TArgs>(args)...) {
             }
 
-            typedef TProjection projection_type;
-            typedef typename TGeomImpl::point_type        point_type;
-            typedef typename TGeomImpl::linestring_type   linestring_type;
-            typedef typename TGeomImpl::polygon_type      polygon_type;
-            typedef typename TGeomImpl::multipolygon_type multipolygon_type;
-            typedef typename TGeomImpl::ring_type         ring_type;
+            using projection_type   = TProjection;
+
+            using point_type        = typename TGeomImpl::point_type;
+            using linestring_type   = typename TGeomImpl::linestring_type;
+            using polygon_type      = typename TGeomImpl::polygon_type;
+            using multipolygon_type = typename TGeomImpl::multipolygon_type;
+            using ring_type         = typename TGeomImpl::ring_type;
 
             int epsg() const {
                 return m_projection.epsg();
@@ -378,8 +379,8 @@ namespace osmium {
                     m_impl.multipolygon_start();
 
                     for (auto it = area.cbegin(); it != area.cend(); ++it) {
-                        const osmium::OuterRing& ring = static_cast<const osmium::OuterRing&>(*it);
                         if (it->type() == osmium::item_type::outer_ring) {
+                            auto& ring = static_cast<const osmium::OuterRing&>(*it);
                             if (num_polygons > 0) {
                                 m_impl.multipolygon_polygon_finish();
                             }
@@ -390,6 +391,7 @@ namespace osmium {
                             ++num_rings;
                             ++num_polygons;
                         } else if (it->type() == osmium::item_type::inner_ring) {
+                            auto& ring = static_cast<const osmium::InnerRing&>(*it);
                             m_impl.multipolygon_inner_ring_start();
                             add_points(ring);
                             m_impl.multipolygon_inner_ring_finish();
diff --git a/include/osmium/geom/geojson.hpp b/include/osmium/geom/geojson.hpp
index 044a89f..5291257 100644
--- a/include/osmium/geom/geojson.hpp
+++ b/include/osmium/geom/geojson.hpp
@@ -53,11 +53,11 @@ namespace osmium {
 
             public:
 
-                typedef std::string point_type;
-                typedef std::string linestring_type;
-                typedef std::string polygon_type;
-                typedef std::string multipolygon_type;
-                typedef std::string ring_type;
+                using point_type        = std::string;
+                using linestring_type   = std::string;
+                using polygon_type      = std::string;
+                using multipolygon_type = std::string;
+                using ring_type         = std::string;
 
                 GeoJSONFactoryImpl(int precision = 7) :
                     m_precision(precision) {
diff --git a/include/osmium/geom/geos.hpp b/include/osmium/geom/geos.hpp
index f9026e5..187c048 100644
--- a/include/osmium/geom/geos.hpp
+++ b/include/osmium/geom/geos.hpp
@@ -93,11 +93,11 @@ namespace osmium {
 
             public:
 
-                typedef std::unique_ptr<geos::geom::Point>        point_type;
-                typedef std::unique_ptr<geos::geom::LineString>   linestring_type;
-                typedef std::unique_ptr<geos::geom::Polygon>      polygon_type;
-                typedef std::unique_ptr<geos::geom::MultiPolygon> multipolygon_type;
-                typedef std::unique_ptr<geos::geom::LinearRing>   ring_type;
+                using point_type        = std::unique_ptr<geos::geom::Point>;
+                using linestring_type   = std::unique_ptr<geos::geom::LineString>;
+                using polygon_type      = std::unique_ptr<geos::geom::Polygon>;
+                using multipolygon_type = std::unique_ptr<geos::geom::MultiPolygon>;
+                using ring_type         = std::unique_ptr<geos::geom::LinearRing>;
 
                 explicit GEOSFactoryImpl(geos::geom::GeometryFactory& geos_factory) :
                     m_precision_model(nullptr),
diff --git a/include/osmium/geom/ogr.hpp b/include/osmium/geom/ogr.hpp
index b8e9ef3..45586e2 100644
--- a/include/osmium/geom/ogr.hpp
+++ b/include/osmium/geom/ogr.hpp
@@ -62,18 +62,18 @@ namespace osmium {
 
             public:
 
-                typedef std::unique_ptr<OGRPoint>        point_type;
-                typedef std::unique_ptr<OGRLineString>   linestring_type;
-                typedef std::unique_ptr<OGRPolygon>      polygon_type;
-                typedef std::unique_ptr<OGRMultiPolygon> multipolygon_type;
-                typedef std::unique_ptr<OGRLinearRing>   ring_type;
+                using point_type        = std::unique_ptr<OGRPoint>;
+                using linestring_type   = std::unique_ptr<OGRLineString>;
+                using polygon_type      = std::unique_ptr<OGRPolygon>;
+                using multipolygon_type = std::unique_ptr<OGRMultiPolygon>;
+                using ring_type         = std::unique_ptr<OGRLinearRing>;
 
             private:
 
-                linestring_type   m_linestring;
-                multipolygon_type m_multipolygon;
-                polygon_type      m_polygon;
-                ring_type         m_ring;
+                linestring_type   m_linestring{nullptr};
+                multipolygon_type m_multipolygon{nullptr};
+                polygon_type      m_polygon{nullptr};
+                ring_type         m_ring{nullptr};
 
             public:
 
@@ -82,13 +82,13 @@ namespace osmium {
                 /* Point */
 
                 point_type make_point(const osmium::geom::Coordinates& xy) const {
-                    return point_type(new OGRPoint(xy.x, xy.y));
+                    return point_type{new OGRPoint{xy.x, xy.y}};
                 }
 
                 /* LineString */
 
                 void linestring_start() {
-                    m_linestring = std::unique_ptr<OGRLineString>(new OGRLineString());
+                    m_linestring.reset(new OGRLineString{});
                 }
 
                 void linestring_add_location(const osmium::geom::Coordinates& xy) {
@@ -97,13 +97,14 @@ namespace osmium {
                 }
 
                 linestring_type linestring_finish(size_t /* num_points */) {
+                    assert(!!m_linestring);
                     return std::move(m_linestring);
                 }
 
                 /* Polygon */
 
                 void polygon_start() {
-                    m_ring = std::unique_ptr<OGRLinearRing>(new OGRLinearRing());
+                    m_ring.reset(new OGRLinearRing{});
                 }
 
                 void polygon_add_location(const osmium::geom::Coordinates& xy) {
@@ -112,7 +113,7 @@ namespace osmium {
                 }
 
                 polygon_type polygon_finish(size_t /* num_points */) {
-                    std::unique_ptr<OGRPolygon> polygon = std::unique_ptr<OGRPolygon>(new OGRPolygon());
+                    auto polygon = std::unique_ptr<OGRPolygon>{new OGRPolygon{}};
                     polygon->addRingDirectly(m_ring.release());
                     return polygon;
                 }
@@ -120,11 +121,11 @@ namespace osmium {
                 /* MultiPolygon */
 
                 void multipolygon_start() {
-                    m_multipolygon.reset(new OGRMultiPolygon());
+                    m_multipolygon.reset(new OGRMultiPolygon{});
                 }
 
                 void multipolygon_polygon_start() {
-                    m_polygon.reset(new OGRPolygon());
+                    m_polygon.reset(new OGRPolygon{});
                 }
 
                 void multipolygon_polygon_finish() {
@@ -134,7 +135,7 @@ namespace osmium {
                 }
 
                 void multipolygon_outer_ring_start() {
-                    m_ring.reset(new OGRLinearRing());
+                    m_ring.reset(new OGRLinearRing{});
                 }
 
                 void multipolygon_outer_ring_finish() {
@@ -144,7 +145,7 @@ namespace osmium {
                 }
 
                 void multipolygon_inner_ring_start() {
-                    m_ring.reset(new OGRLinearRing());
+                    m_ring.reset(new OGRLinearRing{});
                 }
 
                 void multipolygon_inner_ring_finish() {
diff --git a/include/osmium/geom/rapid_geojson.hpp b/include/osmium/geom/rapid_geojson.hpp
index 87e1311..b54809a 100644
--- a/include/osmium/geom/rapid_geojson.hpp
+++ b/include/osmium/geom/rapid_geojson.hpp
@@ -53,11 +53,11 @@ namespace osmium {
 
             public:
 
-                typedef void point_type;
-                typedef void linestring_type;
-                typedef void polygon_type;
-                typedef void multipolygon_type;
-                typedef void ring_type;
+                using point_type        = void;
+                using linestring_type   = void;
+                using polygon_type      = void;
+                using multipolygon_type = void;
+                using ring_type         = void;
 
                 RapidGeoJSONFactoryImpl(TWriter& writer) :
                     m_writer(&writer) {
diff --git a/include/osmium/geom/wkb.hpp b/include/osmium/geom/wkb.hpp
index 19c1216..9df1823 100644
--- a/include/osmium/geom/wkb.hpp
+++ b/include/osmium/geom/wkb.hpp
@@ -142,16 +142,17 @@ namespace osmium {
                 }
 
                 void set_size(const size_t offset, const size_t size) {
-                    *reinterpret_cast<uint32_t*>(&m_data[offset]) = static_cast_with_assert<uint32_t>(size);
+                    uint32_t s = static_cast_with_assert<uint32_t>(size);
+                    std::copy_n(reinterpret_cast<char*>(&s), sizeof(uint32_t), &m_data[offset]);
                 }
 
             public:
 
-                typedef std::string point_type;
-                typedef std::string linestring_type;
-                typedef std::string polygon_type;
-                typedef std::string multipolygon_type;
-                typedef std::string ring_type;
+                using point_type        = std::string;
+                using linestring_type   = std::string;
+                using polygon_type      = std::string;
+                using multipolygon_type = std::string;
+                using ring_type         = std::string;
 
                 explicit WKBFactoryImpl(wkb_type wtype = wkb_type::wkb, out_type otype = out_type::binary) :
                     m_wkb_type(wtype),
diff --git a/include/osmium/geom/wkt.hpp b/include/osmium/geom/wkt.hpp
index ae73b96..57a6625 100644
--- a/include/osmium/geom/wkt.hpp
+++ b/include/osmium/geom/wkt.hpp
@@ -54,11 +54,11 @@ namespace osmium {
 
             public:
 
-                typedef std::string point_type;
-                typedef std::string linestring_type;
-                typedef std::string polygon_type;
-                typedef std::string multipolygon_type;
-                typedef std::string ring_type;
+                using point_type        = std::string;
+                using linestring_type   = std::string;
+                using polygon_type      = std::string;
+                using multipolygon_type = std::string;
+                using ring_type         = std::string;
 
                 WKTFactoryImpl(int precision = 7) :
                     m_precision(precision) {
diff --git a/include/osmium/handler/chain.hpp b/include/osmium/handler/chain.hpp
index e59eb05..109e353 100644
--- a/include/osmium/handler/chain.hpp
+++ b/include/osmium/handler/chain.hpp
@@ -67,7 +67,7 @@ namespace osmium {
         template <typename... THandler>
         class ChainHandler : public osmium::handler::Handler {
 
-            typedef std::tuple<THandler&...> handlers_type;
+            using handlers_type = std::tuple<THandler&...>;
             handlers_type m_handlers;
 
             template <int N, int SIZE, typename THandlers>
diff --git a/include/osmium/handler/disk_store.hpp b/include/osmium/handler/disk_store.hpp
index b8ab229..4e95573 100644
--- a/include/osmium/handler/disk_store.hpp
+++ b/include/osmium/handler/disk_store.hpp
@@ -57,7 +57,7 @@ namespace osmium {
          */
         class DiskStore : public osmium::handler::Handler {
 
-            typedef osmium::index::map::Map<unsigned_object_id_type, size_t> offset_index_type;
+            using offset_index_type = osmium::index::map::Map<unsigned_object_id_type, size_t>;
 
             size_t m_offset = 0;
             int m_data_fd;
diff --git a/include/osmium/handler/node_locations_for_ways.hpp b/include/osmium/handler/node_locations_for_ways.hpp
index f62a4db..e836397 100644
--- a/include/osmium/handler/node_locations_for_ways.hpp
+++ b/include/osmium/handler/node_locations_for_ways.hpp
@@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
+#include <limits>
 #include <type_traits>
 
 #include <osmium/handler.hpp>
@@ -50,7 +51,7 @@ namespace osmium {
 
     namespace handler {
 
-        typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> dummy_type;
+        using dummy_type = osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location>;
 
         /**
          * Handler to retrieve locations from nodes and add them to ways.
@@ -69,8 +70,8 @@ namespace osmium {
 
         public:
 
-            typedef TStoragePosIDs index_pos_type;
-            typedef TStorageNegIDs index_neg_type;
+            using index_pos_type = TStoragePosIDs;
+            using index_neg_type = TStorageNegIDs;
 
         private:
 
@@ -80,6 +81,8 @@ namespace osmium {
             /// Object that handles the actual storage of the node locations (with negative IDs).
             TStorageNegIDs& m_storage_neg;
 
+            osmium::unsigned_object_id_type m_last_id{0};
+
             bool m_ignore_errors {false};
 
             bool m_must_sort {false};
@@ -115,7 +118,11 @@ namespace osmium {
              * Store the location of the node in the storage.
              */
             void node(const osmium::Node& node) {
-                m_must_sort = true;
+                if (node.positive_id() < m_last_id) {
+                    m_must_sort = true;
+                }
+                m_last_id = node.positive_id();
+
                 const osmium::object_id_type id = node.id();
                 if (id >= 0) {
                     m_storage_pos.set(static_cast<osmium::unsigned_object_id_type>( id), node.location());
@@ -144,6 +151,7 @@ namespace osmium {
                     m_storage_pos.sort();
                     m_storage_neg.sort();
                     m_must_sort = false;
+                    m_last_id = std::numeric_limits<osmium::unsigned_object_id_type>::max();
                 }
                 bool error = false;
                 for (auto& node_ref : way.nodes()) {
diff --git a/include/osmium/handler/object_relations.hpp b/include/osmium/handler/object_relations.hpp
index 4afcf6a..279365d 100644
--- a/include/osmium/handler/object_relations.hpp
+++ b/include/osmium/handler/object_relations.hpp
@@ -52,7 +52,7 @@ namespace osmium {
          */
         class ObjectRelations : public osmium::handler::Handler {
 
-            typedef osmium::index::multimap::Multimap<unsigned_object_id_type, unsigned_object_id_type> index_type;
+            using index_type = osmium::index::multimap::Multimap<unsigned_object_id_type, unsigned_object_id_type>;
 
             index_type& m_index_n2w;
             index_type& m_index_n2r;
diff --git a/include/osmium/index/detail/mmap_vector_base.hpp b/include/osmium/index/detail/mmap_vector_base.hpp
index aadeef0..f84cc2e 100644
--- a/include/osmium/index/detail/mmap_vector_base.hpp
+++ b/include/osmium/index/detail/mmap_vector_base.hpp
@@ -33,10 +33,13 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
+#include <algorithm>
+#include <cassert>
 #include <cstddef>
 #include <new> // IWYU pragma: keep
 #include <stdexcept>
 
+#include <osmium/index/index.hpp>
 #include <osmium/util/memory_mapping.hpp>
 
 namespace osmium {
@@ -63,22 +66,26 @@ namespace osmium {
             mmap_vector_base(int fd, size_t capacity, size_t size = 0) :
                 m_size(size),
                 m_mapping(capacity, osmium::util::MemoryMapping::mapping_mode::write_shared, fd) {
+                assert(size <= capacity);
+                std::fill(data() + size, data() + capacity, osmium::index::empty_value<T>());
+                shrink_to_fit();
             }
 
             explicit mmap_vector_base(size_t capacity = mmap_vector_size_increment) :
                 m_size(0),
                 m_mapping(capacity) {
+                std::fill_n(data(), capacity, osmium::index::empty_value<T>());
             }
 
             ~mmap_vector_base() noexcept = default;
 
-            typedef T value_type;
-            typedef T& reference;
-            typedef const T& const_reference;
-            typedef T* pointer;
-            typedef const T* const_pointer;
-            typedef T* iterator;
-            typedef const T* const_iterator;
+            using value_type      = T;
+            using pointer         = value_type*;
+            using const_pointer   = const value_type*;
+            using reference       = value_type&;
+            using const_reference = const value_type&;
+            using iterator        = value_type*;
+            using const_iterator  = const value_type*;
 
             void close() {
                 m_mapping.unmap();
@@ -105,6 +112,7 @@ namespace osmium {
             }
 
             T& operator[](size_t n) {
+                assert(n < m_size);
                 return data()[n];
             }
 
@@ -120,7 +128,9 @@ namespace osmium {
             }
 
             void shrink_to_fit() {
-                // XXX do something here
+                while (m_size > 0 && data()[m_size - 1] == osmium::index::empty_value<T>()) {
+                    --m_size;
+                }
             }
 
             void push_back(const T& value) {
@@ -133,7 +143,9 @@ namespace osmium {
 
             void reserve(size_t new_capacity) {
                 if (new_capacity > capacity()) {
+                    size_t old_capacity = capacity();
                     m_mapping.resize(new_capacity);
+                    std::fill(data() + old_capacity, data() + new_capacity, osmium::index::empty_value<T>());
                 }
             }
 
@@ -141,9 +153,6 @@ namespace osmium {
                 if (new_size > capacity()) {
                     reserve(new_size + osmium::detail::mmap_vector_size_increment);
                 }
-                if (new_size > size()) {
-                    new (data() + size()) T[new_size - size()];
-                }
                 m_size = new_size;
             }
 
diff --git a/include/osmium/index/detail/mmap_vector_file.hpp b/include/osmium/index/detail/mmap_vector_file.hpp
index 666a409..e077c60 100644
--- a/include/osmium/index/detail/mmap_vector_file.hpp
+++ b/include/osmium/index/detail/mmap_vector_file.hpp
@@ -33,6 +33,9 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
+#include <algorithm>
+#include <string>
+
 #include <osmium/index/detail/mmap_vector_base.hpp>
 #include <osmium/index/detail/tmpfile.hpp>
 #include <osmium/util/file.hpp>
@@ -48,6 +51,16 @@ namespace osmium {
         template <typename T>
         class mmap_vector_file : public mmap_vector_base<T> {
 
+            size_t filesize(int fd) const {
+                size_t size = osmium::util::file_size(fd);
+
+                if (size % sizeof(T) != 0) {
+                    throw std::runtime_error("Index file has wrong size (must be multiple of " + std::to_string(sizeof(T)) + ").");
+                }
+
+                return size / sizeof(T);
+            }
+
         public:
 
             mmap_vector_file() :
@@ -59,8 +72,8 @@ namespace osmium {
             explicit mmap_vector_file(int fd) :
                 mmap_vector_base<T>(
                     fd,
-                    osmium::util::file_size(fd) / sizeof(T),
-                    osmium::util::file_size(fd) / sizeof(T)) {
+                    std::max(osmium::detail::mmap_vector_size_increment, filesize(fd)),
+                    filesize(fd)) {
             }
 
             ~mmap_vector_file() noexcept = default;
diff --git a/include/osmium/index/detail/vector_map.hpp b/include/osmium/index/detail/vector_map.hpp
index 0fa59dd..390a402 100644
--- a/include/osmium/index/detail/vector_map.hpp
+++ b/include/osmium/index/detail/vector_map.hpp
@@ -55,10 +55,10 @@ namespace osmium {
 
             public:
 
-                typedef TValue element_type;
-                typedef TVector vector_type;
-                typedef typename vector_type::iterator iterator;
-                typedef typename vector_type::const_iterator const_iterator;
+                using element_type   = TValue;
+                using vector_type    = TVector;
+                using iterator       = typename vector_type::iterator;
+                using const_iterator = typename vector_type::const_iterator;
 
                 VectorBasedDenseMap() :
                     m_vector() {
@@ -146,10 +146,10 @@ namespace osmium {
 
             public:
 
-                typedef typename std::pair<TId, TValue> element_type;
-                typedef TVector<element_type> vector_type;
-                typedef typename vector_type::iterator iterator;
-                typedef typename vector_type::const_iterator const_iterator;
+                using element_type   = typename std::pair<TId, TValue>;
+                using vector_type    = TVector<element_type>;
+                using iterator       = typename vector_type::iterator;
+                using const_iterator = typename vector_type::const_iterator;
 
             private:
 
diff --git a/include/osmium/index/detail/vector_multimap.hpp b/include/osmium/index/detail/vector_multimap.hpp
index 35a4cc1..af2ec53 100644
--- a/include/osmium/index/detail/vector_multimap.hpp
+++ b/include/osmium/index/detail/vector_multimap.hpp
@@ -52,10 +52,10 @@ namespace osmium {
 
             public:
 
-                typedef typename std::pair<TId, TValue> element_type;
-                typedef TVector<element_type> vector_type;
-                typedef typename vector_type::iterator iterator;
-                typedef typename vector_type::const_iterator const_iterator;
+                using element_type   = typename std::pair<TId, TValue>;
+                using vector_type    = TVector<element_type>;
+                using iterator       = typename vector_type::iterator;
+                using const_iterator = typename vector_type::const_iterator;
 
             private:
 
diff --git a/include/osmium/index/map.hpp b/include/osmium/index/map.hpp
index f90a895..c8f7598 100644
--- a/include/osmium/index/map.hpp
+++ b/include/osmium/index/map.hpp
@@ -98,10 +98,10 @@ namespace osmium {
             public:
 
                 /// The "key" type, usually osmium::unsigned_object_id_type.
-                typedef TId key_type;
+                using key_type = TId;
 
                 /// The "value" type, usually a Location or size_t.
-                typedef TValue value_type;
+                using value_type = TValue;
 
                 Map() = default;
 
@@ -171,10 +171,10 @@ namespace osmium {
 
         public:
 
-            typedef TId id_type;
-            typedef TValue value_type;
-            typedef osmium::index::map::Map<id_type, value_type> map_type;
-            typedef std::function<map_type*(const std::vector<std::string>&)> create_map_func;
+            using id_type         = TId;
+            using value_type      = TValue;
+            using map_type        = osmium::index::map::Map<id_type, value_type>;
+            using create_map_func = std::function<map_type*(const std::vector<std::string>&)>;
 
         private:
 
diff --git a/include/osmium/index/map/sparse_mem_map.hpp b/include/osmium/index/map/sparse_mem_map.hpp
index 41351c8..95f570b 100644
--- a/include/osmium/index/map/sparse_mem_map.hpp
+++ b/include/osmium/index/map/sparse_mem_map.hpp
@@ -98,7 +98,7 @@ namespace osmium {
                 }
 
                 void dump_as_list(const int fd) final {
-                    typedef typename std::map<TId, TValue>::value_type t;
+                    using t = typename std::map<TId, TValue>::value_type;
                     std::vector<t> v;
                     v.reserve(m_elements.size());
                     std::copy(m_elements.cbegin(), m_elements.cend(), std::back_inserter(v));
diff --git a/include/osmium/index/multimap.hpp b/include/osmium/index/multimap.hpp
index de6aa1e..9361d6a 100644
--- a/include/osmium/index/multimap.hpp
+++ b/include/osmium/index/multimap.hpp
@@ -52,7 +52,7 @@ namespace osmium {
 
                 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;
+                using element_type = typename std::pair<TId, TValue>;
 
                 Multimap(const Multimap&) = delete;
                 Multimap& operator=(const Multimap&) = delete;
@@ -65,10 +65,10 @@ namespace osmium {
             public:
 
                 /// The "key" type, usually osmium::unsigned_object_id_type.
-                typedef TId key_type;
+                using key_type = TId;
 
                 /// The "value" type, usually a Location or size_t.
-                typedef TValue value_type;
+                using value_type = TValue;
 
                 Multimap() = default;
 
@@ -77,7 +77,7 @@ namespace osmium {
                 /// Set the field with id to value.
                 virtual void set(const TId id, const TValue value) = 0;
 
-                typedef element_type* iterator;
+                using iterator = element_type*;
 
 //                virtual std::pair<iterator, iterator> get_all(const TId id) const = 0;
 
diff --git a/include/osmium/index/multimap/hybrid.hpp b/include/osmium/index/multimap/hybrid.hpp
index ba94302..06a5f6e 100644
--- a/include/osmium/index/multimap/hybrid.hpp
+++ b/include/osmium/index/multimap/hybrid.hpp
@@ -50,10 +50,10 @@ namespace osmium {
             template <typename TId, typename TValue>
             class HybridIterator {
 
-                typedef SparseMemArray<TId, TValue> main_map_type;
-                typedef SparseMemMultimap<TId, TValue> extra_map_type;
+                using main_map_type  = SparseMemArray<TId, TValue>;
+                using extra_map_type = SparseMemMultimap<TId, TValue>;
 
-                typedef typename std::pair<TId, TValue> element_type;
+                using element_type = typename std::pair<TId, TValue>;
 
                 typename main_map_type::iterator m_begin_main;
                 typename main_map_type::iterator m_end_main;
@@ -120,16 +120,16 @@ namespace osmium {
             template <typename TId, typename TValue>
             class Hybrid : public Multimap<TId, TValue> {
 
-                typedef SparseMemArray<TId, TValue> main_map_type;
-                typedef SparseMemMultimap<TId, TValue> extra_map_type;
+                using main_map_type  = SparseMemArray<TId, TValue>;
+                using extra_map_type = SparseMemMultimap<TId, TValue>;
 
                 main_map_type m_main;
                 extra_map_type m_extra;
 
             public:
 
-                typedef HybridIterator<TId, TValue> iterator;
-                typedef const HybridIterator<TId, TValue> const_iterator;
+                using iterator       = HybridIterator<TId, TValue>;
+                using const_iterator = const HybridIterator<TId, TValue>;
 
                 Hybrid() :
                     m_main(),
diff --git a/include/osmium/index/multimap/sparse_mem_multimap.hpp b/include/osmium/index/multimap/sparse_mem_multimap.hpp
index 0859fca..7a67cc7 100644
--- a/include/osmium/index/multimap/sparse_mem_multimap.hpp
+++ b/include/osmium/index/multimap/sparse_mem_multimap.hpp
@@ -63,12 +63,11 @@ namespace osmium {
 
             public:
 
-                typedef typename std::multimap<const TId, TValue> collection_type;
-                typedef typename collection_type::iterator iterator;
-                typedef typename collection_type::const_iterator const_iterator;
-                typedef typename collection_type::value_type value_type;
-
-                typedef typename std::pair<TId, TValue> element_type;
+                using collection_type = typename std::multimap<const TId, TValue>;
+                using iterator        = typename collection_type::iterator;
+                using const_iterator  = typename collection_type::const_iterator;
+                using value_type      = typename collection_type::value_type;
+                using element_type    = typename std::pair<TId, TValue>;
 
             private:
 
diff --git a/include/osmium/io/compression.hpp b/include/osmium/io/compression.hpp
index 7d95661..fc14e26 100644
--- a/include/osmium/io/compression.hpp
+++ b/include/osmium/io/compression.hpp
@@ -118,16 +118,16 @@ namespace osmium {
 
         public:
 
-            typedef std::function<osmium::io::Compressor*(int, fsync)> create_compressor_type;
-            typedef std::function<osmium::io::Decompressor*(int)> create_decompressor_type_fd;
-            typedef std::function<osmium::io::Decompressor*(const char*, size_t)> create_decompressor_type_buffer;
+            using create_compressor_type          = std::function<osmium::io::Compressor*(int, fsync)>;
+            using create_decompressor_type_fd     = std::function<osmium::io::Decompressor*(int)>;
+            using create_decompressor_type_buffer = std::function<osmium::io::Decompressor*(const char*, size_t)>;
 
         private:
 
-            typedef std::map<const osmium::io::file_compression,
-                             std::tuple<create_compressor_type,
-                                        create_decompressor_type_fd,
-                                        create_decompressor_type_buffer>> compression_map_type;
+            using compression_map_type = std::map<const osmium::io::file_compression,
+                                                  std::tuple<create_compressor_type,
+                                                             create_decompressor_type_fd,
+                                                             create_decompressor_type_buffer>>;
 
             compression_map_type m_callbacks;
 
diff --git a/include/osmium/io/detail/debug_output_format.hpp b/include/osmium/io/detail/debug_output_format.hpp
index 3f7537e..b45173c 100644
--- a/include/osmium/io/detail/debug_output_format.hpp
+++ b/include/osmium/io/detail/debug_output_format.hpp
@@ -44,12 +44,15 @@ DEALINGS IN THE SOFTWARE.
 #include <thread>
 #include <utility>
 
+#include <boost/crc.hpp>
+
 #include <osmium/io/detail/output_format.hpp>
 #include <osmium/io/file_format.hpp>
 #include <osmium/memory/buffer.hpp>
 #include <osmium/memory/collection.hpp>
 #include <osmium/osm/box.hpp>
 #include <osmium/osm/changeset.hpp>
+#include <osmium/osm/crc.hpp>
 #include <osmium/osm/item_type.hpp>
 #include <osmium/osm/location.hpp>
 #include <osmium/osm/node.hpp>
@@ -90,6 +93,9 @@ namespace osmium {
                 /// Output with ANSI colors?
                 bool use_color;
 
+                /// Add CRC32 checksum to each object?
+                bool add_crc32;
+
             };
 
             /**
@@ -237,6 +243,21 @@ namespace osmium {
                     *m_out += '\n';
                 }
 
+                template <typename T>
+                void write_crc32(const T& object) {
+                    write_fieldname("crc32");
+                    osmium::CRC<boost::crc_32_type> crc32;
+                    crc32.update(object);
+                    output_formatted("    %x\n", crc32().checksum());
+                }
+
+                void write_crc32(const osmium::Changeset& object) {
+                    write_fieldname("crc32");
+                    osmium::CRC<boost::crc_32_type> crc32;
+                    crc32.update(object);
+                    output_formatted("      %x\n", crc32().checksum());
+                }
+
             public:
 
                 DebugOutputBlock(osmium::memory::Buffer&& buffer, const debug_output_options& options) :
@@ -274,6 +295,10 @@ namespace osmium {
 
                     write_tags(node.tags());
 
+                    if (m_options.add_crc32) {
+                        write_crc32(node);
+                    }
+
                     *m_out += '\n';
                 }
 
@@ -306,6 +331,10 @@ namespace osmium {
                         *m_out += '\n';
                     }
 
+                    if (m_options.add_crc32) {
+                        write_crc32(way);
+                    }
+
                     *m_out += '\n';
                 }
 
@@ -328,6 +357,10 @@ namespace osmium {
                         *m_out += '\n';
                     }
 
+                    if (m_options.add_crc32) {
+                        write_crc32(relation);
+                    }
+
                     *m_out += '\n';
                 }
 
@@ -386,6 +419,10 @@ namespace osmium {
                         }
                     }
 
+                    if (m_options.add_crc32) {
+                        write_crc32(changeset);
+                    }
+
                     *m_out += '\n';
                 }
 
@@ -414,6 +451,7 @@ namespace osmium {
                     m_options() {
                     m_options.add_metadata = file.is_not_false("add_metadata");
                     m_options.use_color    = file.is_true("color");
+                    m_options.add_crc32    = file.is_true("add_crc32");
                 }
 
                 DebugOutputFormat(const DebugOutputFormat&) = delete;
diff --git a/include/osmium/io/detail/input_format.hpp b/include/osmium/io/detail/input_format.hpp
index 0d78851..e04e57f 100644
--- a/include/osmium/io/detail/input_format.hpp
+++ b/include/osmium/io/detail/input_format.hpp
@@ -154,18 +154,14 @@ namespace osmium {
 
             public:
 
-                typedef std::function<
-                            std::unique_ptr<Parser>(
-                                future_string_queue_type&,
-                                future_buffer_queue_type&,
-                                std::promise<osmium::io::Header>& header_promise,
-                                osmium::osm_entity_bits::type read_which_entities
-                            )
-                        > create_parser_type;
+                using create_parser_type = std::function<std::unique_ptr<Parser>(future_string_queue_type&,
+                                                                                 future_buffer_queue_type&,
+                                                                                 std::promise<osmium::io::Header>& header_promise,
+                                                                                 osmium::osm_entity_bits::type read_which_entities)>;
 
             private:
 
-                typedef std::map<osmium::io::file_format, create_parser_type> map_type;
+                using map_type = std::map<osmium::io::file_format, create_parser_type>;
 
                 map_type m_callbacks;
 
diff --git a/include/osmium/io/detail/opl_output_format.hpp b/include/osmium/io/detail/opl_output_format.hpp
index 3fedf7e..427c49e 100644
--- a/include/osmium/io/detail/opl_output_format.hpp
+++ b/include/osmium/io/detail/opl_output_format.hpp
@@ -74,6 +74,9 @@ namespace osmium {
                 /// Should metadata of objects be added?
                 bool add_metadata;
 
+                /// Should node locations be added to ways?
+                bool locations_on_ways;
+
             };
 
             /**
@@ -160,13 +163,33 @@ namespace osmium {
 
                     *m_out += " N";
                     bool first = true;
-                    for (const auto& node_ref : way.nodes()) {
-                        if (first) {
-                            first = false;
-                        } else {
-                            *m_out += ',';
+                    if (m_options.locations_on_ways) {
+                        for (const auto& node_ref : way.nodes()) {
+                            if (first) {
+                                first = false;
+                            } else {
+                                *m_out += ',';
+                            }
+                            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 {
+                                *m_out += ',';
+                            }
+                            output_formatted("n%" PRId64, node_ref.ref());
                         }
-                        output_formatted("n%" PRId64, node_ref.ref());
                     }
                     *m_out += '\n';
                 }
@@ -226,7 +249,8 @@ namespace osmium {
                 OPLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) :
                     OutputFormat(output_queue),
                     m_options() {
-                    m_options.add_metadata = file.is_not_false("add_metadata");
+                    m_options.add_metadata      = file.is_not_false("add_metadata");
+                    m_options.locations_on_ways = file.is_true("locations_on_ways");
                 }
 
                 OPLOutputFormat(const OPLOutputFormat&) = delete;
diff --git a/include/osmium/io/detail/output_format.hpp b/include/osmium/io/detail/output_format.hpp
index 34ca82f..c741218 100644
--- a/include/osmium/io/detail/output_format.hpp
+++ b/include/osmium/io/detail/output_format.hpp
@@ -133,11 +133,11 @@ namespace osmium {
 
             public:
 
-                typedef std::function<osmium::io::detail::OutputFormat*(const osmium::io::File&, future_string_queue_type&)> create_output_type;
+                using create_output_type = std::function<osmium::io::detail::OutputFormat*(const osmium::io::File&, future_string_queue_type&)>;
 
             private:
 
-                typedef std::map<osmium::io::file_format, create_output_type> map_type;
+                using map_type = std::map<osmium::io::file_format, create_output_type>;
 
                 map_type m_callbacks;
 
diff --git a/include/osmium/io/detail/pbf_decoder.hpp b/include/osmium/io/detail/pbf_decoder.hpp
index 43de1c1..1c0d2eb 100644
--- a/include/osmium/io/detail/pbf_decoder.hpp
+++ b/include/osmium/io/detail/pbf_decoder.hpp
@@ -293,6 +293,8 @@ namespace osmium {
                     kv_type keys;
                     kv_type vals;
                     std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> refs;
+                    std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> lats;
+                    std::pair<protozero::pbf_reader::const_sint64_iterator, protozero::pbf_reader::const_sint64_iterator> lons;
 
                     osm_string_len_type user = { "", 0 };
 
@@ -314,6 +316,12 @@ namespace osmium {
                             case OSMFormat::Way::packed_sint64_refs:
                                 refs = pbf_way.get_packed_sint64();
                                 break;
+                            case OSMFormat::Way::packed_sint64_lat:
+                                lats = pbf_way.get_packed_sint64();
+                                break;
+                            case OSMFormat::Way::packed_sint64_lon:
+                                lons = pbf_way.get_packed_sint64();
+                                break;
                             default:
                                 pbf_way.skip();
                         }
@@ -324,8 +332,20 @@ namespace osmium {
                     if (refs.first != refs.second) {
                         osmium::builder::WayNodeListBuilder wnl_builder(m_buffer, &builder);
                         osmium::util::DeltaDecode<int64_t> ref;
-                        while (refs.first != refs.second) {
-                            wnl_builder.add_node_ref(ref.update(*refs.first++));
+                        if (lats.first == lats.second) {
+                            while (refs.first != refs.second) {
+                                wnl_builder.add_node_ref(ref.update(*refs.first++));
+                            }
+                        } else {
+                            osmium::util::DeltaDecode<int64_t> lon;
+                            osmium::util::DeltaDecode<int64_t> lat;
+                            while (refs.first != refs.second && lons.first != lons.second && lats.first != lats.second) {
+                                wnl_builder.add_node_ref(
+                                    ref.update(*refs.first++),
+                                    osmium::Location{convert_pbf_coordinate(lon.update(*lons.first++)),
+                                                     convert_pbf_coordinate(lat.update(*lats.first++))}
+                                );
+                            }
                         }
                     }
 
diff --git a/include/osmium/io/detail/pbf_output_format.hpp b/include/osmium/io/detail/pbf_output_format.hpp
index 878d7b4..5046087 100644
--- a/include/osmium/io/detail/pbf_output_format.hpp
+++ b/include/osmium/io/detail/pbf_output_format.hpp
@@ -101,6 +101,9 @@ namespace osmium {
                 /// Should the visible flag be added to all OSM objects?
                 bool add_visible_flag;
 
+                /// Should node locations be added to ways?
+                bool locations_on_ways;
+
             };
 
             /**
@@ -483,6 +486,7 @@ namespace osmium {
                     m_options.add_metadata = file.is_not_false("pbf_add_metadata") && file.is_not_false("add_metadata");
                     m_options.add_historical_information_flag = file.has_multiple_object_versions();
                     m_options.add_visible_flag = file.has_multiple_object_versions();
+                    m_options.locations_on_ways = file.is_true("locations_on_ways");
                 }
 
                 PBFOutputFormat(const PBFOutputFormat&) = delete;
@@ -514,6 +518,10 @@ namespace osmium {
                         pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_required_features, "HistoricalInformation");
                     }
 
+                    if (m_options.locations_on_ways) {
+                        pbf_header_block.add_string(OSMFormat::HeaderBlock::repeated_string_optional_features, "LocationsOnWays");
+                    }
+
                     pbf_header_block.add_string(OSMFormat::HeaderBlock::optional_string_writingprogram, header.get("generator"));
 
                     std::string osmosis_replication_timestamp = header.get("osmosis_replication_timestamp");
@@ -571,15 +579,34 @@ namespace osmium {
                     pbf_way.add_int64(OSMFormat::Way::required_int64_id, way.id());
                     add_meta(way, pbf_way);
 
+                    const auto& nodes = way.nodes();
+
                     static auto map_node_ref = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> osmium::object_id_type {
                         return node_ref->ref();
                     };
-                    typedef osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_ref), osmium::object_id_type> it_type;
-
-                    const auto& nodes = way.nodes();
+                    using it_type = osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_ref), osmium::object_id_type>;
                     it_type first { nodes.cbegin(), nodes.cend(), map_node_ref };
                     it_type last { nodes.cend(), nodes.cend(), map_node_ref };
                     pbf_way.add_packed_sint64(OSMFormat::Way::packed_sint64_refs, first, last);
+
+                    if (m_options.locations_on_ways) {
+                        static auto map_node_x = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> int64_t {
+                            return lonlat2int(node_ref->location().lon_without_check());
+                        };
+                        static auto map_node_y = [](osmium::NodeRefList::const_iterator node_ref) noexcept -> int64_t {
+                            return lonlat2int(node_ref->location().lat_without_check());
+                        };
+                        using it_type_x = osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_x), int64_t>;
+                        using it_type_y = osmium::util::DeltaEncodeIterator<osmium::NodeRefList::const_iterator, decltype(map_node_y), int64_t>;
+
+                        it_type_x first_x { nodes.cbegin(), nodes.cend(), map_node_x };
+                        it_type_x last_x { nodes.cend(), nodes.cend(), map_node_x };
+                        pbf_way.add_packed_sint64(OSMFormat::Way::packed_sint64_lon, first_x, last_x);
+
+                        it_type_y first_y { nodes.cbegin(), nodes.cend(), map_node_y };
+                        it_type_y last_y { nodes.cend(), nodes.cend(), map_node_y };
+                        pbf_way.add_packed_sint64(OSMFormat::Way::packed_sint64_lat, first_y, last_y);
+                    }
                 }
 
                 void relation(const osmium::Relation& relation) {
@@ -599,7 +626,7 @@ namespace osmium {
                     static auto map_member_ref = [](osmium::RelationMemberList::const_iterator member) noexcept -> osmium::object_id_type {
                         return member->ref();
                     };
-                    typedef osmium::util::DeltaEncodeIterator<osmium::RelationMemberList::const_iterator, decltype(map_member_ref), osmium::object_id_type> it_type;
+                    using it_type = osmium::util::DeltaEncodeIterator<osmium::RelationMemberList::const_iterator, decltype(map_member_ref), osmium::object_id_type>;
                     const auto& members = relation.members();
                     it_type first { members.cbegin(), members.cend(), map_member_ref };
                     it_type last { members.cend(), members.cend(), map_member_ref };
diff --git a/include/osmium/io/detail/protobuf_tags.hpp b/include/osmium/io/detail/protobuf_tags.hpp
index bdaabba..3eb7902 100644
--- a/include/osmium/io/detail/protobuf_tags.hpp
+++ b/include/osmium/io/detail/protobuf_tags.hpp
@@ -146,7 +146,9 @@ namespace osmium {
                     packed_uint32_keys = 2,
                     packed_uint32_vals = 3,
                     optional_Info_info = 4,
-                    packed_sint64_refs = 8
+                    packed_sint64_refs = 8,
+                    packed_sint64_lat  = 9,
+                    packed_sint64_lon  = 10
                 };
 
                 enum class Relation : protozero::pbf_tag_type {
diff --git a/include/osmium/io/detail/queue_util.hpp b/include/osmium/io/detail/queue_util.hpp
index af84544..bc47020 100644
--- a/include/osmium/io/detail/queue_util.hpp
+++ b/include/osmium/io/detail/queue_util.hpp
@@ -47,6 +47,9 @@ namespace osmium {
 
         namespace detail {
 
+            template <typename T>
+            using future_queue_type = osmium::thread::Queue<std::future<T>>;
+
             /**
              * This type of queue contains buffers with OSM data in them.
              * The "end of file" is marked by an invalid Buffer.
@@ -54,7 +57,7 @@ namespace osmium {
              * transport exceptions. The future also helps with keeping the
              * data in order.
              */
-            using future_buffer_queue_type = osmium::thread::Queue<std::future<osmium::memory::Buffer>>;
+            using future_buffer_queue_type = future_queue_type<osmium::memory::Buffer>;
 
             /**
              * This type of queue contains OSM file data in the form it is
@@ -71,24 +74,24 @@ namespace osmium {
              * transport exceptions. The future also helps with keeping the
              * data in order.
              */
-            using future_string_queue_type = osmium::thread::Queue<std::future<std::string>>;
+            using future_string_queue_type = future_queue_type<std::string>;
 
             template <typename T>
-            inline void add_to_queue(osmium::thread::Queue<std::future<T>>& queue, T&& data) {
+            inline void add_to_queue(future_queue_type<T>& queue, T&& data) {
                 std::promise<T> promise;
                 queue.push(promise.get_future());
                 promise.set_value(std::forward<T>(data));
             }
 
             template <typename T>
-            inline void add_to_queue(osmium::thread::Queue<std::future<T>>& queue, std::exception_ptr&& exception) {
+            inline void add_to_queue(future_queue_type<T>& queue, std::exception_ptr&& exception) {
                 std::promise<T> promise;
                 queue.push(promise.get_future());
                 promise.set_exception(std::move(exception));
             }
 
             template <typename T>
-            inline void add_end_of_data_to_queue(osmium::thread::Queue<std::future<T>>& queue) {
+            inline void add_end_of_data_to_queue(future_queue_type<T>& queue) {
                 add_to_queue<T>(queue, T{});
             }
 
@@ -103,14 +106,12 @@ namespace osmium {
             template <typename T>
             class queue_wrapper {
 
-                using queue_type = osmium::thread::Queue<std::future<T>>;
-
-                queue_type& m_queue;
+                future_queue_type<T>& m_queue;
                 bool m_has_reached_end_of_data;
 
             public:
 
-                explicit queue_wrapper(queue_type& queue) :
+                explicit queue_wrapper(future_queue_type<T>& queue) :
                     m_queue(queue),
                     m_has_reached_end_of_data(false) {
                 }
diff --git a/include/osmium/io/detail/string_table.hpp b/include/osmium/io/detail/string_table.hpp
index 55c6226..b989530 100644
--- a/include/osmium/io/detail/string_table.hpp
+++ b/include/osmium/io/detail/string_table.hpp
@@ -109,15 +109,22 @@ namespace osmium {
                     return m_chunks.front().c_str() + chunk_len;
                 }
 
-                class const_iterator : public std::iterator<std::forward_iterator_tag, const char*> {
+                class const_iterator {
+
+                    using it_type = std::list<std::string>::const_iterator;
 
-                    typedef std::list<std::string>::const_iterator it_type;
                     it_type m_it;
                     const it_type m_last;
                     const char* m_pos;
 
                 public:
 
+                    using iterator_category = std::forward_iterator_tag;
+                    using value_type        = const char*;
+                    using difference_type   = std::ptrdiff_t;
+                    using pointer           = value_type*;
+                    using reference         = value_type&;
+
                     const_iterator(it_type it, it_type last) :
                         m_it(it),
                         m_last(last),
@@ -192,7 +199,7 @@ namespace osmium {
             struct StrComp {
 
                 bool operator()(const char* lhs, const char* rhs) const {
-                    return strcmp(lhs, rhs) < 0;
+                    return std::strcmp(lhs, rhs) < 0;
                 }
 
             }; // struct StrComp
diff --git a/include/osmium/io/detail/xml_input_format.hpp b/include/osmium/io/detail/xml_input_format.hpp
index 8cb5efe..0233917 100644
--- a/include/osmium/io/detail/xml_input_format.hpp
+++ b/include/osmium/io/detail/xml_input_format.hpp
@@ -192,6 +192,17 @@ namespace osmium {
                         static_cast<XMLParser*>(data)->characters(text, len);
                     }
 
+                    // This handler is called when there are any XML entities
+                    // declared in the OSM file. Entities are normally not used,
+                    // but they can be misused. See
+                    // https://en.wikipedia.org/wiki/Billion_laughs
+                    // The handler will just throw an error.
+                    static void entity_declaration_handler(void*,
+                            const XML_Char*, int, const XML_Char*, int, const XML_Char*,
+                            const XML_Char*, const XML_Char*, const XML_Char*) {
+                        throw osmium::xml_error("XML entities are not supported");
+                    }
+
                 public:
 
                     explicit ExpatXMLParser(T* callback_object) :
@@ -202,6 +213,7 @@ namespace osmium {
                         XML_SetUserData(m_parser, callback_object);
                         XML_SetElementHandler(m_parser, start_element_wrapper, end_element_wrapper);
                         XML_SetCharacterDataHandler(m_parser, character_data_wrapper);
+                        XML_SetEntityDeclHandler(m_parser, entity_declaration_handler);
                     }
 
                     ExpatXMLParser(const ExpatXMLParser&) = delete;
@@ -240,11 +252,11 @@ namespace osmium {
                     osmium::Location location;
 
                     check_attributes(attrs, [&location, &user, &object](const XML_Char* name, const XML_Char* value) {
-                        if (!strcmp(name, "lon")) {
+                        if (!std::strcmp(name, "lon")) {
                             location.set_lon(std::atof(value)); // XXX doesn't detect garbage after the number
-                        } else if (!strcmp(name, "lat")) {
+                        } else if (!std::strcmp(name, "lat")) {
                             location.set_lat(std::atof(value)); // XXX doesn't detect garbage after the number
-                        } else if (!strcmp(name, "user")) {
+                        } else if (!std::strcmp(name, "user")) {
                             user = value;
                         } else {
                             object.set_attribute(name, value);
@@ -265,15 +277,15 @@ namespace osmium {
                     osmium::Location min;
                     osmium::Location max;
                     check_attributes(attrs, [&min, &max, &user, &new_changeset](const XML_Char* name, const XML_Char* value) {
-                        if (!strcmp(name, "min_lon")) {
+                        if (!std::strcmp(name, "min_lon")) {
                             min.set_lon(atof(value));
-                        } else if (!strcmp(name, "min_lat")) {
+                        } else if (!std::strcmp(name, "min_lat")) {
                             min.set_lat(atof(value));
-                        } else if (!strcmp(name, "max_lon")) {
+                        } else if (!std::strcmp(name, "max_lon")) {
                             max.set_lon(atof(value));
-                        } else if (!strcmp(name, "max_lat")) {
+                        } else if (!std::strcmp(name, "max_lat")) {
                             max.set_lat(atof(value));
-                        } else if (!strcmp(name, "user")) {
+                        } else if (!std::strcmp(name, "user")) {
                             user = value;
                         } else {
                             new_changeset.set_attribute(name, value);
@@ -309,17 +321,17 @@ namespace osmium {
                 void start_element(const XML_Char* element, const XML_Char** attrs) {
                     switch (m_context) {
                         case context::root:
-                            if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) {
-                                if (!strcmp(element, "osmChange")) {
+                            if (!std::strcmp(element, "osm") || !std::strcmp(element, "osmChange")) {
+                                if (!std::strcmp(element, "osmChange")) {
                                     m_header.set_has_multiple_object_versions(true);
                                 }
                                 check_attributes(attrs, [this](const XML_Char* name, const XML_Char* value) {
-                                    if (!strcmp(name, "version")) {
+                                    if (!std::strcmp(name, "version")) {
                                         m_header.set("version", value);
-                                        if (strcmp(value, "0.6")) {
+                                        if (std::strcmp(value, "0.6")) {
                                             throw osmium::format_version_error(value);
                                         }
-                                    } else if (!strcmp(name, "generator")) {
+                                    } else if (!std::strcmp(name, "generator")) {
                                         m_header.set("generator", value);
                                     }
                                 });
@@ -333,7 +345,7 @@ namespace osmium {
                             break;
                         case context::top:
                             assert(!m_tl_builder);
-                            if (!strcmp(element, "node")) {
+                            if (!std::strcmp(element, "node")) {
                                 mark_header_as_done();
                                 if (read_types() & osmium::osm_entity_bits::node) {
                                     m_node_builder = std::unique_ptr<osmium::builder::NodeBuilder>(new osmium::builder::NodeBuilder(m_buffer));
@@ -342,7 +354,7 @@ namespace osmium {
                                 } else {
                                     m_context = context::ignored_node;
                                 }
-                            } else if (!strcmp(element, "way")) {
+                            } else if (!std::strcmp(element, "way")) {
                                 mark_header_as_done();
                                 if (read_types() & osmium::osm_entity_bits::way) {
                                     m_way_builder = std::unique_ptr<osmium::builder::WayBuilder>(new osmium::builder::WayBuilder(m_buffer));
@@ -351,7 +363,7 @@ namespace osmium {
                                 } else {
                                     m_context = context::ignored_way;
                                 }
-                            } else if (!strcmp(element, "relation")) {
+                            } else if (!std::strcmp(element, "relation")) {
                                 mark_header_as_done();
                                 if (read_types() & osmium::osm_entity_bits::relation) {
                                     m_relation_builder = std::unique_ptr<osmium::builder::RelationBuilder>(new osmium::builder::RelationBuilder(m_buffer));
@@ -360,7 +372,7 @@ namespace osmium {
                                 } else {
                                     m_context = context::ignored_relation;
                                 }
-                            } else if (!strcmp(element, "changeset")) {
+                            } else if (!std::strcmp(element, "changeset")) {
                                 mark_header_as_done();
                                 if (read_types() & osmium::osm_entity_bits::changeset) {
                                     m_changeset_builder = std::unique_ptr<osmium::builder::ChangesetBuilder>(new osmium::builder::ChangesetBuilder(m_buffer));
@@ -369,50 +381,56 @@ namespace osmium {
                                 } else {
                                     m_context = context::ignored_changeset;
                                 }
-                            } else if (!strcmp(element, "bounds")) {
+                            } else if (!std::strcmp(element, "bounds")) {
                                 osmium::Location min;
                                 osmium::Location max;
                                 check_attributes(attrs, [&min, &max](const XML_Char* name, const XML_Char* value) {
-                                    if (!strcmp(name, "minlon")) {
+                                    if (!std::strcmp(name, "minlon")) {
                                         min.set_lon(atof(value));
-                                    } else if (!strcmp(name, "minlat")) {
+                                    } else if (!std::strcmp(name, "minlat")) {
                                         min.set_lat(atof(value));
-                                    } else if (!strcmp(name, "maxlon")) {
+                                    } else if (!std::strcmp(name, "maxlon")) {
                                         max.set_lon(atof(value));
-                                    } else if (!strcmp(name, "maxlat")) {
+                                    } else if (!std::strcmp(name, "maxlat")) {
                                         max.set_lat(atof(value));
                                     }
                                 });
                                 osmium::Box box;
                                 box.extend(min).extend(max);
                                 m_header.add_box(box);
-                            } else if (!strcmp(element, "delete")) {
+                            } else if (!std::strcmp(element, "delete")) {
                                 m_in_delete_section = true;
                             }
                             break;
                         case context::node:
                             m_last_context = context::node;
                             m_context = context::in_object;
-                            if (!strcmp(element, "tag")) {
+                            if (!std::strcmp(element, "tag")) {
                                 get_tag(m_node_builder.get(), attrs);
                             }
                             break;
                         case context::way:
                             m_last_context = context::way;
                             m_context = context::in_object;
-                            if (!strcmp(element, "nd")) {
+                            if (!std::strcmp(element, "nd")) {
                                 m_tl_builder.reset();
 
                                 if (!m_wnl_builder) {
                                     m_wnl_builder = std::unique_ptr<osmium::builder::WayNodeListBuilder>(new osmium::builder::WayNodeListBuilder(m_buffer, m_way_builder.get()));
                                 }
 
-                                check_attributes(attrs, [this](const XML_Char* name, const XML_Char* value) {
-                                    if (!strcmp(name, "ref")) {
-                                        m_wnl_builder->add_node_ref(osmium::string_to_object_id(value));
+                                NodeRef nr;
+                                check_attributes(attrs, [this, &nr](const XML_Char* name, const XML_Char* value) {
+                                    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
+                                    } else if (!std::strcmp(name, "lat")) {
+                                        nr.location().set_lat(std::atof(value)); // XXX doesn't detect garbage after the number
                                     }
                                 });
-                            } else if (!strcmp(element, "tag")) {
+                                m_wnl_builder->add_node_ref(nr);
+                            } else if (!std::strcmp(element, "tag")) {
                                 m_wnl_builder.reset();
                                 get_tag(m_way_builder.get(), attrs);
                             }
@@ -420,7 +438,7 @@ namespace osmium {
                         case context::relation:
                             m_last_context = context::relation;
                             m_context = context::in_object;
-                            if (!strcmp(element, "member")) {
+                            if (!std::strcmp(element, "member")) {
                                 m_tl_builder.reset();
 
                                 if (!m_rml_builder) {
@@ -431,11 +449,11 @@ namespace osmium {
                                 object_id_type ref = 0;
                                 const char* role = "";
                                 check_attributes(attrs, [&type, &ref, &role](const XML_Char* name, const XML_Char* value) {
-                                    if (!strcmp(name, "type")) {
+                                    if (!std::strcmp(name, "type")) {
                                         type = char_to_item_type(value[0]);
-                                    } else if (!strcmp(name, "ref")) {
+                                    } else if (!std::strcmp(name, "ref")) {
                                         ref = osmium::string_to_object_id(value);
-                                    } else if (!strcmp(name, "role")) {
+                                    } else if (!std::strcmp(name, "role")) {
                                         role = static_cast<const char*>(value);
                                     }
                                 });
@@ -446,37 +464,37 @@ namespace osmium {
                                     throw osmium::xml_error("Missing ref on relation member");
                                 }
                                 m_rml_builder->add_member(type, ref, role);
-                            } else if (!strcmp(element, "tag")) {
+                            } else if (!std::strcmp(element, "tag")) {
                                 m_rml_builder.reset();
                                 get_tag(m_relation_builder.get(), attrs);
                             }
                             break;
                         case context::changeset:
                             m_last_context = context::changeset;
-                            if (!strcmp(element, "discussion")) {
+                            if (!std::strcmp(element, "discussion")) {
                                 m_context = context::discussion;
                                 m_tl_builder.reset();
                                 if (!m_changeset_discussion_builder) {
                                     m_changeset_discussion_builder = std::unique_ptr<osmium::builder::ChangesetDiscussionBuilder>(new osmium::builder::ChangesetDiscussionBuilder(m_buffer, m_changeset_builder.get()));
                                 }
-                            } else if (!strcmp(element, "tag")) {
+                            } else if (!std::strcmp(element, "tag")) {
                                 m_context = context::in_object;
                                 m_changeset_discussion_builder.reset();
                                 get_tag(m_changeset_builder.get(), attrs);
                             }
                             break;
                         case context::discussion:
-                            if (!strcmp(element, "comment")) {
+                            if (!std::strcmp(element, "comment")) {
                                 m_context = context::comment;
                                 osmium::Timestamp date;
                                 osmium::user_id_type uid = 0;
                                 const char* user = "";
                                 check_attributes(attrs, [&date, &uid, &user](const XML_Char* name, const XML_Char* value) {
-                                    if (!strcmp(name, "date")) {
+                                    if (!std::strcmp(name, "date")) {
                                         date = osmium::Timestamp(value);
-                                    } else if (!strcmp(name, "uid")) {
+                                    } else if (!std::strcmp(name, "uid")) {
                                         uid = osmium::string_to_user_id(value);
-                                    } else if (!strcmp(name, "user")) {
+                                    } else if (!std::strcmp(name, "user")) {
                                         user = static_cast<const char*>(value);
                                     }
                                 });
@@ -484,7 +502,7 @@ namespace osmium {
                             }
                             break;
                         case context::comment:
-                            if (!strcmp(element, "text")) {
+                            if (!std::strcmp(element, "text")) {
                                 m_context = context::comment_text;
                             }
                             break;
@@ -510,15 +528,15 @@ namespace osmium {
                             assert(false); // should never be here
                             break;
                         case context::top:
-                            if (!strcmp(element, "osm") || !strcmp(element, "osmChange")) {
+                            if (!std::strcmp(element, "osm") || !std::strcmp(element, "osmChange")) {
                                 mark_header_as_done();
                                 m_context = context::root;
-                            } else if (!strcmp(element, "delete")) {
+                            } else if (!std::strcmp(element, "delete")) {
                                 m_in_delete_section = false;
                             }
                             break;
                         case context::node:
-                            assert(!strcmp(element, "node"));
+                            assert(!std::strcmp(element, "node"));
                             m_tl_builder.reset();
                             m_node_builder.reset();
                             m_buffer.commit();
@@ -526,7 +544,7 @@ namespace osmium {
                             flush_buffer();
                             break;
                         case context::way:
-                            assert(!strcmp(element, "way"));
+                            assert(!std::strcmp(element, "way"));
                             m_tl_builder.reset();
                             m_wnl_builder.reset();
                             m_way_builder.reset();
@@ -535,7 +553,7 @@ namespace osmium {
                             flush_buffer();
                             break;
                         case context::relation:
-                            assert(!strcmp(element, "relation"));
+                            assert(!std::strcmp(element, "relation"));
                             m_tl_builder.reset();
                             m_rml_builder.reset();
                             m_relation_builder.reset();
@@ -544,7 +562,7 @@ namespace osmium {
                             flush_buffer();
                             break;
                         case context::changeset:
-                            assert(!strcmp(element, "changeset"));
+                            assert(!std::strcmp(element, "changeset"));
                             m_tl_builder.reset();
                             m_changeset_discussion_builder.reset();
                             m_changeset_builder.reset();
@@ -553,15 +571,15 @@ namespace osmium {
                             flush_buffer();
                             break;
                         case context::discussion:
-                            assert(!strcmp(element, "discussion"));
+                            assert(!std::strcmp(element, "discussion"));
                             m_context = context::changeset;
                             break;
                         case context::comment:
-                            assert(!strcmp(element, "comment"));
+                            assert(!std::strcmp(element, "comment"));
                             m_context = context::discussion;
                             break;
                         case context::comment_text:
-                            assert(!strcmp(element, "text"));
+                            assert(!std::strcmp(element, "text"));
                             m_context = context::comment;
                             m_changeset_discussion_builder->add_comment_text(m_comment_text);
                             break;
@@ -569,22 +587,22 @@ namespace osmium {
                             m_context = m_last_context;
                             break;
                         case context::ignored_node:
-                            if (!strcmp(element, "node")) {
+                            if (!std::strcmp(element, "node")) {
                                 m_context = context::top;
                             }
                             break;
                         case context::ignored_way:
-                            if (!strcmp(element, "way")) {
+                            if (!std::strcmp(element, "way")) {
                                 m_context = context::top;
                             }
                             break;
                         case context::ignored_relation:
-                            if (!strcmp(element, "relation")) {
+                            if (!std::strcmp(element, "relation")) {
                                 m_context = context::top;
                             }
                             break;
                         case context::ignored_changeset:
-                            if (!strcmp(element, "changeset")) {
+                            if (!std::strcmp(element, "changeset")) {
                                 m_context = context::top;
                             }
                             break;
diff --git a/include/osmium/io/detail/xml_output_format.hpp b/include/osmium/io/detail/xml_output_format.hpp
index 09bd6b3..ee58457 100644
--- a/include/osmium/io/detail/xml_output_format.hpp
+++ b/include/osmium/io/detail/xml_output_format.hpp
@@ -86,6 +86,8 @@ namespace osmium {
                  */
                 bool use_change_ops;
 
+                /// Should node locations be added to ways?
+                bool locations_on_ways;
             };
 
             class XMLOutputBlock : public OutputBlock {
@@ -287,9 +289,24 @@ namespace osmium {
 
                     *m_out += ">\n";
 
-                    for (const auto& node_ref : way.nodes()) {
-                        write_prefix();
-                        output_formatted("  <nd ref=\"%" PRId64 "\"/>\n", node_ref.ref());
+                    if (m_options.locations_on_ways) {
+                        for (const auto& node_ref : way.nodes()) {
+                            write_prefix();
+                            output_formatted("  <nd ref=\"%" PRId64 "\"", 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 += "\"";
+                            }
+                            *m_out += "/>\n";
+                        }
+                    } else {
+                        for (const auto& node_ref : way.nodes()) {
+                            write_prefix();
+                            output_formatted("  <nd ref=\"%" PRId64 "\"/>\n", node_ref.ref());
+                        }
                     }
 
                     write_tags(way.tags(), prefix_spaces());
@@ -394,9 +411,10 @@ namespace osmium {
                 XMLOutputFormat(const osmium::io::File& file, future_string_queue_type& output_queue) :
                     OutputFormat(output_queue),
                     m_options() {
-                    m_options.add_metadata     = file.is_not_false("add_metadata");
-                    m_options.use_change_ops   = file.is_true("xml_change_format");
-                    m_options.add_visible_flag = (file.has_multiple_object_versions() || file.is_true("force_visible_flag")) && !m_options.use_change_ops;
+                    m_options.add_metadata      = file.is_not_false("add_metadata");
+                    m_options.use_change_ops    = file.is_true("xml_change_format");
+                    m_options.add_visible_flag  = (file.has_multiple_object_versions() || file.is_true("force_visible_flag")) && !m_options.use_change_ops;
+                    m_options.locations_on_ways = file.is_true("locations_on_ways");
                 }
 
                 XMLOutputFormat(const XMLOutputFormat&) = delete;
diff --git a/include/osmium/io/input_iterator.hpp b/include/osmium/io/input_iterator.hpp
index 8be9759..a66f355 100644
--- a/include/osmium/io/input_iterator.hpp
+++ b/include/osmium/io/input_iterator.hpp
@@ -57,7 +57,7 @@ namespace osmium {
 
             static_assert(std::is_base_of<osmium::memory::Item, TItem>::value, "TItem must derive from osmium::buffer::Item");
 
-            typedef typename osmium::memory::Buffer::t_iterator<TItem> item_iterator;
+            using item_iterator = typename osmium::memory::Buffer::t_iterator<TItem>;
 
             TSource* m_source;
             std::shared_ptr<osmium::memory::Buffer> m_buffer;
@@ -69,20 +69,20 @@ namespace osmium {
                     if (!m_buffer || !*m_buffer) { // end of input
                         m_source = nullptr;
                         m_buffer.reset();
-                        m_iter = item_iterator();
+                        m_iter = item_iterator{};
                         return;
                     }
-                    m_iter = m_buffer->begin<TItem>();
-                } while (m_iter == m_buffer->end<TItem>());
+                    m_iter = m_buffer->select<TItem>().begin();
+                } while (m_iter == m_buffer->select<TItem>().end());
             }
 
         public:
 
-            typedef std::input_iterator_tag iterator_category;
-            typedef TItem                   value_type;
-            typedef ptrdiff_t               difference_type;
-            typedef TItem*                  pointer;
-            typedef TItem&                  reference;
+            using iterator_category = std::input_iterator_tag;
+            using value_type        = TItem;
+            using difference_type   = ptrdiff_t;
+            using pointer           = value_type*;
+            using reference         = value_type&;
 
             explicit InputIterator(TSource& source) :
                 m_source(&source) {
@@ -99,7 +99,7 @@ namespace osmium {
                 assert(m_buffer);
                 assert(m_iter);
                 ++m_iter;
-                if (m_iter == m_buffer->end<TItem>()) {
+                if (m_iter == m_buffer->select<TItem>().end()) {
                     update_buffer();
                 }
                 return *this;
diff --git a/include/osmium/io/output_iterator.hpp b/include/osmium/io/output_iterator.hpp
index ce050eb..f7025fc 100644
--- a/include/osmium/io/output_iterator.hpp
+++ b/include/osmium/io/output_iterator.hpp
@@ -51,12 +51,18 @@ namespace osmium {
     namespace io {
 
         template <typename TDest>
-        class OutputIterator : public std::iterator<std::output_iterator_tag, osmium::memory::Item> {
+        class OutputIterator {
 
             TDest* m_destination;
 
         public:
 
+            using iterator_category = std::output_iterator_tag;
+            using value_type        = void;
+            using difference_type   = void;
+            using pointer           = void;
+            using reference         = void;
+
             explicit OutputIterator(TDest& destination) :
                 m_destination(&destination) {
             }
diff --git a/include/osmium/memory/buffer.hpp b/include/osmium/memory/buffer.hpp
index 07a31fe..8c246db 100644
--- a/include/osmium/memory/buffer.hpp
+++ b/include/osmium/memory/buffer.hpp
@@ -517,6 +517,16 @@ namespace osmium {
              */
             using const_iterator = t_const_iterator<osmium::OSMEntity>;
 
+            template <typename T>
+            ItemIteratorRange<T> select() {
+                return ItemIteratorRange<T>{m_data, m_data + m_committed};
+            }
+
+            template <typename T>
+            ItemIteratorRange<const T> select() const {
+                return ItemIteratorRange<const T>{m_data, m_data + m_committed};
+            }
+
             /**
              * Get iterator for iterating over all items of type T in the
              * buffer.
diff --git a/include/osmium/memory/collection.hpp b/include/osmium/memory/collection.hpp
index 17ace70..2a2c040 100644
--- a/include/osmium/memory/collection.hpp
+++ b/include/osmium/memory/collection.hpp
@@ -44,17 +44,23 @@ namespace osmium {
     namespace memory {
 
         template <typename TMember>
-        class CollectionIterator : public std::iterator<std::forward_iterator_tag, TMember> {
+        class CollectionIterator {
 
             // This data_type is either 'unsigned char*' or 'const unsigned char*' depending
             // on whether TMember is const. This allows this class to be used as an iterator and
             // as a const_iterator.
-            typedef typename std::conditional<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type data_type;
+            using data_type = typename std::conditional<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type;
 
             data_type m_data;
 
         public:
 
+            using iterator_category = std::forward_iterator_tag;
+            using value_type        = TMember;
+            using difference_type   = std::ptrdiff_t;
+            using pointer           = value_type*;
+            using reference         = value_type&;
+
             CollectionIterator() noexcept :
                 m_data(nullptr) {
             }
@@ -112,9 +118,9 @@ namespace osmium {
 
         public:
 
-            typedef CollectionIterator<TMember> iterator;
-            typedef CollectionIterator<const TMember> const_iterator;
-            typedef TMember value_type;
+            using iterator       = CollectionIterator<TMember>;
+            using const_iterator = CollectionIterator<const TMember>;
+            using value_type     = TMember;
 
             static constexpr osmium::item_type itemtype = TCollectionItemType;
 
diff --git a/include/osmium/memory/item.hpp b/include/osmium/memory/item.hpp
index fd404ce..0d550fb 100644
--- a/include/osmium/memory/item.hpp
+++ b/include/osmium/memory/item.hpp
@@ -47,7 +47,7 @@ namespace osmium {
 
     namespace memory {
 
-        typedef uint32_t item_size_type;
+        using item_size_type = uint32_t;
 
         // align datastructures to this many bytes
         constexpr item_size_type align_bytes = 8;
diff --git a/include/osmium/memory/item_iterator.hpp b/include/osmium/memory/item_iterator.hpp
index 3886c98..df72999 100644
--- a/include/osmium/memory/item_iterator.hpp
+++ b/include/osmium/memory/item_iterator.hpp
@@ -116,19 +116,19 @@ namespace osmium {
         } // namespace detail
 
         template <typename TMember>
-        class ItemIterator : public std::iterator<std::forward_iterator_tag, TMember> {
+        class ItemIterator {
 
             static_assert(std::is_base_of<osmium::memory::Item, TMember>::value, "TMember must derive from osmium::memory::Item");
 
             // This data_type is either 'unsigned char*' or 'const unsigned char*' depending
             // on whether TMember is const. This allows this class to be used as an iterator and
             // as a const_iterator.
-            typedef typename std::conditional<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type data_type;
+            using data_type = typename std::conditional<std::is_const<TMember>::value, const unsigned char*, unsigned char*>::type;
 
             data_type m_data;
             data_type m_end;
 
-            void advance_to_next_item_of_right_type() {
+            void advance_to_next_item_of_right_type() noexcept {
                 while (m_data != m_end &&
                        !detail::type_is_compatible<typename std::remove_const<TMember>::type>(reinterpret_cast<const osmium::memory::Item*>(m_data)->type())) {
                     m_data = reinterpret_cast<TMember*>(m_data)->next();
@@ -137,23 +137,29 @@ namespace osmium {
 
         public:
 
+            using iterator_category = std::forward_iterator_tag;
+            using value_type        = TMember;
+            using difference_type   = std::ptrdiff_t;
+            using pointer           = value_type*;
+            using reference         = value_type&;
+
             ItemIterator() noexcept :
                 m_data(nullptr),
                 m_end(nullptr) {
             }
 
-            ItemIterator(data_type data, data_type end) :
+            ItemIterator(data_type data, data_type end) noexcept :
                 m_data(data),
                 m_end(end) {
                 advance_to_next_item_of_right_type();
             }
 
             template <typename T>
-            ItemIterator<T> cast() const {
+            ItemIterator<T> cast() const noexcept {
                 return ItemIterator<T>(m_data, m_end);
             }
 
-            ItemIterator<TMember>& operator++() {
+            ItemIterator<TMember>& operator++() noexcept {
                 assert(m_data);
                 assert(m_data != m_end);
                 m_data = reinterpret_cast<TMember*>(m_data)->next();
@@ -166,45 +172,50 @@ namespace osmium {
              * types. Do not use this unless you know what you are
              * doing.
              */
-            ItemIterator<TMember>& advance_once() {
+            ItemIterator<TMember>& advance_once() noexcept {
                 assert(m_data);
                 assert(m_data != m_end);
                 m_data = reinterpret_cast<TMember*>(m_data)->next();
                 return *static_cast<ItemIterator<TMember>*>(this);
             }
 
-            ItemIterator<TMember> operator++(int) {
+            ItemIterator<TMember> operator++(int) noexcept {
                 ItemIterator<TMember> tmp(*this);
                 operator++();
                 return tmp;
             }
 
-            bool operator==(const ItemIterator<TMember>& rhs) const {
+            bool operator==(const ItemIterator<TMember>& rhs) const noexcept {
                 return m_data == rhs.m_data && m_end == rhs.m_end;
             }
 
-            bool operator!=(const ItemIterator<TMember>& rhs) const {
+            bool operator!=(const ItemIterator<TMember>& rhs) const noexcept {
                 return !(*this == rhs);
             }
 
-            unsigned char* data() const {
+            data_type data() noexcept {
+                assert(m_data);
+                return m_data;
+            }
+
+            const unsigned char* data() const noexcept {
                 assert(m_data);
                 return m_data;
             }
 
-            TMember& operator*() const {
+            TMember& operator*() const noexcept {
                 assert(m_data);
                 assert(m_data != m_end);
                 return *reinterpret_cast<TMember*>(m_data);
             }
 
-            TMember* operator->() const {
+            TMember* operator->() const noexcept {
                 assert(m_data);
                 assert(m_data != m_end);
                 return reinterpret_cast<TMember*>(m_data);
             }
 
-            explicit operator bool() const {
+            explicit operator bool() const noexcept {
                 return (m_data != nullptr) && (m_data != m_end);
             }
 
@@ -221,6 +232,77 @@ namespace osmium {
             return out;
         }
 
+        template <typename T>
+        class ItemIteratorRange {
+
+            static_assert(std::is_base_of<osmium::memory::Item, T>::value, "Template parameter must derive from osmium::memory::Item");
+
+            // This data_type is either 'unsigned char*' or
+            // 'const unsigned char*' depending on whether T is const.
+            using data_type = typename std::conditional<std::is_const<T>::value, const unsigned char*, unsigned char*>::type;
+
+            data_type m_begin;
+            data_type m_end;
+
+        public:
+
+            using iterator = ItemIterator<T>;
+            using const_iterator = ItemIterator<const T>;
+
+            ItemIteratorRange(data_type first, data_type last) noexcept :
+                m_begin(first),
+                m_end(last) {
+            }
+
+            iterator begin() noexcept {
+                return iterator{m_begin, m_end};
+            }
+
+            iterator end() noexcept {
+                return iterator{m_end, m_end};
+            }
+
+            const_iterator cbegin() const noexcept {
+                return const_iterator{m_begin, m_end};
+            }
+
+            const_iterator cend() const noexcept {
+                return const_iterator{m_end, m_end};
+            }
+
+            const_iterator begin() const noexcept {
+                return cbegin();
+            }
+
+            const_iterator end() const noexcept {
+                return cend();
+            }
+
+            /**
+             * Return the number of items in this range.
+             *
+             * Note that this methods has worst-case complexity O(n) with n
+             * being the number of items in the underlying range.
+             */
+            size_t size() const {
+                if (m_begin == m_end) {
+                    return 0;
+                }
+                return std::distance(cbegin(), cend());
+            }
+
+            /**
+             * Is this range empty?
+             *
+             * Note that this methods has worst-case complexity O(n) with n
+             * being the number of items in the underlying range.
+             */
+            bool empty() const {
+                return size() == 0;
+            }
+
+        }; // class ItemIteratorRange
+
     } // namespace memory
 
 } // namespace osmium
diff --git a/include/osmium/object_pointer_collection.hpp b/include/osmium/object_pointer_collection.hpp
index 09a5293..ce77bd2 100644
--- a/include/osmium/object_pointer_collection.hpp
+++ b/include/osmium/object_pointer_collection.hpp
@@ -70,8 +70,8 @@ namespace osmium {
 
     public:
 
-        typedef boost::indirect_iterator<std::vector<osmium::OSMObject*>::iterator, osmium::OSMObject> iterator;
-        typedef boost::indirect_iterator<std::vector<osmium::OSMObject*>::const_iterator, const osmium::OSMObject> const_iterator;
+        using iterator       = boost::indirect_iterator<std::vector<osmium::OSMObject*>::iterator, osmium::OSMObject>;
+        using const_iterator = boost::indirect_iterator<std::vector<osmium::OSMObject*>::const_iterator, const osmium::OSMObject>;
 
         ObjectPointerCollection() noexcept :
             m_objects() {
diff --git a/include/osmium/osm/area.hpp b/include/osmium/osm/area.hpp
index ee232f0..858c90d 100644
--- a/include/osmium/osm/area.hpp
+++ b/include/osmium/osm/area.hpp
@@ -43,6 +43,7 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/osm/object.hpp>
 #include <osmium/osm/types.hpp>
 #include <osmium/osm/node_ref_list.hpp>
+#include <osmium/util/compatibility.hpp>
 
 namespace osmium {
 
@@ -144,8 +145,8 @@ namespace osmium {
          *
          * @returns Pair (number outer rings, number inner rings)
          */
-        std::pair<int, int> num_rings() const {
-            std::pair<int, int> counter { 0, 0 };
+        std::pair<size_t, size_t> num_rings() const {
+            std::pair<size_t, size_t> counter { 0, 0 };
 
             for (auto it = cbegin(); it != cend(); ++it) {
                 switch (it->type()) {
@@ -185,27 +186,51 @@ namespace osmium {
         }
 
         /**
+         * @deprecated Use inner_rings() instead.
+         *
          * 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 {
+        OSMIUM_DEPRECATED osmium::memory::ItemIterator<const osmium::InnerRing> inner_ring_cbegin(const osmium::memory::ItemIterator<const osmium::OuterRing>& it) const {
             return it.cast<const osmium::InnerRing>();
         }
 
         /**
+         * @deprecated Use inner_rings() instead.
+         *
          * 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 {
+        OSMIUM_DEPRECATED 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>();
         }
 
+        /**
+         * Return an iterator range for all outer rings.
+         * You can use the usual begin() and end() functions to iterate over
+         * all outer rings.
+         */
+        osmium::memory::ItemIteratorRange<const osmium::OuterRing> outer_rings() const {
+            return subitems<const osmium::OuterRing>();
+        }
+
+        /**
+         * Return an iterator range for all inner rings in the given outer
+         * ring.
+         * You can use the usual begin() and end() functions to iterate over
+         * all outer rings.
+         */
+        osmium::memory::ItemIteratorRange<const osmium::InnerRing> inner_rings(const osmium::OuterRing& outer) const {
+            osmium::memory::ItemIteratorRange<const osmium::OuterRing> outer_range{outer.data(), next()};
+            return osmium::memory::ItemIteratorRange<const osmium::InnerRing>{outer_range.cbegin().data(), std::next(outer_range.cbegin()).data()};
+        }
+
     }; // class Area
 
     static_assert(sizeof(Area) % osmium::memory::align_bytes == 0, "Class osmium::Area has wrong size to be aligned properly!");
diff --git a/include/osmium/osm/changeset.hpp b/include/osmium/osm/changeset.hpp
index f59db48..0ec994c 100644
--- a/include/osmium/osm/changeset.hpp
+++ b/include/osmium/osm/changeset.hpp
@@ -131,7 +131,7 @@ namespace osmium {
 
     public:
 
-        typedef size_t size_type;
+        using size_type = size_t;
 
         ChangesetDiscussion() :
             osmium::memory::Collection<ChangesetComment, osmium::item_type::changeset_discussion>() {
@@ -369,23 +369,23 @@ namespace osmium {
          * @param value Value of the attribute
          */
         void set_attribute(const char* attr, const char* value) {
-            if (!strcmp(attr, "id")) {
+            if (!std::strcmp(attr, "id")) {
                 set_id(value);
-            } else if (!strcmp(attr, "num_changes")) {
+            } else if (!std::strcmp(attr, "num_changes")) {
                 set_num_changes(value);
-            } else if (!strcmp(attr, "comments_count")) {
+            } else if (!std::strcmp(attr, "comments_count")) {
                 set_num_comments(value);
-            } else if (!strcmp(attr, "created_at")) {
+            } else if (!std::strcmp(attr, "created_at")) {
                 set_created_at(osmium::Timestamp(value));
-            } else if (!strcmp(attr, "closed_at")) {
+            } else if (!std::strcmp(attr, "closed_at")) {
                 set_closed_at(osmium::Timestamp(value));
-            } else if (!strcmp(attr, "uid")) {
+            } else if (!std::strcmp(attr, "uid")) {
                 set_uid(value);
             }
         }
 
-        typedef osmium::memory::CollectionIterator<Item> iterator;
-        typedef osmium::memory::CollectionIterator<const Item> const_iterator;
+        using iterator       = osmium::memory::CollectionIterator<Item>;
+        using const_iterator = osmium::memory::CollectionIterator<const Item>;
 
         iterator begin() {
             return iterator(subitems_position());
diff --git a/include/osmium/osm/crc.hpp b/include/osmium/osm/crc.hpp
index ff39996..e7c233a 100644
--- a/include/osmium/osm/crc.hpp
+++ b/include/osmium/osm/crc.hpp
@@ -151,6 +151,7 @@ namespace osmium {
 
         void update(const NodeRef& node_ref) {
             update_int64(node_ref.ref());
+            update(node_ref.location());
         }
 
         void update(const NodeRefList& node_refs) {
diff --git a/include/osmium/osm/entity_bits.hpp b/include/osmium/osm/entity_bits.hpp
index 50b3e4c..b8e9ddb 100644
--- a/include/osmium/osm/entity_bits.hpp
+++ b/include/osmium/osm/entity_bits.hpp
@@ -33,12 +33,15 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
+#include <cassert>
+#include <type_traits>
+
 #include <osmium/osm/item_type.hpp>
 
 namespace osmium {
 
     /**
-     * @brief Bitfield for OSM entity types.
+     * @brief Bit field for OSM entity types.
      */
     namespace osm_entity_bits {
 
@@ -94,8 +97,19 @@ namespace osmium {
             return lhs;
         }
 
+        /**
+         * Get entity_bits from item_type.
+         *
+         * @pre item_type must be undefined, node, way, relation, area, or
+         *      changeset.
+         */
         inline type from_item_type(osmium::item_type item_type) noexcept {
-            return static_cast<osmium::osm_entity_bits::type>(0x1 << (static_cast<uint16_t>(item_type) - 1));
+            auto ut = static_cast<std::underlying_type<osmium::item_type>::type>(item_type);
+            assert(ut <= 0x05);
+            if (ut == 0) {
+                return nothing;
+            }
+            return static_cast<osmium::osm_entity_bits::type>(0x1 << (ut - 1));
         }
 
     } // namespace osm_entity_bits
diff --git a/include/osmium/osm/location.hpp b/include/osmium/osm/location.hpp
index 85f4b16..2949a90 100644
--- a/include/osmium/osm/location.hpp
+++ b/include/osmium/osm/location.hpp
@@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE.
 
 #include <cmath>
 #include <cstdint>
+#include <functional>
 #include <iosfwd>
 #include <stdexcept>
 #include <string>
@@ -273,13 +274,52 @@ namespace osmium {
     template <typename TChar, typename TTraits>
     inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const osmium::Location& location) {
         if (location) {
-            out << '(' << location.lon() << ',' << location.lat() << ')';
+            out << '(';
+            location.as_string(std::ostream_iterator<char>(out), ',');
+            out << ')';
         } else {
             out << "(undefined,undefined)";
         }
         return out;
     }
 
+    namespace detail {
+
+        template <int N>
+        inline size_t hash(const osmium::Location& location) noexcept {
+            return location.x() ^ location.y();
+        }
+
+        template <>
+        inline size_t hash<8>(const osmium::Location& location) noexcept {
+            size_t h = location.x();
+            h <<= 32;
+            return h ^ location.y();
+        }
+
+    } // namespace detail
+
 } // namespace osmium
 
+namespace std {
+
+// This pragma is a workaround for a bug in an old libc implementation
+#ifdef __clang__
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wmismatched-tags"
+#endif
+    template <>
+    struct hash<osmium::Location> {
+        using argument_type = osmium::Location;
+        using result_type = size_t;
+        size_t operator()(const osmium::Location& location) const noexcept {
+            return osmium::detail::hash<sizeof(size_t)>(location);
+        }
+    };
+#ifdef __clang__
+#pragma clang diagnostic pop
+#endif
+
+} // namespace std
+
 #endif // OSMIUM_OSM_LOCATION_HPP
diff --git a/include/osmium/osm/object.hpp b/include/osmium/osm/object.hpp
index 6d1de6f..a373156 100644
--- a/include/osmium/osm/object.hpp
+++ b/include/osmium/osm/object.hpp
@@ -38,6 +38,7 @@ DEALINGS IN THE SOFTWARE.
 #include <cstdlib>
 #include <cstring>
 #include <stdexcept>
+#include <tuple>
 
 #include <osmium/memory/collection.hpp>
 #include <osmium/memory/item.hpp>
@@ -49,6 +50,7 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/osm/timestamp.hpp>
 #include <osmium/osm/types.hpp>
 #include <osmium/osm/types_from_string.hpp>
+#include <osmium/util/misc.hpp>
 
 namespace osmium {
 
@@ -172,9 +174,9 @@ namespace osmium {
          * @returns Reference to object to make calls chainable.
          */
         OSMObject& set_visible(const char* visible) {
-            if (!strcmp("true", visible)) {
+            if (!std::strcmp("true", visible)) {
                 set_visible(true);
-            } else if (!strcmp("false", visible)) {
+            } else if (!std::strcmp("false", visible)) {
                 set_visible(false);
             } else {
                 throw std::invalid_argument("Unknown value for visible attribute (allowed is 'true' or 'false')");
@@ -313,23 +315,23 @@ namespace osmium {
          * @param value Value of the attribute
          */
         void set_attribute(const char* attr, const char* value) {
-            if (!strcmp(attr, "id")) {
+            if (!std::strcmp(attr, "id")) {
                 set_id(value);
-            } else if (!strcmp(attr, "version")) {
+            } else if (!std::strcmp(attr, "version")) {
                 set_version(value);
-            } else if (!strcmp(attr, "changeset")) {
+            } else if (!std::strcmp(attr, "changeset")) {
                 set_changeset(value);
-            } else if (!strcmp(attr, "timestamp")) {
+            } else if (!std::strcmp(attr, "timestamp")) {
                 set_timestamp(osmium::Timestamp(value));
-            } else if (!strcmp(attr, "uid")) {
+            } else if (!std::strcmp(attr, "uid")) {
                 set_uid(value);
-            } else if (!strcmp(attr, "visible")) {
+            } else if (!std::strcmp(attr, "visible")) {
                 set_visible(value);
             }
         }
 
-        typedef osmium::memory::CollectionIterator<Item> iterator;
-        typedef osmium::memory::CollectionIterator<const Item> const_iterator;
+        using iterator       = osmium::memory::CollectionIterator<Item>;
+        using const_iterator = osmium::memory::CollectionIterator<const Item>;
 
         iterator begin() {
             return iterator(subitems_position());
@@ -355,6 +357,26 @@ namespace osmium {
             return cend();
         }
 
+        /**
+         * Get a range of subitems of a specific type.
+         *
+         * @tparam The type (must be derived from osmium::memory::Item.
+         */
+        template <typename T>
+        osmium::memory::ItemIteratorRange<T> subitems() {
+            return osmium::memory::ItemIteratorRange<T>{subitems_position(), next()};
+        }
+
+        /**
+         * Get a range of subitems of a specific type.
+         *
+         * @tparam The type (must be derived from osmium::memory::Item.
+         */
+        template <typename T>
+        osmium::memory::ItemIteratorRange<const T> subitems() const {
+            return osmium::memory::ItemIteratorRange<const T>{subitems_position(), next()};
+        }
+
         template <typename T>
         using t_iterator = osmium::memory::ItemIterator<T>;
 
@@ -399,8 +421,8 @@ namespace osmium {
      * OSMObjects are equal if their type, id, and version are equal.
      */
     inline bool operator==(const OSMObject& lhs, const OSMObject& rhs) noexcept {
-        return lhs.type() == rhs.type() &&
-               lhs.id() == rhs.id() &&
+        return lhs.type()    == rhs.type() &&
+               lhs.id()      == rhs.id()   &&
                lhs.version() == rhs.version();
     }
 
@@ -409,16 +431,22 @@ namespace osmium {
     }
 
     /**
-     * OSMObjects can be ordered by type, id and version.
-     * Note that we use the absolute value of the id for a
-     * better ordering of objects with negative id.
+     * OSMObjects can be ordered by type, id, version, and timestamp. Usually
+     * ordering by timestamp is not necessary as there shouldn't be two
+     * objects with the same type, id, and version. But this can happen when
+     * creating diff files from extracts, so we take the timestamp into
+     * account  here.
+     *
+     * Note that we use the absolute value of the id for a better ordering
+     * of objects with negative id. If the IDs have the same absolute value,
+     * the positive ID comes first.
+     *
+     * See object_order_type_id_reverse_version if you need a different
+     * ordering.
      */
     inline bool operator<(const OSMObject& lhs, const OSMObject& rhs) noexcept {
-        if (lhs.type() != rhs.type()) {
-            return lhs.type() < rhs.type();
-        }
-        return (lhs.id() == rhs.id() && lhs.version() < rhs.version()) ||
-               lhs.positive_id() < rhs.positive_id();
+        return const_tie(lhs.type(), lhs.positive_id(), lhs.id() < 0, lhs.version(), lhs.timestamp()) <
+               const_tie(rhs.type(), rhs.positive_id(), rhs.id() < 0, rhs.version(), rhs.timestamp());
     }
 
     inline bool operator>(const OSMObject& lhs, const OSMObject& rhs) noexcept {
diff --git a/include/osmium/osm/object_comparisons.hpp b/include/osmium/osm/object_comparisons.hpp
index fe3529b..a17d47d 100644
--- a/include/osmium/osm/object_comparisons.hpp
+++ b/include/osmium/osm/object_comparisons.hpp
@@ -34,11 +34,13 @@ DEALINGS IN THE SOFTWARE.
 */
 
 #include <osmium/osm/object.hpp>
+#include <osmium/util/misc.hpp>
 
 namespace osmium {
 
     /**
-     * Function object class for comparing OSM objects for equality by type, id, and version.
+     * Function object class for comparing OSM objects for equality by type,
+     * id, and version.
      */
     struct object_equal_type_id_version {
 
@@ -53,8 +55,8 @@ namespace osmium {
     }; // struct object_equal_type_id_version
 
     /**
-     * Function object class for comparing OSM objects for equality by type and id,
-     * ignoring the version.
+     * Function object class for comparing OSM objects for equality by type
+     * and id, ignoring the version.
      */
     struct object_equal_type_id {
 
@@ -70,7 +72,8 @@ namespace osmium {
     }; // struct object_equal_type_id
 
     /**
-     * Function object class for ordering OSM objects by type, id, and version.
+     * Function object class for ordering OSM objects by type, id, version,
+     * and timestamp.
      */
     struct object_order_type_id_version {
 
@@ -85,18 +88,17 @@ namespace osmium {
     }; // struct object_order_type_id_version
 
     /**
-     * Function object class for ordering OSM objects by type, id, and reverse version,
-     * ie objects are ordered by type and id, but later versions of an object are
-     * ordered before earlier versions of the same object.
+     * Function object class for ordering OSM objects by type, id, and
+     * reverse version, timestamp. So objects are ordered by type and id, but
+     * later versions of an object are ordered before earlier versions of the
+     * same object. This is useful when the last version of an object needs
+     * to be used.
      */
     struct object_order_type_id_reverse_version {
 
         bool operator()(const osmium::OSMObject& lhs, const osmium::OSMObject& rhs) const noexcept {
-            if (lhs.type() != rhs.type()) {
-                return lhs.type() < rhs.type();
-            }
-            return (lhs.id() == rhs.id() && lhs.version() > rhs.version()) ||
-                   lhs.positive_id() < rhs.positive_id();
+            return const_tie(lhs.type(), lhs.id() < 0, lhs.positive_id(), rhs.version(), rhs.timestamp()) <
+                   const_tie(rhs.type(), rhs.id() < 0, rhs.positive_id(), lhs.version(), lhs.timestamp());
         }
 
         bool operator()(const osmium::OSMObject* lhs, const osmium::OSMObject* rhs) const noexcept {
diff --git a/include/osmium/osm/relation.hpp b/include/osmium/osm/relation.hpp
index 9c4e69c..1ea0a5e 100644
--- a/include/osmium/osm/relation.hpp
+++ b/include/osmium/osm/relation.hpp
@@ -149,7 +149,7 @@ namespace osmium {
 
     public:
 
-        typedef size_t size_type;
+        using size_type = size_t;
 
         RelationMemberList() :
             osmium::memory::Collection<RelationMember, osmium::item_type::relation_member_list>() {
diff --git a/include/osmium/osm/tag.hpp b/include/osmium/osm/tag.hpp
index 3f1a298..d5415f7 100644
--- a/include/osmium/osm/tag.hpp
+++ b/include/osmium/osm/tag.hpp
@@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE.
 */
 
 #include <algorithm>
+#include <cassert>
 #include <cstddef>
 #include <cstring>
 #include <iosfwd>
@@ -87,7 +88,7 @@ namespace osmium {
     }; // class Tag
 
     inline bool operator==(const Tag& a, const Tag& b) {
-        return !std::strcmp(a.key(), b.key()) && !strcmp(a.value(), b.value());
+        return !std::strcmp(a.key(), b.key()) && !std::strcmp(a.value(), b.value());
     }
 
     inline bool operator<(const Tag& a, const Tag& b) {
@@ -104,32 +105,72 @@ namespace osmium {
 
     class TagList : public osmium::memory::Collection<Tag, osmium::item_type::tag_list> {
 
+        const_iterator find_key(const char* key) const noexcept {
+            return std::find_if(cbegin(), cend(), [key](const Tag& tag) {
+                return !std::strcmp(tag.key(), key);
+            });
+        }
+
     public:
 
-        typedef size_t size_type;
+        using size_type = size_t;
 
         TagList() :
             osmium::memory::Collection<Tag, osmium::item_type::tag_list>() {
         }
 
+        /**
+         * Returns the number of tags in this tag list.
+         */
         size_type size() const noexcept {
             return static_cast<size_type>(std::distance(begin(), end()));
         }
 
+        /**
+         * Get tag value for the given tag key. If the key is not set, returns
+         * the default_value.
+         *
+         * @pre @code key != nullptr @endcode
+         */
         const char* get_value_by_key(const char* key, const char* default_value = nullptr) const noexcept {
-            auto result = std::find_if(cbegin(), cend(), [key](const Tag& tag) {
-                return !strcmp(tag.key(), key);
-            });
-            if (result == cend()) {
-                return default_value;
-            }
-            return result->value();
+            assert(key);
+            const auto result = find_key(key);
+            return result == cend() ? default_value : result->value();
         }
 
+        /**
+         * Get tag value for the given tag key. If the key is not set, returns
+         * nullptr.
+         *
+         * @pre @code key != nullptr @endcode
+         */
         const char* operator[](const char* key) const noexcept {
             return get_value_by_key(key);
         }
 
+        /**
+         * Returns true if the tag with the given key is in the tag list.
+         *
+         * @pre @code key != nullptr @endcode
+         */
+        bool has_key(const char* key) const noexcept {
+            assert(key);
+            return find_key(key) != cend();
+        }
+
+        /**
+         * Returns true if the tag with the given key and value is in the
+         * tag list.
+         *
+         * @pre @code key != nullptr && value != nullptr @endcode
+         */
+        bool has_tag(const char* key, const char* value) const noexcept {
+            assert(key);
+            assert(value);
+            const auto result = find_key(key);
+            return result != cend() && !std::strcmp(result->value(), value);
+        }
+
     }; // class TagList
 
     static_assert(sizeof(TagList) % osmium::memory::align_bytes == 0, "Class osmium::TagList has wrong size to be aligned properly!");
diff --git a/include/osmium/osm/types.hpp b/include/osmium/osm/types.hpp
index 984dd13..ec46bb2 100644
--- a/include/osmium/osm/types.hpp
+++ b/include/osmium/osm/types.hpp
@@ -38,25 +38,25 @@ DEALINGS IN THE SOFTWARE.
 namespace osmium {
 
     /*
-     * The following typedefs are chosen so that they can represent all needed
+     * The following types are chosen so that they can represent all needed
      * numbers and still be reasonably space efficient. As the OSM database
      * needs 64 bit IDs for nodes, this size is used for all object IDs.
      */
-    typedef int64_t  object_id_type;          ///< Type for OSM object (node, way, or relation) IDs.
-    typedef uint64_t unsigned_object_id_type; ///< Type for OSM object (node, way, or relation) IDs where we only allow positive IDs.
-    typedef uint32_t object_version_type;     ///< Type for OSM object version number.
-    typedef uint32_t changeset_id_type;       ///< Type for OSM changeset IDs.
-    typedef uint32_t user_id_type;            ///< Type for OSM user IDs.
-    typedef int32_t  signed_user_id_type;     ///< Type for signed OSM user IDs.
-    typedef uint32_t num_changes_type;        ///< Type for changeset num_changes.
-    typedef uint32_t num_comments_type;       ///< Type for changeset num_comments.
+    using object_id_type          =  int64_t; ///< Type for OSM object (node, way, or relation) IDs.
+    using unsigned_object_id_type = uint64_t; ///< Type for OSM object (node, way, or relation) IDs where we only allow positive IDs.
+    using object_version_type     = uint32_t; ///< Type for OSM object version number.
+    using changeset_id_type       = uint32_t; ///< Type for OSM changeset IDs.
+    using user_id_type            = uint32_t; ///< Type for OSM user IDs.
+    using signed_user_id_type     =  int32_t; ///< Type for signed OSM user IDs.
+    using num_changes_type        = uint32_t; ///< Type for changeset num_changes.
+    using num_comments_type       = uint32_t; ///< Type for changeset num_comments.
 
     /**
      * Size for strings in OSM data such as user names, tag keys, roles, etc.
      * In Osmium they can be up to 2^16 bytes long, but OSM usually has lower
      * defined limits.
      */
-    typedef uint16_t string_size_type;
+    using string_size_type = uint16_t;
 
     // maximum of 256 characters of max 4 bytes each (in UTF-8 encoding)
     constexpr const int max_osm_string_length = 256 * 4;
diff --git a/include/osmium/osm/types_from_string.hpp b/include/osmium/osm/types_from_string.hpp
index aed0648..d5da72b 100644
--- a/include/osmium/osm/types_from_string.hpp
+++ b/include/osmium/osm/types_from_string.hpp
@@ -50,7 +50,7 @@ namespace osmium {
     /**
      * Convert string with object id to object_id_type.
      *
-     * @pre input must not be nullptr.
+     * @pre @code input != nullptr @endcode
      *
      * @param input Input string.
      *
@@ -70,23 +70,29 @@ namespace osmium {
 
     /**
      * Parse string with object type identifier followed by object id. This
-     * reads strings like "n1234" and "w10".
+     * reads strings like "n1234" and "w10". If there is no type prefix,
+     * the default_type is returned.
      *
-     * @pre input must not be nullptr.
+     * @pre @code input != nullptr @endcode
+     * @pre @code types != osmium::osm_entity_bits::nothing @endcode
      *
      * @param input Input string.
      * @param types Allowed types. Must not be osmium::osm_entity_bits::nothing.
+     * @param default_type Type used when there is no type prefix.
      *
      * @returns std::pair of type and id.
      *
      * @throws std::range_error if the value is out of range.
      */
-    inline std::pair<osmium::item_type, osmium::object_id_type> string_to_object_id(const char* input, osmium::osm_entity_bits::type types) {
+    inline std::pair<osmium::item_type, osmium::object_id_type>
+    string_to_object_id(const char* input,
+                        osmium::osm_entity_bits::type types,
+                        osmium::item_type default_type = osmium::item_type::undefined) {
         assert(input);
         assert(types != osmium::osm_entity_bits::nothing);
         if (*input != '\0') {
             if (std::isdigit(*input)) {
-                return std::make_pair(osmium::item_type::undefined, string_to_object_id(input));
+                return std::make_pair(default_type, string_to_object_id(input));
             }
             osmium::item_type t = osmium::char_to_item_type(*input);
             if (osmium::osm_entity_bits::from_item_type(t) & types) {
@@ -126,7 +132,7 @@ namespace osmium {
     }
 
     /**
-     * Convert string with object version to object_version_type.
+     * Convert string with changeset id to changeset_id_type.
      *
      * @pre input must not be nullptr.
      *
diff --git a/include/osmium/relations/collector.hpp b/include/osmium/relations/collector.hpp
index 7d7d14d..4d2b523 100644
--- a/include/osmium/relations/collector.hpp
+++ b/include/osmium/relations/collector.hpp
@@ -64,13 +64,6 @@ namespace osmium {
 
         namespace detail {
 
-            template <typename R>
-            inline typename std::iterator_traits<typename R::iterator>::difference_type count_not_removed(const R& range) {
-                return std::count_if(range.begin(), range.end(), [](MemberMeta& mm) {
-                    return !mm.removed();
-                });
-            }
-
         } // namespace detail
 
         /**
@@ -193,14 +186,14 @@ namespace osmium {
 
             int m_count_complete = 0;
 
-            typedef std::function<void(osmium::memory::Buffer&&)> callback_func_type;
+            using callback_func_type = std::function<void(osmium::memory::Buffer&&)>;
             callback_func_type m_callback;
 
             static constexpr size_t initial_buffer_size = 1024 * 1024;
 
             iterator_range<mm_iterator> find_member_meta(osmium::item_type type, osmium::object_id_type id) {
                 auto& mmv = member_meta(type);
-                return iterator_range<mm_iterator>{std::equal_range(mmv.begin(), mmv.end(), MemberMeta(id))};
+                return make_range(std::equal_range(mmv.begin(), mmv.end(), MemberMeta(id)));
             }
 
         public:
@@ -313,6 +306,7 @@ namespace osmium {
             }
 
             const osmium::Relation& get_relation(size_t offset) const {
+                assert(m_relations_buffer.committed() > offset);
                 return m_relations_buffer.get<osmium::Relation>(offset);
             }
 
@@ -323,7 +317,15 @@ namespace osmium {
                 return get_relation(relation_meta.relation_offset());
             }
 
+            /**
+             * Get the relation from a member_meta.
+             */
+            const osmium::Relation& get_relation(const MemberMeta& member_meta) const {
+                return get_relation(m_relations[member_meta.relation_pos()]);
+            }
+
             osmium::OSMObject& get_member(size_t offset) const {
+                assert(m_members_buffer.committed() > offset);
                 return m_members_buffer.get<osmium::OSMObject>(offset);
             }
 
@@ -378,6 +380,12 @@ namespace osmium {
                 std::sort(m_member_meta[2].begin(), m_member_meta[2].end());
             }
 
+            static typename iterator_range<mm_iterator>::iterator::difference_type count_not_removed(const iterator_range<mm_iterator>& range) {
+                return std::count_if(range.begin(), range.end(), [](MemberMeta& mm) {
+                    return !mm.removed();
+                });
+            }
+
             /**
              * Find this object in the member vectors and add it to all
              * relations that need it.
@@ -388,7 +396,7 @@ namespace osmium {
             bool find_and_add_object(const osmium::OSMObject& object) {
                 auto range = find_member_meta(object.type(), object.id());
 
-                if (detail::count_not_removed(range) == 0) {
+                if (count_not_removed(range) == 0) {
                     // nothing found
                     return false;
                 }
@@ -434,12 +442,12 @@ namespace osmium {
 
                         // if this is the last time this object was needed
                         // then mark it as removed
-                        if (detail::count_not_removed(range) == 1) {
+                        if (count_not_removed(range) == 1) {
                             get_member(range.begin()->buffer_offset()).set_removed(true);
                         }
 
                         for (auto& member_meta : range) {
-                            if (!member_meta.removed() && relation.id() == get_relation(member_meta.relation_pos()).id()) {
+                            if (!member_meta.removed() && relation.id() == get_relation(member_meta).id()) {
                                 member_meta.remove();
                                 break;
                             }
diff --git a/include/osmium/relations/detail/member_meta.hpp b/include/osmium/relations/detail/member_meta.hpp
index f0e9c36..fb71416 100644
--- a/include/osmium/relations/detail/member_meta.hpp
+++ b/include/osmium/relations/detail/member_meta.hpp
@@ -132,7 +132,7 @@ namespace osmium {
 
         template <typename TChar, typename TTraits>
         inline std::basic_ostream<TChar, TTraits>& operator<<(std::basic_ostream<TChar, TTraits>& out, const MemberMeta& mm) {
-            out << "MemberMeta(member_id=" << mm.member_id() << " relation_pos=" << mm.relation_pos() << " member_pos=" << mm.member_pos() << " buffer_offset=" << mm.buffer_offset() << ")";
+            out << "MemberMeta(member_id=" << mm.member_id() << " relation_pos=" << mm.relation_pos() << " member_pos=" << mm.member_pos() << " buffer_offset=" << mm.buffer_offset() << " removed=" << (mm.removed() ? "yes" : "no") << ")";
             return out;
         }
 
diff --git a/include/osmium/relations/detail/relation_meta.hpp b/include/osmium/relations/detail/relation_meta.hpp
index 93aa41c..b71c5a5 100644
--- a/include/osmium/relations/detail/relation_meta.hpp
+++ b/include/osmium/relations/detail/relation_meta.hpp
@@ -117,8 +117,8 @@ namespace osmium {
          */
         struct has_all_members {
 
-            typedef RelationMeta& argument_type;
-            typedef bool result_type;
+            using argument_type = RelationMeta&;
+            using result_type = bool;
 
             /**
              * @returns true if this relation is complete, false otherwise.
diff --git a/include/osmium/tags/filter.hpp b/include/osmium/tags/filter.hpp
index 407992e..7451737 100644
--- a/include/osmium/tags/filter.hpp
+++ b/include/osmium/tags/filter.hpp
@@ -76,8 +76,8 @@ namespace osmium {
         template <typename TKey, typename TValue=void, typename TKeyComp=match_key<TKey>, typename TValueComp=match_value<TValue>>
         class Filter {
 
-            typedef TKey key_type;
-            typedef typename std::conditional<std::is_void<TValue>::value, bool, TValue>::type value_type;
+            using key_type   = TKey;
+            using value_type = typename std::conditional<std::is_void<TValue>::value, bool, TValue>::type;
 
             struct Rule {
                 key_type key;
@@ -106,10 +106,10 @@ namespace osmium {
 
         public:
 
-            typedef Filter<TKey, TValue, TKeyComp, TValueComp> filter_type;
-            typedef const osmium::Tag& argument_type;
-            typedef bool result_type;
-            typedef boost::filter_iterator<filter_type, osmium::TagList::const_iterator> iterator;
+            using filter_type   = Filter<TKey, TValue, TKeyComp, TValueComp>;
+            using argument_type = const osmium::Tag&;
+            using result_type   = bool;
+            using iterator      = boost::filter_iterator<filter_type, osmium::TagList::const_iterator>;
 
             explicit Filter(bool default_result = false) :
                 m_default_result(default_result) {
@@ -151,9 +151,9 @@ namespace osmium {
 
         }; // class Filter
 
-        typedef Filter<std::string, std::string> KeyValueFilter;
-        typedef Filter<std::string> KeyFilter;
-        typedef Filter<std::string, void, match_key_prefix> KeyPrefixFilter;
+        using KeyValueFilter  = Filter<std::string, std::string>;
+        using KeyFilter       = Filter<std::string>;
+        using KeyPrefixFilter = Filter<std::string, void, match_key_prefix>;
 
     } // namespace tags
 
diff --git a/include/osmium/tags/regex_filter.hpp b/include/osmium/tags/regex_filter.hpp
index 8ea6d60..3df94dd 100644
--- a/include/osmium/tags/regex_filter.hpp
+++ b/include/osmium/tags/regex_filter.hpp
@@ -49,7 +49,7 @@ namespace osmium {
             }
         }; // struct match_value<std::regex>
 
-        typedef Filter<std::string, std::regex> RegexFilter;
+        using RegexFilter = Filter<std::string, std::regex>;
 
     } // namespace tags
 
diff --git a/include/osmium/thread/pool.hpp b/include/osmium/thread/pool.hpp
index 207f555..44b6d15 100644
--- a/include/osmium/thread/pool.hpp
+++ b/include/osmium/thread/pool.hpp
@@ -66,7 +66,7 @@ namespace osmium {
                 }
 
                 if (num_threads < 0) {
-                    num_threads += hardware_concurrency;
+                    num_threads += int(hardware_concurrency);
                 }
 
                 if (num_threads < 1) {
@@ -190,7 +190,7 @@ namespace osmium {
             template <typename TFunction>
             std::future<typename std::result_of<TFunction()>::type> submit(TFunction&& func) {
 
-                typedef typename std::result_of<TFunction()>::type result_type;
+                using result_type = typename std::result_of<TFunction()>::type;
 
                 std::packaged_task<result_type()> task(std::forward<TFunction>(func));
                 std::future<result_type> future_result(task.get_future());
diff --git a/include/osmium/thread/sorted_queue.hpp b/include/osmium/thread/sorted_queue.hpp
deleted file mode 100644
index 5478643..0000000
--- a/include/osmium/thread/sorted_queue.hpp
+++ /dev/null
@@ -1,159 +0,0 @@
-#ifndef OSMIUM_THREAD_SORTED_QUEUE_HPP
-#define OSMIUM_THREAD_SORTED_QUEUE_HPP
-
-/*
-
-This file is part of Osmium (http://osmcode.org/libosmium).
-
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
-
-Boost Software License - Version 1.0 - August 17th, 2003
-
-Permission is hereby granted, free of charge, to any person or organization
-obtaining a copy of the software and accompanying documentation covered by
-this license (the "Software") to use, reproduce, display, distribute,
-execute, and transmit the Software, and to prepare derivative works of the
-Software, and to permit third-parties to whom the Software is furnished to
-do so, all subject to the following:
-
-The copyright notices in the Software and this entire statement, including
-the above license grant, this restriction and the following disclaimer,
-must be included in all copies of the Software, in whole or in part, and
-all derivative works of the Software, unless such copies or derivative
-works are solely in the form of machine-executable object code generated by
-a source language processor.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
-SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
-FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
-DEALINGS IN THE SOFTWARE.
-
-*/
-
-#include <condition_variable>
-#include <cstddef>
-#include <deque>
-#include <mutex>
-
-namespace osmium {
-
-    namespace thread {
-
-        /**
-         * This implements a sorted queue. It is a bit like a priority
-         * queue. We have n worker threads pushing items into the queue
-         * and one thread pulling them out again "in order". The order
-         * is defined by the monotonically increasing "num" parameter
-         * to the push() method. The wait_and_pop() and try_pop() methods
-         * will only give out the next numbered item. This way several
-         * workers can work in their own time on different pieces of
-         * some incoming data, but it all gets serialized properly again
-         * after the workers have done their work.
-         */
-        template <typename T>
-        class SortedQueue {
-
-            typedef typename std::deque<T>::size_type size_type;
-
-            mutable std::mutex m_mutex;
-            std::deque<T> m_queue;
-            std::condition_variable m_data_available;
-
-            size_type m_offset;
-
-            // this method expects that we already have the lock
-            bool empty_intern() const {
-                return m_queue.front() == T();
-            }
-
-        public:
-
-            SortedQueue() :
-                m_mutex(),
-                m_queue(1),
-                m_data_available(),
-                m_offset(0) {
-            }
-
-            /**
-             * Push an item into the queue.
-             *
-             * @param value The item to push into the queue.
-             * @param num Number to describe ordering for the items.
-             *            It must increase monotonically.
-             */
-            void push(T value, size_type num) {
-                std::lock_guard<std::mutex> lock(m_mutex);
-
-                num -= m_offset;
-                if (m_queue.size() <= num + 1) {
-                    m_queue.resize(num + 2);
-                }
-                m_queue[num] = std::move(value);
-
-                m_data_available.notify_one();
-            }
-
-            /**
-             * Wait until the next item becomes available and make it
-             * available through value.
-             */
-            void wait_and_pop(T& value) {
-                std::unique_lock<std::mutex> lock(m_mutex);
-
-                m_data_available.wait(lock, [this] {
-                    return !empty_intern();
-                });
-                value = std::move(m_queue.front());
-                m_queue.pop_front();
-                ++m_offset;
-            }
-
-            /**
-             * Get next item if it is available and return true. Or
-             * return false otherwise.
-             */
-            bool try_pop(T& value) {
-                std::lock_guard<std::mutex> lock(m_mutex);
-
-                if (empty_intern()) {
-                    return false;
-                }
-                value = std::move(m_queue.front());
-                m_queue.pop_front();
-                ++m_offset;
-                return true;
-            }
-
-            /**
-             * The queue is empty. This means try_pop() would fail if called.
-             * It does not mean that there is nothing on the queue. Because
-             * the queue is sorted, it could mean that the next item in the
-             * queue is not available, but other items are.
-             */
-            bool empty() const {
-                std::lock_guard<std::mutex> lock(m_mutex);
-
-                return empty_intern();
-            }
-
-            /**
-             * Returns the number of items in the queue, regardless of whether
-             * they can be accessed. If this is =0 it
-             * implies empty()==true, but not the other way around.
-             */
-            size_t size() const {
-                std::lock_guard<std::mutex> lock(m_mutex);
-                return m_queue.size();
-            }
-
-        }; // class SortedQueue
-
-    } // namespace thread
-
-} // namespace osmium
-
-#endif // OSMIUM_THREAD_SORTED_QUEUE_HPP
diff --git a/include/osmium/util/delta.hpp b/include/osmium/util/delta.hpp
index 34c4eb2..8fd63a4 100644
--- a/include/osmium/util/delta.hpp
+++ b/include/osmium/util/delta.hpp
@@ -119,7 +119,7 @@ namespace osmium {
         }; // class DeltaDecode
 
         template <typename TBaseIterator, typename TTransform, typename TValue, typename TDelta = int64_t>
-        class DeltaEncodeIterator : public std::iterator<std::input_iterator_tag, TValue> {
+        class DeltaEncodeIterator {
 
             TBaseIterator m_it;
             TBaseIterator m_end;
@@ -129,8 +129,13 @@ namespace osmium {
 
         public:
 
-            using value_type = TValue;
-            using delta_type = TDelta;
+            using iterator_category = std::input_iterator_tag;
+            using value_type        = TValue;
+            using difference_type   = std::ptrdiff_t;
+            using pointer           = value_type*;
+            using reference         = value_type&;
+
+            using delta_type        = TDelta;
 
             DeltaEncodeIterator(TBaseIterator first, TBaseIterator last, TTransform& trans) :
                 m_it(first),
diff --git a/include/osmium/util/iterator.hpp b/include/osmium/util/iterator.hpp
index 4cef519..15f3f3d 100644
--- a/include/osmium/util/iterator.hpp
+++ b/include/osmium/util/iterator.hpp
@@ -46,7 +46,7 @@ namespace osmium {
         iterator_range(P&& p) :
             P(std::forward<P>(p)) {
         }
-
+/*
         It begin() {
             return this->first;
         }
@@ -54,7 +54,7 @@ namespace osmium {
         It end() {
             return this->second;
         }
-
+*/
         It begin() const {
             return this->first;
         }
@@ -67,7 +67,16 @@ namespace osmium {
             return begin() == end();
         }
 
-    };
+    }; // struct iterator_range
+
+    /**
+     * Helper function to create iterator_range from std::pair.
+     */
+    template <typename P, typename It = typename P::first_type>
+    inline iterator_range<It> make_range(P&& p) {
+        static_assert(std::is_same<P, std::pair<It, It>>::value, "make_range needs pair of iterators as argument");
+        return iterator_range<It>(std::forward<P>(p));
+    }
 
 } // namespace osmium
 
diff --git a/include/osmium/util/memory_mapping.hpp b/include/osmium/util/memory_mapping.hpp
index 67e944e..cf28e6a 100644
--- a/include/osmium/util/memory_mapping.hpp
+++ b/include/osmium/util/memory_mapping.hpp
@@ -126,22 +126,18 @@ private:
             void make_invalid() noexcept;
 
 #ifdef _WIN32
-            typedef DWORD flag_type;
+            using flag_type = DWORD;
 #else
-            typedef int flag_type;
+            using flag_type = int;
 #endif
 
             flag_type get_protection() const noexcept;
 
             flag_type get_flags() const noexcept;
 
-            // A zero-sized mapping is not allowed by the operating system.
-            // So if the user asks for a mapping of size 0, we map a full
-            // page instead. This way we don't have a special case in the rest
-            // of the code.
-            static size_t initial_size(size_t size) {
+            static size_t check_size(size_t size) {
                 if (size == 0) {
-                    return osmium::util::get_pagesize();
+                    throw std::runtime_error("Zero-sized mapping is not allowed.");
                 }
                 return size;
             }
@@ -550,7 +546,7 @@ inline int osmium::util::MemoryMapping::get_flags() const noexcept {
 }
 
 inline osmium::util::MemoryMapping::MemoryMapping(size_t size, mapping_mode mode, int fd, off_t offset) :
-    m_size(initial_size(size)),
+    m_size(check_size(size)),
     m_offset(offset),
     m_fd(resize_fd(fd)),
     m_mapping_mode(mode),
@@ -689,7 +685,7 @@ inline void osmium::util::MemoryMapping::make_invalid() noexcept {
 }
 
 inline osmium::util::MemoryMapping::MemoryMapping(size_t size, MemoryMapping::mapping_mode mode, int fd, off_t offset) :
-    m_size(initial_size(size)),
+    m_size(check_size(size)),
     m_offset(offset),
     m_fd(resize_fd(fd)),
     m_mapping_mode(mode),
diff --git a/include/osmium/tags/regex_filter.hpp b/include/osmium/util/misc.hpp
similarity index 73%
copy from include/osmium/tags/regex_filter.hpp
copy to include/osmium/util/misc.hpp
index 8ea6d60..8acecb5 100644
--- a/include/osmium/tags/regex_filter.hpp
+++ b/include/osmium/util/misc.hpp
@@ -1,5 +1,5 @@
-#ifndef OSMIUM_TAGS_REGEX_FILTER_HPP
-#define OSMIUM_TAGS_REGEX_FILTER_HPP
+#ifndef OSMIUM_UTIL_MISC_HPP
+#define OSMIUM_UTIL_MISC_HPP
 
 /*
 
@@ -33,26 +33,20 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
-#include <regex>
-#include <string>
-
-#include <osmium/tags/filter.hpp>
+#include <tuple>
 
 namespace osmium {
 
-    namespace tags {
-
-        template <>
-        struct match_value<std::regex> {
-            bool operator()(const std::regex& rule_value, const char* tag_value) {
-                return std::regex_match(tag_value, rule_value);
-            }
-        }; // struct match_value<std::regex>
-
-        typedef Filter<std::string, std::regex> RegexFilter;
-
-    } // namespace tags
+    /**
+     * Like std::tie(), but takes its arguments as const references. Used
+     * as a helper function when sorting.
+     */
+    template<typename... Ts>
+    inline std::tuple<const Ts&...>
+    const_tie(const Ts&... args) noexcept {
+        return std::tuple<const Ts&...>(args...);
+    }
 
 } // namespace osmium
 
-#endif // OSMIUM_TAGS_REGEX_FILTER_HPP
+#endif // OSMIUM_UTIL_MISC_HPP
diff --git a/include/osmium/util/iterator.hpp b/include/osmium/util/timer.hpp
similarity index 63%
copy from include/osmium/util/iterator.hpp
copy to include/osmium/util/timer.hpp
index 4cef519..8cae80a 100644
--- a/include/osmium/util/iterator.hpp
+++ b/include/osmium/util/timer.hpp
@@ -1,5 +1,5 @@
-#ifndef OSMIUM_UTIL_ITERATOR_HPP
-#define OSMIUM_UTIL_ITERATOR_HPP
+#ifndef OSMIUM_UTIL_TIMER_HPP
+#define OSMIUM_UTIL_TIMER_HPP
 
 /*
 
@@ -33,42 +33,66 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
-#include <cstddef>
-#include <utility>
+#include <cstdint>
+
+#ifdef OSMIUM_WITH_TIMER
+
+#include <chrono>
 
 namespace osmium {
 
-    template <typename It, typename P = std::pair<It, It>>
-    struct iterator_range : public P {
+    class Timer {
+
+        using clock = std::chrono::high_resolution_clock;
+        std::chrono::time_point<clock> m_start;
+        std::chrono::time_point<clock> m_stop;
+
+    public:
 
-        using iterator = It;
+        Timer() :
+            m_start(clock::now()) {
+        }
 
-        iterator_range(P&& p) :
-            P(std::forward<P>(p)) {
+        void start() {
+            m_start = clock::now();
         }
 
-        It begin() {
-            return this->first;
+        void stop() {
+            m_stop = clock::now();
         }
 
-        It end() {
-            return this->second;
+        int64_t elapsed_microseconds() const {
+            return std::chrono::duration_cast<std::chrono::microseconds>(m_stop - m_start).count();
         }
 
-        It begin() const {
-            return this->first;
+    };
+
+} // namespace osmium
+
+#else
+
+namespace osmium {
+
+    class Timer {
+
+    public:
+
+        Timer() = default;
+
+        void start() {
         }
 
-        It end() const {
-            return this->second;
+        void stop() {
         }
 
-        size_t empty() const {
-            return begin() == end();
+        int64_t elapsed_microseconds() const {
+            return 0;
         }
 
     };
 
 } // namespace osmium
 
-#endif // OSMIUM_UTIL_ITERATOR_HPP
+#endif
+
+#endif // OSMIUM_UTIL_TIMER_HPP
diff --git a/include/osmium/tags/regex_filter.hpp b/include/osmium/version.hpp
similarity index 71%
copy from include/osmium/tags/regex_filter.hpp
copy to include/osmium/version.hpp
index 8ea6d60..ac2765c 100644
--- a/include/osmium/tags/regex_filter.hpp
+++ b/include/osmium/version.hpp
@@ -1,5 +1,5 @@
-#ifndef OSMIUM_TAGS_REGEX_FILTER_HPP
-#define OSMIUM_TAGS_REGEX_FILTER_HPP
+#ifndef OSMIUM_VERSION_HPP
+#define OSMIUM_VERSION_HPP
 
 /*
 
@@ -33,26 +33,10 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
-#include <regex>
-#include <string>
+#define LIBOSMIUM_VERSION_MAJOR 2
+#define LIBOSMIUM_VERSION_MINOR 6
+#define LIBOSMIUM_VERSION_PATCH 1
 
-#include <osmium/tags/filter.hpp>
+#define LIBOSMIUM_VERSION_STRING "2.6.1"
 
-namespace osmium {
-
-    namespace tags {
-
-        template <>
-        struct match_value<std::regex> {
-            bool operator()(const std::regex& rule_value, const char* tag_value) {
-                return std::regex_match(tag_value, rule_value);
-            }
-        }; // struct match_value<std::regex>
-
-        typedef Filter<std::string, std::regex> RegexFilter;
-
-    } // namespace tags
-
-} // namespace osmium
-
-#endif // OSMIUM_TAGS_REGEX_FILTER_HPP
+#endif // OSMIUM_VERSION_HPP
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index cea67ec..241c192 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -134,6 +134,7 @@ add_unit_test(geom test_wkb)
 add_unit_test(geom test_wkt)
 
 add_unit_test(index test_id_to_location ENABLE_IF ${SPARSEHASH_FOUND})
+add_unit_test(index test_file_based_index)
 
 add_unit_test(io test_bzip2 ENABLE_IF ${BZIP2_FOUND} LIBS ${BZIP2_LIBRARIES})
 add_unit_test(io test_file_formats)
diff --git a/test/README b/test/README
index 8195824..868b533 100644
--- a/test/README
+++ b/test/README
@@ -3,11 +3,3 @@ Osmium uses Catch (https://github.com/philsquared/Catch/) for its unit tests.
 Only one header file is needed (catch.hpp) which can be downloaded from
 http://builds.catch-lib.net/ and put into the include directory.
 
-Osmium needs a few changes to catch.hpp, they were patched in. To be able to
-compare with the original version, it is stored in include/catch_orig.hpp.
-
-Changes are:
-* Disable more warnings in GCC
-* CATCH_CONFIG_CPP11_NULLPTR must be set for MSVC
-* Problem with test running in loop: https://github.com/philsquared/Catch/issues/271
-
diff --git a/test/data-tests/testdata-multipolygon.cpp b/test/data-tests/testdata-multipolygon.cpp
index cf4fc52..6a863c8 100644
--- a/test/data-tests/testdata-multipolygon.cpp
+++ b/test/data-tests/testdata-multipolygon.cpp
@@ -156,7 +156,9 @@ int main(int argc, char* argv[]) {
 
     osmium::area::ProblemReporterOGR problem_reporter(dataset);
     osmium::area::Assembler::config_type assembler_config(&problem_reporter);
-    assembler_config.enable_debug_output();
+    assembler_config.check_roles = true;
+    assembler_config.create_empty_areas = true;
+    assembler_config.debug_level = 2;
     osmium::area::MultipolygonCollector<osmium::area::Assembler> collector(assembler_config);
 
     std::cerr << "Pass 1...\n";
diff --git a/test/data-tests/testdata-xml.cpp b/test/data-tests/testdata-xml.cpp
index b5a0e90..01cee29 100644
--- a/test/data-tests/testdata-xml.cpp
+++ b/test/data-tests/testdata-xml.cpp
@@ -336,10 +336,10 @@ TEST_CASE("Reading OSM XML 140") {
         reader.close();
 
         int count = 0;
-        for (auto it = buffer.begin<osmium::Node>(); it != buffer.end<osmium::Node>(); ++it) {
+        for (const auto& node : buffer.select<osmium::Node>()) {
             ++count;
-            REQUIRE(it->id() == count);
-            const osmium::TagList& t = it->tags();
+            REQUIRE(node.id() == count);
+            const osmium::TagList& t = node.tags();
 
             const char* uc = t["unicode_char"];
 
@@ -411,35 +411,35 @@ TEST_CASE("Reading OSM XML 142") {
         reader.close();
 
         int count = 0;
-        for (auto it = buffer.begin<osmium::Node>(); it != buffer.end<osmium::Node>(); ++it) {
+        for (const auto& node : buffer.select<osmium::Node>()) {
             ++count;
-            REQUIRE(it->id() == count);
-            REQUIRE(it->tags().size() == 1);
-            const osmium::Tag& tag = *(it->tags().begin());
+            REQUIRE(node.id() == count);
+            REQUIRE(node.tags().size() == 1);
+            const osmium::Tag& tag = *(node.tags().begin());
 
             switch (count) {
                 case 1:
-                    REQUIRE(S_(it->user()) == "user name");
+                    REQUIRE(S_(node.user()) == "user name");
                     REQUIRE(S_(tag.key()) == "key with space");
                     REQUIRE(S_(tag.value()) == "value with space");
                     break;
                 case 2:
-                    REQUIRE(S_(it->user()) == "line\nfeed");
+                    REQUIRE(S_(node.user()) == "line\nfeed");
                     REQUIRE(S_(tag.key()) == "key with\nlinefeed");
                     REQUIRE(S_(tag.value()) == "value with\nlinefeed");
                     break;
                 case 3:
-                    REQUIRE(S_(it->user()) == "carriage\rreturn");
+                    REQUIRE(S_(node.user()) == "carriage\rreturn");
                     REQUIRE(S_(tag.key()) == "key with\rcarriage\rreturn");
                     REQUIRE(S_(tag.value()) == "value with\rcarriage\rreturn");
                     break;
                 case 4:
-                    REQUIRE(S_(it->user()) == "tab\tulator");
+                    REQUIRE(S_(node.user()) == "tab\tulator");
                     REQUIRE(S_(tag.key()) == "key with\ttab");
                     REQUIRE(S_(tag.value()) == "value with\ttab");
                     break;
                 case 5:
-                    REQUIRE(S_(it->user()) == "unencoded linefeed");
+                    REQUIRE(S_(node.user()) == "unencoded linefeed");
                     REQUIRE(S_(tag.key()) == "key with unencoded linefeed");
                     REQUIRE(S_(tag.value()) == "value with unencoded linefeed");
                     break;
@@ -455,8 +455,8 @@ TEST_CASE("Reading OSM XML 142") {
         osmium::memory::Buffer buffer = reader.read();
         reader.close();
 
-        auto it = buffer.begin<osmium::Relation>();
-        REQUIRE(it != buffer.end<osmium::Relation>());
+        auto it = buffer.select<osmium::Relation>().begin();
+        REQUIRE(it != buffer.select<osmium::Relation>().end());
         REQUIRE(it->id() == 21);
         const auto& members = it->members();
         REQUIRE(members.size() == 5);
diff --git a/test/include/catch.hpp b/test/include/catch.hpp
index f042149..2a7146a 100644
--- a/test/include/catch.hpp
+++ b/test/include/catch.hpp
@@ -1,6 +1,6 @@
 /*
- *  Catch v1.3.3
- *  Generated: 2016-01-22 07:51:36.661106
+ *  Catch v1.4.0
+ *  Generated: 2016-03-15 07:23:12.623111
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
  *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -62,7 +62,11 @@
 
 #define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
 #define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
-#define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#ifdef CATCH_CONFIG_COUNTER
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
+#else
+#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
+#endif
 
 #define INTERNAL_CATCH_STRINGIFY2( expr ) #expr
 #define INTERNAL_CATCH_STRINGIFY( expr ) INTERNAL_CATCH_STRINGIFY2( expr )
@@ -89,7 +93,7 @@
 // CATCH_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
 
 // CATCH_CONFIG_VARIADIC_MACROS : are variadic macros supported?
-
+// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
 // ****************
 // Note to maintainers: if new toggles are added please document them
 // in configuration.md, too
@@ -102,6 +106,10 @@
 
 // All the C++11 features can be disabled with CATCH_CONFIG_NO_CPP11
 
+#if defined(__cplusplus) && __cplusplus >= 201103L
+#  define CATCH_CPP11_OR_GREATER
+#endif
+
 #ifdef __clang__
 
 #  if __has_feature(cxx_nullptr)
@@ -112,6 +120,10 @@
 #    define CATCH_INTERNAL_CONFIG_CPP11_NOEXCEPT
 #  endif
 
+#   if defined(CATCH_CPP11_OR_GREATER)
+#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
+#   endif
+
 #endif // __clang__
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -136,9 +148,13 @@
 // GCC
 #ifdef __GNUC__
 
-#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
-#   define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
-#endif
+#   if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#       define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
+#   endif
+
+#   if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) && defined(CATCH_CPP11_OR_GREATER)
+#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS _Pragma( "GCC diagnostic ignored \"-Wparentheses\"" )
+#   endif
 
 // - otherwise more recent versions define __cplusplus >= 201103L
 // and will get picked up below
@@ -173,13 +189,20 @@
 
 #endif
 
+// Use __COUNTER__ if the compiler supports it
+#if ( defined _MSC_VER && _MSC_VER >= 1300 ) || \
+    ( defined __GNUC__  && __GNUC__ >= 4 && __GNUC_MINOR__ >= 3 ) || \
+    ( defined __clang__ && __clang_major__ >= 3 )
+
+#define CATCH_INTERNAL_CONFIG_COUNTER
+
+#endif
+
 ////////////////////////////////////////////////////////////////////////////////
 // C++ language feature support
 
 // catch all support for C++11
-#if defined(__cplusplus) && __cplusplus >= 201103L
-
-#  define CATCH_CPP11_OR_GREATER
+#if defined(CATCH_CPP11_OR_GREATER)
 
 #  if !defined(CATCH_INTERNAL_CONFIG_CPP11_NULLPTR)
 #    define CATCH_INTERNAL_CONFIG_CPP11_NULLPTR
@@ -246,6 +269,13 @@
 #if defined(CATCH_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_UNIQUE_PTR) && !defined(CATCH_CONFIG_CPP11_UNIQUE_PTR) && !defined(CATCH_CONFIG_NO_CPP11)
 #   define CATCH_CONFIG_CPP11_UNIQUE_PTR
 #endif
+#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
+#   define CATCH_CONFIG_COUNTER
+#endif
+
+#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
+#   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
+#endif
 
 // noexcept support:
 #if defined(CATCH_CONFIG_CPP11_NOEXCEPT) && !defined(CATCH_NOEXCEPT)
@@ -672,24 +702,28 @@ void registerTestCaseFunction
 
 #ifdef CATCH_CONFIG_VARIADIC_MACROS
     ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
+        static void TestName(); \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
+        static void TestName()
     #define INTERNAL_CATCH_TESTCASE( ... ) \
-        static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( __VA_ARGS__ ) ); }\
-        static void INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ )()
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
 
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
         namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); }
 
     ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... )\
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
         namespace{ \
-            struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+            struct TestName : ClassName{ \
                 void test(); \
             }; \
-            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestName::test, #ClassName, Catch::NameAndDesc( __VA_ARGS__ ), CATCH_INTERNAL_LINEINFO ); \
         } \
-        void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+        void TestName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
 
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
@@ -697,24 +731,28 @@ void registerTestCaseFunction
 
 #else
     ///////////////////////////////////////////////////////////////////////////////
+    #define INTERNAL_CATCH_TESTCASE2( TestName, Name, Desc ) \
+        static void TestName(); \
+        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &TestName, CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
+        static void TestName()
     #define INTERNAL_CATCH_TESTCASE( Name, Desc ) \
-        static void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )(); \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ ), CATCH_INTERNAL_LINEINFO, Catch::NameAndDesc( Name, Desc ) ); }\
-        static void INTERNAL_CATCH_UNIQUE_NAME(  ____C_A_T_C_H____T_E_S_T____ )()
+        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), Name, Desc )
 
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, Name, Desc ) \
         namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( &QualifiedMethod, "&" #QualifiedMethod, Catch::NameAndDesc( Name, Desc ), CATCH_INTERNAL_LINEINFO ); }
 
     ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestCaseName, ClassName, TestName, Desc )\
         namespace{ \
-            struct INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) : ClassName{ \
+            struct TestCaseName : ClassName{ \
                 void test(); \
             }; \
-            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
+            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( &TestCaseName::test, #ClassName, Catch::NameAndDesc( TestName, Desc ), CATCH_INTERNAL_LINEINFO ); \
         } \
-        void INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ )::test()
+        void TestCaseName::test()
+    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, TestName, Desc )\
+        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, TestName, Desc )
 
     ///////////////////////////////////////////////////////////////////////////////
     #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, Name, Desc ) \
@@ -1287,37 +1325,37 @@ namespace Internal {
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsEqualTo> {
         static bool evaluate( T1 const& lhs, T2 const& rhs) {
-            return opCast( lhs ) ==  opCast( rhs );
+            return bool( opCast( lhs ) ==  opCast( rhs ) );
         }
     };
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsNotEqualTo> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) != opCast( rhs );
+            return bool( opCast( lhs ) != opCast( rhs ) );
         }
     };
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsLessThan> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) < opCast( rhs );
+            return bool( opCast( lhs ) < opCast( rhs ) );
         }
     };
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsGreaterThan> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) > opCast( rhs );
+            return bool( opCast( lhs ) > opCast( rhs ) );
         }
     };
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsGreaterThanOrEqualTo> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) >= opCast( rhs );
+            return bool( opCast( lhs ) >= opCast( rhs ) );
         }
     };
     template<typename T1, typename T2>
     struct Evaluator<T1, T2, IsLessThanOrEqualTo> {
         static bool evaluate( T1 const& lhs, T2 const& rhs ) {
-            return opCast( lhs ) <= opCast( rhs );
+            return bool( opCast( lhs ) <= opCast( rhs ) );
         }
     };
 
@@ -2020,13 +2058,14 @@ namespace Catch {
     do { \
         Catch::ResultBuilder __catchResult( macroName, CATCH_INTERNAL_LINEINFO, #expr, resultDisposition ); \
         try { \
+            CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
             ( __catchResult <= expr ).endExpression(); \
         } \
         catch( ... ) { \
             __catchResult.useActiveException( Catch::ResultDisposition::Normal ); \
         } \
         INTERNAL_CATCH_REACT( __catchResult ) \
-    } while( Catch::isTrue( false && (expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
+    } while( Catch::isTrue( false && static_cast<bool>(expr) ) ) // expr here is never evaluated at runtime but it forces the compiler to give it a look
 
 ///////////////////////////////////////////////////////////////////////////////
 #define INTERNAL_CATCH_IF( expr, resultDisposition, macroName ) \
@@ -2563,10 +2602,12 @@ namespace Catch {
 }
 
 ///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) \
-    static std::string INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator )( signature ); \
-    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ) ); }\
-    static std::string INTERNAL_CATCH_UNIQUE_NAME(  catch_internal_ExceptionTranslator )( signature )
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
+    static std::string translatorName( signature ); \
+    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); }\
+    static std::string translatorName( signature )
+
+#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
 
 // #included from: internal/catch_approx.hpp
 #define TWOBLUECUBES_CATCH_APPROX_HPP_INCLUDED
@@ -3331,6 +3372,11 @@ namespace Catch {
         InLexicographicalOrder,
         InRandomOrder
     }; };
+    struct UseColour { enum YesOrNo {
+        Auto,
+        Yes,
+        No
+    }; };
 
     class TestSpec;
 
@@ -3350,7 +3396,7 @@ namespace Catch {
         virtual TestSpec const& testSpec() const = 0;
         virtual RunTests::InWhatOrder runOrder() const = 0;
         virtual unsigned int rngSeed() const = 0;
-        virtual bool forceColour() const = 0;
+        virtual UseColour::YesOrNo useColour() const = 0;
     };
 }
 
@@ -3439,14 +3485,14 @@ namespace Catch {
             noThrow( false ),
             showHelp( false ),
             showInvisibles( false ),
-            forceColour( false ),
             filenamesAsTags( false ),
             abortAfter( -1 ),
             rngSeed( 0 ),
             verbosity( Verbosity::Normal ),
             warnings( WarnAbout::Nothing ),
             showDurations( ShowDurations::DefaultForReporter ),
-            runOrder( RunTests::InDeclarationOrder )
+            runOrder( RunTests::InDeclarationOrder ),
+            useColour( UseColour::Auto )
         {}
 
         bool listTests;
@@ -3459,7 +3505,6 @@ namespace Catch {
         bool noThrow;
         bool showHelp;
         bool showInvisibles;
-        bool forceColour;
         bool filenamesAsTags;
 
         int abortAfter;
@@ -3469,6 +3514,7 @@ namespace Catch {
         WarnAbout::What warnings;
         ShowDurations::OrNot showDurations;
         RunTests::InWhatOrder runOrder;
+        UseColour::YesOrNo useColour;
 
         std::string outputFilename;
         std::string name;
@@ -3534,7 +3580,7 @@ namespace Catch {
         virtual ShowDurations::OrNot showDurations() const { return m_data.showDurations; }
         virtual RunTests::InWhatOrder runOrder() const  { return m_data.runOrder; }
         virtual unsigned int rngSeed() const    { return m_data.rngSeed; }
-        virtual bool forceColour() const { return m_data.forceColour; }
+        virtual UseColour::YesOrNo useColour() const { return m_data.useColour; }
 
     private:
 
@@ -3572,6 +3618,8 @@ namespace Catch {
 #define STITCH_CLARA_OPEN_NAMESPACE namespace Catch {
 // #included from: ../external/clara.h
 
+// Version 0.0.1.1
+
 // Only use header guard if we are not using an outer namespace
 #if !defined(TWOBLUECUBES_CLARA_H_INCLUDED) || defined(STITCH_CLARA_OPEN_NAMESPACE)
 
@@ -3596,6 +3644,7 @@ namespace Catch {
 #include <string>
 #include <vector>
 #include <sstream>
+#include <algorithm>
 
 // Use optional outer namespace
 #ifdef STITCH_TBC_TEXT_FORMAT_OUTER_NAMESPACE
@@ -3730,12 +3779,158 @@ namespace Tbc {
 #endif // TBC_TEXT_FORMAT_H_INCLUDED
 
 // ----------- end of #include from tbc_text_format.h -----------
-// ........... back in /Users/philnash/Dev/OSS/Clara/srcs/clara.h
+// ........... back in clara.h
 
 #undef STITCH_TBC_TEXT_FORMAT_OPEN_NAMESPACE
 
+// ----------- #included from clara_compilers.h -----------
+
+#ifndef TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+#define TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// Detect a number of compiler features - mostly C++11/14 conformance - by compiler
+// The following features are defined:
+//
+// CLARA_CONFIG_CPP11_NULLPTR : is nullptr supported?
+// CLARA_CONFIG_CPP11_NOEXCEPT : is noexcept supported?
+// CLARA_CONFIG_CPP11_GENERATED_METHODS : The delete and default keywords for compiler generated methods
+// CLARA_CONFIG_CPP11_OVERRIDE : is override supported?
+// CLARA_CONFIG_CPP11_UNIQUE_PTR : is unique_ptr supported (otherwise use auto_ptr)
+
+// CLARA_CONFIG_CPP11_OR_GREATER : Is C++11 supported?
+
+// CLARA_CONFIG_VARIADIC_MACROS : are variadic macros supported?
+
+// In general each macro has a _NO_<feature name> form
+// (e.g. CLARA_CONFIG_CPP11_NO_NULLPTR) which disables the feature.
+// Many features, at point of detection, define an _INTERNAL_ macro, so they
+// can be combined, en-mass, with the _NO_ forms later.
+
+// All the C++11 features can be disabled with CLARA_CONFIG_NO_CPP11
+
+#ifdef __clang__
+
+#if __has_feature(cxx_nullptr)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#if __has_feature(cxx_noexcept)
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#endif // __clang__
+
+////////////////////////////////////////////////////////////////////////////////
+// GCC
+#ifdef __GNUC__
+
+#if __GNUC__ == 4 && __GNUC_MINOR__ >= 6 && defined(__GXX_EXPERIMENTAL_CXX0X__)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+// - otherwise more recent versions define __cplusplus >= 201103L
+// and will get picked up below
+
+#endif // __GNUC__
+
+////////////////////////////////////////////////////////////////////////////////
+// Visual C++
+#ifdef _MSC_VER
+
+#if (_MSC_VER >= 1600)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#if (_MSC_VER >= 1900 ) // (VC++ 13 (VS2015))
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#endif // _MSC_VER
+
+////////////////////////////////////////////////////////////////////////////////
+// C++ language feature support
+
+// catch all support for C++11
+#if defined(__cplusplus) && __cplusplus >= 201103L
+
+#define CLARA_CPP11_OR_GREATER
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_NULLPTR
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#define CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT
+#endif
+
+#ifndef CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#define CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS
+#endif
+
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE)
+#define CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE
+#endif
+#if !defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR)
+#define CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+#endif // __cplusplus >= 201103L
+
+// Now set the actual defines based on the above + anything the user has configured
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NO_NULLPTR) && !defined(CLARA_CONFIG_CPP11_NULLPTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NULLPTR
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NO_NOEXCEPT) && !defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_NOEXCEPT
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_NO_GENERATED_METHODS) && !defined(CLARA_CONFIG_CPP11_GENERATED_METHODS) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_GENERATED_METHODS
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_OVERRIDE) && !defined(CLARA_CONFIG_CPP11_OVERRIDE) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_OVERRIDE
+#endif
+#if defined(CLARA_INTERNAL_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_UNIQUE_PTR) && !defined(CLARA_CONFIG_CPP11_UNIQUE_PTR) && !defined(CLARA_CONFIG_NO_CPP11)
+#define CLARA_CONFIG_CPP11_UNIQUE_PTR
+#endif
+
+// noexcept support:
+#if defined(CLARA_CONFIG_CPP11_NOEXCEPT) && !defined(CLARA_NOEXCEPT)
+#define CLARA_NOEXCEPT noexcept
+#  define CLARA_NOEXCEPT_IS(x) noexcept(x)
+#else
+#define CLARA_NOEXCEPT throw()
+#  define CLARA_NOEXCEPT_IS(x)
+#endif
+
+// nullptr support
+#ifdef CLARA_CONFIG_CPP11_NULLPTR
+#define CLARA_NULL nullptr
+#else
+#define CLARA_NULL NULL
+#endif
+
+// override support
+#ifdef CLARA_CONFIG_CPP11_OVERRIDE
+#define CLARA_OVERRIDE override
+#else
+#define CLARA_OVERRIDE
+#endif
+
+// unique_ptr support
+#ifdef CLARA_CONFIG_CPP11_UNIQUE_PTR
+#   define CLARA_AUTO_PTR( T ) std::unique_ptr<T>
+#else
+#   define CLARA_AUTO_PTR( T ) std::auto_ptr<T>
+#endif
+
+#endif // TWOBLUECUBES_CLARA_COMPILERS_H_INCLUDED
+
+// ----------- end of #include from clara_compilers.h -----------
+// ........... back in clara.h
+
 #include <map>
-#include <algorithm>
 #include <stdexcept>
 #include <memory>
 
@@ -3762,6 +3957,9 @@ namespace Clara {
     const unsigned int consoleWidth = 80;
 #endif
 
+        // Use this to try and stop compiler from warning about unreachable code
+        inline bool isTrue( bool value ) { return value; }
+
         using namespace Tbc;
 
         inline bool startsWith( std::string const& str, std::string const& prefix ) {
@@ -3802,16 +4000,17 @@ namespace Clara {
         }
         template<typename T>
         inline void convertInto( bool, T& ) {
-            throw std::runtime_error( "Invalid conversion" );
+            if( isTrue( true ) )
+                throw std::runtime_error( "Invalid conversion" );
         }
 
         template<typename ConfigT>
         struct IArgFunction {
             virtual ~IArgFunction() {}
-#  ifdef CATCH_CONFIG_CPP11_GENERATED_METHODS
+#ifdef CLARA_CONFIG_CPP11_GENERATED_METHODS
             IArgFunction()                      = default;
             IArgFunction( IArgFunction const& ) = default;
-#  endif
+#endif
             virtual void set( ConfigT& config, std::string const& value ) const = 0;
             virtual void setFlag( ConfigT& config ) const = 0;
             virtual bool takesArg() const = 0;
@@ -3821,11 +4020,11 @@ namespace Clara {
         template<typename ConfigT>
         class BoundArgFunction {
         public:
-            BoundArgFunction() : functionObj( CATCH_NULL ) {}
+            BoundArgFunction() : functionObj( CLARA_NULL ) {}
             BoundArgFunction( IArgFunction<ConfigT>* _functionObj ) : functionObj( _functionObj ) {}
-            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CATCH_NULL ) {}
+            BoundArgFunction( BoundArgFunction const& other ) : functionObj( other.functionObj ? other.functionObj->clone() : CLARA_NULL ) {}
             BoundArgFunction& operator = ( BoundArgFunction const& other ) {
-                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CATCH_NULL;
+                IArgFunction<ConfigT>* newFunctionObj = other.functionObj ? other.functionObj->clone() : CLARA_NULL;
                 delete functionObj;
                 functionObj = newFunctionObj;
                 return *this;
@@ -3841,7 +4040,7 @@ namespace Clara {
             bool takesArg() const { return functionObj->takesArg(); }
 
             bool isSet() const {
-                return functionObj != CATCH_NULL;
+                return functionObj != CLARA_NULL;
             }
         private:
             IArgFunction<ConfigT>* functionObj;
@@ -3949,7 +4148,7 @@ namespace Clara {
             std::string data;
         };
 
-        void parseIntoTokens( int argc, char const * const * argv, std::vector<Parser::Token>& tokens ) const {
+        void parseIntoTokens( int argc, char const* const argv[], std::vector<Parser::Token>& tokens ) const {
             const std::string doubleDash = "--";
             for( int i = 1; i < argc && argv[i] != doubleDash; ++i )
                 parseIntoTokens( argv[i] , tokens);
@@ -4059,7 +4258,7 @@ namespace Clara {
             }
         };
 
-        typedef CATCH_AUTO_PTR( Arg ) ArgAutoPtr;
+        typedef CLARA_AUTO_PTR( Arg ) ArgAutoPtr;
 
         friend void addOptName( Arg& arg, std::string const& optName )
         {
@@ -4135,8 +4334,8 @@ namespace Clara {
                 m_arg->description = description;
                 return *this;
             }
-            ArgBuilder& detail( std::string const& _detail ) {
-                m_arg->detail = _detail;
+            ArgBuilder& detail( std::string const& detail ) {
+                m_arg->detail = detail;
                 return *this;
             }
 
@@ -4219,14 +4418,14 @@ namespace Clara {
                 maxWidth = (std::max)( maxWidth, it->commands().size() );
 
             for( it = itBegin; it != itEnd; ++it ) {
-                Detail::Text usageText( it->commands(), Detail::TextAttributes()
+                Detail::Text usage( it->commands(), Detail::TextAttributes()
                                                         .setWidth( maxWidth+indent )
                                                         .setIndent( indent ) );
                 Detail::Text desc( it->description, Detail::TextAttributes()
                                                         .setWidth( width - maxWidth - 3 ) );
 
-                for( std::size_t i = 0; i < (std::max)( usageText.size(), desc.size() ); ++i ) {
-                    std::string usageCol = i < usageText.size() ? usageText[i] : "";
+                for( std::size_t i = 0; i < (std::max)( usage.size(), desc.size() ); ++i ) {
+                    std::string usageCol = i < usage.size() ? usage[i] : "";
                     os << usageCol;
 
                     if( i < desc.size() && !desc[i].empty() )
@@ -4283,13 +4482,13 @@ namespace Clara {
             return oss.str();
         }
 
-        ConfigT parse( int argc, char const * const * argv ) const {
+        ConfigT parse( int argc, char const* const argv[] ) const {
             ConfigT config;
             parseInto( argc, argv, config );
             return config;
         }
 
-        std::vector<Parser::Token> parseInto( int argc, char const * const * argv, ConfigT& config ) const {
+        std::vector<Parser::Token> parseInto( int argc, char const* argv[], ConfigT& config ) const {
             std::string processName = argv[0];
             std::size_t lastSlash = processName.find_last_of( "/\\" );
             if( lastSlash != std::string::npos )
@@ -4471,6 +4670,21 @@ namespace Catch {
             ? ShowDurations::Always
             : ShowDurations::Never;
     }
+    inline void setUseColour( ConfigData& config, std::string const& value ) {
+        std::string mode = toLower( value );
+
+        if( mode == "yes" )
+            config.useColour = UseColour::Yes;
+        else if( mode == "no" )
+            config.useColour = UseColour::No;
+        else if( mode == "auto" )
+            config.useColour = UseColour::Auto;
+        else
+            throw std::runtime_error( "colour mode must be one of: auto, yes or no" );
+    }
+    inline void forceColour( ConfigData& config ) {
+        config.useColour = UseColour::Yes;
+    }
     inline void loadTestNamesFromFile( ConfigData& config, std::string const& _filename ) {
         std::ifstream f( _filename.c_str() );
         if( !f.is_open() )
@@ -4557,7 +4771,7 @@ namespace Catch {
 
         cli["-d"]["--durations"]
             .describe( "show test durations" )
-            .bind( &setShowDurations, "yes/no" );
+            .bind( &setShowDurations, "yes|no" );
 
         cli["-f"]["--input-file"]
             .describe( "load test names to run from a file" )
@@ -4585,8 +4799,12 @@ namespace Catch {
             .bind( &setRngSeed, "'time'|number" );
 
         cli["--force-colour"]
-            .describe( "force colourised output" )
-            .bind( &ConfigData::forceColour );
+            .describe( "force colourised output (deprecated)" )
+            .bind( &forceColour );
+
+        cli["--use-colour"]
+            .describe( "should output be colourised" )
+            .bind( &setUseColour, "yes|no" );
 
         return cli;
     }
@@ -5692,6 +5910,11 @@ namespace Catch {
             while( getCurrentContext().advanceGeneratorsForCurrentTest() && !aborting() );
 
             Totals deltaTotals = m_totals.delta( prevTotals );
+            if( testInfo.expectedToFail() && deltaTotals.testCases.passed > 0 ) {
+                deltaTotals.assertions.failed++;
+                deltaTotals.testCases.passed--;
+                deltaTotals.testCases.failed++;
+            }
             m_totals.testCases += deltaTotals.testCases;
             m_reporter->testCaseEnded( TestCaseStats(   testInfo,
                                                         deltaTotals,
@@ -6083,7 +6306,7 @@ namespace Catch {
             Catch::cout() << "For more detail usage please see the project docs\n" << std::endl;
         }
 
-        int applyCommandLine( int argc, char const* const argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
+        int applyCommandLine( int argc, char const* argv[], OnUnusedOptions::DoWhat unusedOptionBehaviour = OnUnusedOptions::Fail ) {
             try {
                 m_cli.setThrowOnUnrecognisedTokens( unusedOptionBehaviour == OnUnusedOptions::Fail );
                 m_unusedTokens = m_cli.parseInto( argc, argv, m_configData );
@@ -6110,13 +6333,16 @@ namespace Catch {
             m_config.reset();
         }
 
-        int run( int argc, char const* const argv[] ) {
+        int run( int argc, char const* argv[] ) {
 
             int returnCode = applyCommandLine( argc, argv );
             if( returnCode == 0 )
                 returnCode = run();
             return returnCode;
         }
+        int run( int argc, char* argv[] ) {
+            return run( argc, const_cast<char const**>( argv ) );
+        }
 
         int run() {
             if( m_configData.showHelp )
@@ -6815,7 +7041,18 @@ namespace {
 
     IColourImpl* platformColourInstance() {
         static Win32ColourImpl s_instance;
-        return &s_instance;
+
+        Ptr<IConfig const> config = getCurrentContext().getConfig();
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = !isDebuggerActive()
+                ? UseColour::Yes
+                : UseColour::No;
+        return colourMode == UseColour::Yes
+            ? &s_instance
+            : NoColourImpl::instance();
     }
 
 } // end anon namespace
@@ -6866,7 +7103,14 @@ namespace {
 
     IColourImpl* platformColourInstance() {
         Ptr<IConfig const> config = getCurrentContext().getConfig();
-        return (config && config->forceColour()) || isatty(STDOUT_FILENO)
+        UseColour::YesOrNo colourMode = config
+            ? config->useColour()
+            : UseColour::Auto;
+        if( colourMode == UseColour::Auto )
+            colourMode = (!isDebuggerActive() && isatty(STDOUT_FILENO) )
+                ? UseColour::Yes
+                : UseColour::No;
+        return colourMode == UseColour::Yes
             ? PosixColourImpl::instance()
             : NoColourImpl::instance();
     }
@@ -6891,9 +7135,7 @@ namespace Catch {
     Colour::~Colour(){ if( !m_moved ) use( None ); }
 
     void Colour::use( Code _colourCode ) {
-        static IColourImpl* impl = isDebuggerActive()
-            ? NoColourImpl::instance()
-            : platformColourInstance();
+        static IColourImpl* impl = platformColourInstance();
         impl->use( _colourCode );
     }
 
@@ -7270,7 +7512,7 @@ namespace Catch {
         return os;
     }
 
-    Version libraryVersion( 1, 3, 3, "", 0 );
+    Version libraryVersion( 1, 4, 0, "", 0 );
 
 }
 
@@ -9566,7 +9808,7 @@ namespace Catch {
             if( totals.testCases.total() == 0 ) {
                 stream << Colour( Colour::Warning ) << "No tests ran\n";
             }
-            else if( totals.assertions.total() > 0 && totals.assertions.allPassed() ) {
+            else if( totals.assertions.total() > 0 && totals.testCases.allPassed() ) {
                 stream << Colour( Colour::ResultSuccess ) << "All tests passed";
                 stream << " ("
                         << pluralise( totals.assertions.passed, "assertion" ) << " in "
diff --git a/test/t/area/test_node_ref_segment.cpp b/test/t/area/test_node_ref_segment.cpp
index 3261c24..5e33c2b 100644
--- a/test/t/area/test_node_ref_segment.cpp
+++ b/test/t/area/test_node_ref_segment.cpp
@@ -20,91 +20,109 @@ TEST_CASE("NodeRefSegmentClass") {
         osmium::NodeRef nr3(3, { 1.2, 3.6 });
         osmium::NodeRef nr4(4, { 1.2, 3.7 });
 
-        NodeRefSegment s1(nr1, nr2, nullptr, nullptr);
+        NodeRefSegment s1(nr1, nr2);
         REQUIRE(s1.first().ref() == 1);
         REQUIRE(s1.second().ref() == 2);
 
-        NodeRefSegment s2(nr2, nr3, nullptr, nullptr);
+        NodeRefSegment s2(nr2, nr3);
         REQUIRE(s2.first().ref() == 3);
         REQUIRE(s2.second().ref() == 2);
 
-        NodeRefSegment s3(nr3, nr4, nullptr, nullptr);
+        NodeRefSegment s3(nr3, nr4);
         REQUIRE(s3.first().ref() == 3);
         REQUIRE(s3.second().ref() == 4);
     }
 
     SECTION("intersection") {
-        NodeRefSegment s1({ 1, {0.0, 0.0}}, { 2, {2.0, 2.0}}, nullptr, nullptr);
-        NodeRefSegment s2({ 3, {0.0, 2.0}}, { 4, {2.0, 0.0}}, nullptr, nullptr);
-        NodeRefSegment s3({ 5, {2.0, 0.0}}, { 6, {4.0, 2.0}}, nullptr, nullptr);
-        NodeRefSegment s4({ 7, {1.0, 0.0}}, { 8, {3.0, 2.0}}, nullptr, nullptr);
-        NodeRefSegment s5({ 9, {0.0, 4.0}}, {10, {4.0, 0.0}}, nullptr, nullptr);
-        NodeRefSegment s6({11, {0.0, 0.0}}, {12, {1.0, 1.0}}, nullptr, nullptr);
-        NodeRefSegment s7({13, {1.0, 1.0}}, {14, {3.0, 3.0}}, nullptr, nullptr);
+        NodeRefSegment s1({ 1, {0.0, 0.0}}, { 2, {2.0, 2.0}});
+        NodeRefSegment s2({ 3, {0.0, 2.0}}, { 4, {2.0, 0.0}});
+        NodeRefSegment s3({ 5, {2.0, 0.0}}, { 6, {4.0, 2.0}});
+        NodeRefSegment s4({ 7, {1.0, 0.0}}, { 8, {3.0, 2.0}});
+        NodeRefSegment s5({ 9, {0.0, 4.0}}, {10, {4.0, 0.0}});
+        NodeRefSegment s6({11, {0.0, 0.0}}, {12, {1.0, 1.0}});
+        NodeRefSegment s7({13, {1.0, 1.0}}, {14, {3.0, 3.0}});
 
         REQUIRE(calculate_intersection(s1, s2) == osmium::Location(1.0, 1.0));
+        REQUIRE(calculate_intersection(s2, s1) == osmium::Location(1.0, 1.0));
+
         REQUIRE(calculate_intersection(s1, s3) == osmium::Location());
+        REQUIRE(calculate_intersection(s3, s1) == osmium::Location());
+
         REQUIRE(calculate_intersection(s2, s3) == osmium::Location());
+        REQUIRE(calculate_intersection(s3, s2) == osmium::Location());
+
         REQUIRE(calculate_intersection(s1, s4) == osmium::Location());
+        REQUIRE(calculate_intersection(s4, s1) == osmium::Location());
+
         REQUIRE(calculate_intersection(s1, s5) == osmium::Location(2.0, 2.0));
-        REQUIRE(calculate_intersection(s1, s1) == osmium::Location());
-        REQUIRE(calculate_intersection(s1, s6) == osmium::Location());
-        REQUIRE(calculate_intersection(s1, s7) == osmium::Location());
-    }
+        REQUIRE(calculate_intersection(s5, s1) == osmium::Location(2.0, 2.0));
 
-    SECTION("intersection of very long segments") {
-        NodeRefSegment s1({ 1, {90.0, 90.0}}, { 2, {-90.0, -90.0}}, nullptr, nullptr);
-        NodeRefSegment s2({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr);
-        REQUIRE(calculate_intersection(s1, s2) == osmium::Location(0.0, 0.0));
+        REQUIRE(calculate_intersection(s1, s6) == osmium::Location(1.0, 1.0));
+        REQUIRE(calculate_intersection(s6, s1) == osmium::Location(1.0, 1.0));
 
-        NodeRefSegment s3({ 1, {-90.0, -90.0}}, { 2, {90.0, 90.0}}, nullptr, nullptr);
-        NodeRefSegment s4({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr);
-        REQUIRE(calculate_intersection(s3, s4) == osmium::Location(0.0, 0.0));
+        REQUIRE(calculate_intersection(s1, s7) == osmium::Location(1.0, 1.0));
+        REQUIRE(calculate_intersection(s7, s1) == osmium::Location(1.0, 1.0));
 
-        NodeRefSegment s5({ 1, {-90.0000001, -90.0}}, { 2, {90.0, 90.0}}, nullptr, nullptr);
-        NodeRefSegment s6({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}}, nullptr, nullptr);
-        REQUIRE(calculate_intersection(s5, s6) == osmium::Location(0.0, 0.0));
+        REQUIRE(calculate_intersection(s6, s7) == osmium::Location());
+        REQUIRE(calculate_intersection(s7, s6) == osmium::Location());
     }
 
-    SECTION("to_left_of") {
-        osmium::Location loc { 2.0, 2.0 };
+    SECTION("intersection of collinear segments") {
+        NodeRefSegment s1({ 1, {0.0, 0.0}}, { 2, {2.0, 0.0}}); // *---*
+        NodeRefSegment s2({ 3, {2.0, 0.0}}, { 4, {4.0, 0.0}}); //     *---*
+        NodeRefSegment s3({ 5, {0.0, 0.0}}, { 6, {1.0, 0.0}}); // *-*
+        NodeRefSegment s4({ 7, {1.0, 0.0}}, { 8, {2.0, 0.0}}); //   *-*
+        NodeRefSegment s5({ 9, {1.0, 0.0}}, {10, {3.0, 0.0}}); //   *---*
+        NodeRefSegment s6({11, {0.0, 0.0}}, {12, {4.0, 0.0}}); // *-------*
+        NodeRefSegment s7({13, {0.0, 0.0}}, {14, {5.0, 0.0}}); // *---------*
+        NodeRefSegment s8({13, {1.0, 0.0}}, {14, {5.0, 0.0}}); //   *-------*
+        NodeRefSegment s9({13, {3.0, 0.0}}, {14, {4.0, 0.0}}); //       *-*
+
+        REQUIRE(calculate_intersection(s1, s1) == osmium::Location());
 
-        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {4.0, 0.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
-        REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(calculate_intersection(s1, s2) == osmium::Location());
+        REQUIRE(calculate_intersection(s2, s1) == osmium::Location());
 
-        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {3.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {4.0, 3.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(calculate_intersection(s1, s3) == osmium::Location(1.0, 0.0));
+        REQUIRE(calculate_intersection(s3, s1) == osmium::Location(1.0, 0.0));
 
-        REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {2.0, 0.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {3.0, 1.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {3.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(calculate_intersection(s1, s4) == osmium::Location(1.0, 0.0));
+        REQUIRE(calculate_intersection(s4, s1) == osmium::Location(1.0, 0.0));
 
-        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(calculate_intersection(s1, s5) == osmium::Location(1.0, 0.0));
+        REQUIRE(calculate_intersection(s5, s1) == osmium::Location(1.0, 0.0));
 
-        REQUIRE(NodeRefSegment({0, {2.0, 0.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {2.0, 0.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
-        REQUIRE(NodeRefSegment({0, {2.0, 2.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(calculate_intersection(s1, s6) == osmium::Location(2.0, 0.0));
+        REQUIRE(calculate_intersection(s6, s1) == osmium::Location(2.0, 0.0));
 
-        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 1.0}}, nullptr, nullptr).to_left_of(loc) == false);
-        REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {0.0, 1.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(calculate_intersection(s1, s7) == osmium::Location(2.0, 0.0));
+        REQUIRE(calculate_intersection(s7, s1) == osmium::Location(2.0, 0.0));
 
-        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {1.0, 3.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {2.0, 0.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {3.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(calculate_intersection(s1, s8) == osmium::Location(1.0, 0.0));
+        REQUIRE(calculate_intersection(s8, s1) == osmium::Location(1.0, 0.0));
 
-        REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {1.0, 2.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {1.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
-        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(calculate_intersection(s1, s9) == osmium::Location());
+        REQUIRE(calculate_intersection(s9, s1) == osmium::Location());
 
-        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 2.0}}, nullptr, nullptr).to_left_of(loc));
-        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(calculate_intersection(s5, s6) == osmium::Location(1.0, 0.0));
+        REQUIRE(calculate_intersection(s6, s5) == osmium::Location(1.0, 0.0));
 
-        REQUIRE(NodeRefSegment({0, {0.0, 1.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
-        REQUIRE(NodeRefSegment({0, {2.0, 2.0}}, {1, {4.0, 0.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(calculate_intersection(s7, s8) == osmium::Location(1.0, 0.0));
+        REQUIRE(calculate_intersection(s8, s7) == osmium::Location(1.0, 0.0));
+    }
+
+    SECTION("intersection of very long segments") {
+        NodeRefSegment s1({ 1, {90.0, 90.0}}, { 2, {-90.0, -90.0}});
+        NodeRefSegment s2({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}});
+        REQUIRE(calculate_intersection(s1, s2) == osmium::Location(0.0, 0.0));
+
+        NodeRefSegment s3({ 1, {-90.0, -90.0}}, { 2, {90.0, 90.0}});
+        NodeRefSegment s4({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}});
+        REQUIRE(calculate_intersection(s3, s4) == osmium::Location(0.0, 0.0));
+
+        NodeRefSegment s5({ 1, {-90.00000001, -90.0}}, { 2, {90.0, 90.0}});
+        NodeRefSegment s6({ 1, {-90.0, 90.0}}, { 2, {90.0, -90.0}});
+        REQUIRE(calculate_intersection(s5, s6) == osmium::Location(0.0, 0.0));
     }
 
     SECTION("ordering") {
@@ -127,3 +145,64 @@ TEST_CASE("NodeRefSegmentClass") {
 
 }
 
+TEST_CASE("Ordering of NodeRefSegments") {
+    osmium::NodeRef nr0(0, { 0.0, 0.0 });
+    osmium::NodeRef nr1(1, { 1.0, 0.0 });
+    osmium::NodeRef nr2(2, { 0.0, 1.0 });
+    osmium::NodeRef nr3(3, { 2.0, 0.0 });
+    osmium::NodeRef nr4(4, { 0.0, 2.0 });
+    osmium::NodeRef nr5(5, { 1.0, 1.0 });
+    osmium::NodeRef nr6(6, { 2.0, 2.0 });
+    osmium::NodeRef nr7(6, { 1.0, 2.0 });
+
+    NodeRefSegment s1(nr0, nr1);
+    NodeRefSegment s2(nr0, nr2);
+    NodeRefSegment s3(nr0, nr3);
+    NodeRefSegment s4(nr0, nr4);
+    NodeRefSegment s5(nr0, nr5);
+    NodeRefSegment s6(nr0, nr6);
+    NodeRefSegment s7(nr0, nr7);
+
+    // s1
+    REQUIRE_FALSE(s1 < s1);
+    REQUIRE(s2 < s1);
+    REQUIRE(s1 < s3);
+    REQUIRE(s4 < s1);
+    REQUIRE(s5 < s1);
+    REQUIRE(s6 < s1);
+    REQUIRE(s7 < s1);
+
+    // s2
+    REQUIRE_FALSE(s2 < s2);
+    REQUIRE(s2 < s3);
+    REQUIRE(s2 < s4);
+    REQUIRE(s2 < s5);
+    REQUIRE(s2 < s6);
+    REQUIRE(s2 < s7);
+
+    // s3
+    REQUIRE_FALSE(s3 < s3);
+    REQUIRE(s4 < s3);
+    REQUIRE(s5 < s3);
+    REQUIRE(s6 < s3);
+    REQUIRE(s7 < s3);
+
+    // s4
+    REQUIRE_FALSE(s4 < s4);
+    REQUIRE(s4 < s5);
+    REQUIRE(s4 < s6);
+    REQUIRE(s4 < s7);
+
+    // s5
+    REQUIRE_FALSE(s5 < s5);
+    REQUIRE(s5 < s6);
+    REQUIRE(s7 < s5);
+
+    // s6
+    REQUIRE_FALSE(s6 < s6);
+    REQUIRE(s7 < s6);
+
+    // s7
+    REQUIRE_FALSE(s7 < s7);
+}
+
diff --git a/test/t/basic/test_entity_bits.cpp b/test/t/basic/test_entity_bits.cpp
index f15068b..13de94b 100644
--- a/test/t/basic/test_entity_bits.cpp
+++ b/test/t/basic/test_entity_bits.cpp
@@ -21,6 +21,7 @@ TEST_CASE("entity_bits") {
         REQUIRE(! (entities & osmium::osm_entity_bits::way));
         REQUIRE(entities == osmium::osm_entity_bits::node);
 
+        REQUIRE(osmium::osm_entity_bits::nothing   == osmium::osm_entity_bits::from_item_type(osmium::item_type::undefined));
         REQUIRE(osmium::osm_entity_bits::node      == osmium::osm_entity_bits::from_item_type(osmium::item_type::node));
         REQUIRE(osmium::osm_entity_bits::way       == osmium::osm_entity_bits::from_item_type(osmium::item_type::way));
         REQUIRE(osmium::osm_entity_bits::relation  == osmium::osm_entity_bits::from_item_type(osmium::item_type::relation));
diff --git a/test/t/basic/test_location.cpp b/test/t/basic/test_location.cpp
index 3fd8d15..c861722 100644
--- a/test/t/basic/test_location.cpp
+++ b/test/t/basic/test_location.cpp
@@ -137,7 +137,7 @@ TEST_CASE("Location") {
     }
 
     SECTION("output_defined") {
-        osmium::Location p(-3.2, 47.3);
+        osmium::Location p(-3.20, 47.30);
         std::stringstream out;
         out << p;
         REQUIRE(out.str() == "(-3.2,47.3)");
@@ -152,3 +152,17 @@ TEST_CASE("Location") {
 
 }
 
+TEST_CASE("Location hash") {
+    if (sizeof(size_t) == 8) {
+        REQUIRE(std::hash<osmium::Location>{}({0, 0}) == 0);
+        REQUIRE(std::hash<osmium::Location>{}({0, 1}) == 1);
+        REQUIRE(std::hash<osmium::Location>{}({1, 0}) == 0x100000000);
+        REQUIRE(std::hash<osmium::Location>{}({1, 1}) == 0x100000001);
+    } else {
+        REQUIRE(std::hash<osmium::Location>{}({0, 0}) == 0);
+        REQUIRE(std::hash<osmium::Location>{}({0, 1}) == 1);
+        REQUIRE(std::hash<osmium::Location>{}({1, 0}) == 1);
+        REQUIRE(std::hash<osmium::Location>{}({1, 1}) == 0);
+    }
+}
+
diff --git a/test/t/basic/test_object_comparisons.cpp b/test/t/basic/test_object_comparisons.cpp
index 461969f..42bcbff 100644
--- a/test/t/basic/test_object_comparisons.cpp
+++ b/test/t/basic/test_object_comparisons.cpp
@@ -1,76 +1,80 @@
 #include "catch.hpp"
 
+#include <algorithm>
+#include <functional>
+#include <vector>
+
 #include <osmium/builder/attr.hpp>
 #include <osmium/builder/osm_object_builder.hpp>
 #include <osmium/osm.hpp>
 #include <osmium/osm/object_comparisons.hpp>
 
-TEST_CASE("Object_Comparisons") {
-
-    using namespace osmium::builder::attr;
-
-    SECTION("order") {
-        osmium::memory::Buffer buffer(10 * 1000);
-
-        osmium::builder::add_node(buffer, _id(10), _version(1));
-        osmium::builder::add_node(buffer, _id(15), _version(2));
-
-        auto it = buffer.begin();
-        osmium::Node& node1 = static_cast<osmium::Node&>(*it);
-        osmium::Node& node2 = static_cast<osmium::Node&>(*(++it));
-
-        REQUIRE(node1 < node2);
-        REQUIRE_FALSE(node1 > node2);
-        node1.set_id(20);
-        node1.set_version(1);
-        node2.set_id(20);
-        node2.set_version(2);
-        REQUIRE(node1 < node2);
-        REQUIRE_FALSE(node1 > node2);
-        node1.set_id(-10);
-        node1.set_version(2);
-        node2.set_id(-15);
-        node2.set_version(1);
-        REQUIRE(node1 < node2);
-        REQUIRE_FALSE(node1 > node2);
+using namespace osmium::builder::attr;
+
+TEST_CASE("Node comparisons") {
+
+    osmium::memory::Buffer buffer(10 * 1000);
+    std::vector<std::reference_wrapper<osmium::Node>> nodes;
+
+    SECTION("nodes are ordered by id, version, and timestamp") {
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(            0), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(            1), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(           10), _version(2), _timestamp("2016-01-01T00:01:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(           10), _version(3), _timestamp("2016-01-01T00:00:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(           12), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(           12), _version(2), _timestamp("2016-01-01T00:01:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(           15), _version(1), _timestamp("2016-01-01T00:00:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(10000000000ll), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+
+        REQUIRE(std::is_sorted(nodes.cbegin(), nodes.cend()));
     }
 
-    SECTION("order_types") {
-        osmium::memory::Buffer buffer(10 * 1000);
-
-        osmium::builder::add_node(buffer, _id(3), _version(3));
-        osmium::builder::add_node(buffer, _id(3), _version(4));
-        osmium::builder::add_node(buffer, _id(3), _version(4));
-        osmium::builder::add_way(buffer, _id(2), _version(2));
-        osmium::builder::add_relation(buffer, _id(1), _version(1));
-
-        auto it = buffer.begin();
-        const osmium::Node& node1 = static_cast<const osmium::Node&>(*it);
-        const osmium::Node& node2 = static_cast<const osmium::Node&>(*(++it));
-        const osmium::Node& node3 = static_cast<const osmium::Node&>(*(++it));
-        const osmium::Way& way = static_cast<const osmium::Way&>(*(++it));
-        const osmium::Relation& relation = static_cast<const osmium::Relation&>(*(++it));
-
-        REQUIRE(node1 < node2);
-        REQUIRE(node2 < way);
-        REQUIRE_FALSE(node2 > way);
-        REQUIRE(way < relation);
-        REQUIRE(node1 < relation);
-
-        REQUIRE(osmium::object_order_type_id_version()(node1, node2));
-        REQUIRE(osmium::object_order_type_id_reverse_version()(node2, node1));
-        REQUIRE(osmium::object_order_type_id_version()(node1, way));
-        REQUIRE(osmium::object_order_type_id_reverse_version()(node1, way));
-
-        REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, node2));
-        REQUIRE(osmium::object_equal_type_id_version()(node2, node3));
-
-        REQUIRE(osmium::object_equal_type_id()(node1, node2));
-        REQUIRE(osmium::object_equal_type_id()(node2, node3));
-
-        REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, way));
-        REQUIRE_FALSE(osmium::object_equal_type_id_version()(node1, relation));
-        REQUIRE_FALSE(osmium::object_equal_type_id()(node1, relation));
+    SECTION("equal nodes are not different") {
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(1), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(1), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+
+        REQUIRE(nodes[0] == nodes[1]);
+        REQUIRE_FALSE(nodes[0] < nodes[1]);
+        REQUIRE_FALSE(nodes[0] > nodes[1]);
+    }
+
+    SECTION("IDs are ordered by absolute value") {
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(  0))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(  1))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( -1))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id( 10))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(-10))));
+
+        REQUIRE(std::is_sorted(nodes.cbegin(), nodes.cend()));
+    }
+
+    SECTION("reverse version ordering") {
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(            0), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(            1), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(           10), _version(3), _timestamp("2016-01-01T00:00:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(           10), _version(2), _timestamp("2016-01-01T00:01:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(           12), _version(2), _timestamp("2016-01-01T00:01:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(           12), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(           15), _version(1), _timestamp("2016-01-01T00:00:00Z"))));
+        nodes.emplace_back(buffer.get<osmium::Node>(osmium::builder::add_node(buffer, _id(10000000000ll), _version(2), _timestamp("2016-01-01T00:00:00Z"))));
+
+        REQUIRE(std::is_sorted(nodes.cbegin(), nodes.cend(), osmium::object_order_type_id_reverse_version{}));
     }
 
 }
+
+TEST_CASE("Object comparisons") {
+
+    osmium::memory::Buffer buffer(10 * 1000);
+    std::vector<std::reference_wrapper<osmium::OSMObject>> objects;
+
+    SECTION("types are ordered nodes, then ways, then relations") {
+        objects.emplace_back(buffer.get<osmium::Node>(    osmium::builder::add_node(    buffer, _id(3))));
+        objects.emplace_back(buffer.get<osmium::Way>(     osmium::builder::add_way(     buffer, _id(2))));
+        objects.emplace_back(buffer.get<osmium::Relation>(osmium::builder::add_relation(buffer, _id(1))));
+
+        REQUIRE(std::is_sorted(objects.cbegin(), objects.cend()));
+    }
+
+}
+
diff --git a/test/t/basic/test_types_from_string.cpp b/test/t/basic/test_types_from_string.cpp
index 2481ae8..2866b8f 100644
--- a/test/t/basic/test_types_from_string.cpp
+++ b/test/t/basic/test_types_from_string.cpp
@@ -31,9 +31,17 @@ TEST_CASE("set type and ID from string") {
     REQUIRE(r_2.first == osmium::item_type::relation);
     REQUIRE(r_2.second == -2);
 
-    auto x3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr);
-    REQUIRE(x3.first == osmium::item_type::undefined);
-    REQUIRE(x3.second == 3);
+    auto d3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr);
+    REQUIRE(d3.first == osmium::item_type::undefined);
+    REQUIRE(d3.second == 3);
+
+    auto u3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr, osmium::item_type::undefined);
+    REQUIRE(u3.first == osmium::item_type::undefined);
+    REQUIRE(u3.second == 3);
+
+    auto n3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr, osmium::item_type::node);
+    REQUIRE(n3.first == osmium::item_type::node);
+    REQUIRE(n3.second == 3);
 
     REQUIRE_THROWS_AS(osmium::string_to_object_id("", osmium::osm_entity_bits::nwr), std::range_error);
     REQUIRE_THROWS_AS(osmium::string_to_object_id("n", osmium::osm_entity_bits::nwr), std::range_error);
diff --git a/test/t/basic/test_way.cpp b/test/t/basic/test_way.cpp
index c4fa9d5..005ef30 100644
--- a/test/t/basic/test_way.cpp
+++ b/test/t/basic/test_way.cpp
@@ -46,7 +46,7 @@ TEST_CASE("Build way") {
 
     osmium::CRC<boost::crc_32_type> crc32;
     crc32.update(way);
-    REQUIRE(crc32().checksum() == 0x7676d0c2);
+    REQUIRE(crc32().checksum() == 0x65f6ba91);
 }
 
 TEST_CASE("build closed way") {
diff --git a/test/t/builder/test_attr.cpp b/test/t/builder/test_attr.cpp
index 258ae7a..dedde77 100644
--- a/test/t/builder/test_attr.cpp
+++ b/test/t/builder/test_attr.cpp
@@ -67,14 +67,14 @@ TEST_CASE("create node using builders") {
         osmium::builder::add_node(buffer, _id(5), _visible(true));
         osmium::builder::add_node(buffer, _id(6), _visible(false));
 
-        auto it = buffer.cbegin<osmium::Node>();
+        auto it = buffer.select<osmium::Node>().cbegin();
         REQUIRE_FALSE(it++->visible());
         REQUIRE_FALSE(it++->visible());
         REQUIRE(it++->visible());
         REQUIRE(it++->visible());
         REQUIRE(it++->visible());
         REQUIRE_FALSE(it++->visible());
-        REQUIRE(it == buffer.cend<osmium::Node>());
+        REQUIRE(it == buffer.select<osmium::Node>().cend());
     }
 
     SECTION("order of attributes doesn't matter") {
diff --git a/test/t/geom/test_geojson.cpp b/test/t/geom/test_geojson.cpp
index a9e8398..725be7f 100644
--- a/test/t/geom/test_geojson.cpp
+++ b/test/t/geom/test_geojson.cpp
@@ -96,7 +96,7 @@ SECTION("area_1outer_0inner") {
 
     REQUIRE(!area.is_multipolygon());
     REQUIRE(std::distance(area.cbegin(), area.cend()) == 2);
-    REQUIRE(std::distance(area.cbegin<osmium::OuterRing>(), area.cend<osmium::OuterRing>()) == area.num_rings().first);
+    REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
 
     {
         std::string json {factory.create_multipolygon(area)};
@@ -112,8 +112,8 @@ SECTION("area_1outer_1inner") {
 
     REQUIRE(!area.is_multipolygon());
     REQUIRE(std::distance(area.cbegin(), area.cend()) == 3);
-    REQUIRE(std::distance(area.cbegin<osmium::OuterRing>(), area.cend<osmium::OuterRing>()) == area.num_rings().first);
-    REQUIRE(std::distance(area.cbegin<osmium::InnerRing>(), area.cend<osmium::InnerRing>()) == area.num_rings().second);
+    REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
+    REQUIRE(area.subitems<osmium::InnerRing>().size() == area.num_rings().second);
 
     {
         std::string json {factory.create_multipolygon(area)};
@@ -129,24 +129,24 @@ SECTION("area_2outer_2inner") {
 
     REQUIRE(area.is_multipolygon());
     REQUIRE(std::distance(area.cbegin(), area.cend()) == 5);
-    REQUIRE(std::distance(area.cbegin<osmium::OuterRing>(), area.cend<osmium::OuterRing>()) == area.num_rings().first);
-    REQUIRE(std::distance(area.cbegin<osmium::InnerRing>(), area.cend<osmium::InnerRing>()) == area.num_rings().second);
+    REQUIRE(area.subitems<osmium::OuterRing>().size() == area.num_rings().first);
+    REQUIRE(area.subitems<osmium::InnerRing>().size() == area.num_rings().second);
 
     int outer_ring=0;
     int inner_ring=0;
-    for (auto it_outer = area.cbegin<osmium::OuterRing>(); it_outer !=  area.cend<osmium::OuterRing>(); ++it_outer) {
+    for (const auto& outer : area.outer_rings()) {
         if (outer_ring == 0) {
-            REQUIRE(it_outer->front().ref() == 1);
+            REQUIRE(outer.front().ref() == 1);
         } else if (outer_ring == 1) {
-            REQUIRE(it_outer->front().ref() == 100);
+            REQUIRE(outer.front().ref() == 100);
         } else {
             REQUIRE(false);
         }
-        for (auto it_inner = area.inner_ring_cbegin(it_outer); it_inner != area.inner_ring_cend(it_outer); ++it_inner) {
+        for (const auto& inner : area.inner_rings(outer)) {
             if (outer_ring == 0 && inner_ring == 0) {
-                REQUIRE(it_inner->front().ref() == 5);
+                REQUIRE(inner.front().ref() == 5);
             } else if (outer_ring == 0 && inner_ring == 1) {
-                REQUIRE(it_inner->front().ref() == 10);
+                REQUIRE(inner.front().ref() == 10);
             } else {
                 REQUIRE(false);
             }
diff --git a/test/t/geom/test_geos.cpp b/test/t/geom/test_geos.cpp
index 8bf11c9..d85a603 100644
--- a/test/t/geom/test_geos.cpp
+++ b/test/t/geom/test_geos.cpp
@@ -89,6 +89,7 @@ TEST_CASE("GEOS geometry factory - create area with one outer and no inner rings
     REQUIRE(1 == mp->getNumGeometries());
 
     const geos::geom::Polygon* p0 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(0));
+    REQUIRE(p0);
     REQUIRE(0 == p0->getNumInteriorRing());
 
     const geos::geom::LineString* l0e = p0->getExteriorRing();
@@ -108,6 +109,7 @@ TEST_CASE("GEOS geometry factory - create area with one outer and one inner ring
     REQUIRE(1 == mp->getNumGeometries());
 
     const geos::geom::Polygon* p0 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(0));
+    REQUIRE(p0);
     REQUIRE(1 == p0->getNumInteriorRing());
 
     const geos::geom::LineString* l0e = p0->getExteriorRing();
@@ -127,12 +129,14 @@ TEST_CASE("GEOS geometry factory - create area with two outer and two inner ring
     REQUIRE(2 == mp->getNumGeometries());
 
     const geos::geom::Polygon* p0 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(0));
+    REQUIRE(p0);
     REQUIRE(2 == p0->getNumInteriorRing());
 
     const geos::geom::LineString* l0e = p0->getExteriorRing();
     REQUIRE(5 == l0e->getNumPoints());
 
     const geos::geom::Polygon* p1 = dynamic_cast<const geos::geom::Polygon*>(mp->getGeometryN(1));
+    REQUIRE(p1);
     REQUIRE(0 == p1->getNumInteriorRing());
 
     const geos::geom::LineString* l1e = p1->getExteriorRing();
diff --git a/test/t/geom/test_ogr.cpp b/test/t/geom/test_ogr.cpp
index 26d34c1..3490c57 100644
--- a/test/t/geom/test_ogr.cpp
+++ b/test/t/geom/test_ogr.cpp
@@ -68,6 +68,7 @@ SECTION("area_1outer_0inner") {
     REQUIRE(1 == mp->getNumGeometries());
 
     const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+    REQUIRE(p0);
     REQUIRE(0 == p0->getNumInteriorRings());
 
     const OGRLineString* l0e = p0->getExteriorRing();
@@ -86,6 +87,7 @@ SECTION("area_1outer_1inner") {
     REQUIRE(1 == mp->getNumGeometries());
 
     const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+    REQUIRE(p0);
     REQUIRE(1 == p0->getNumInteriorRings());
 
     const OGRLineString* l0e = p0->getExteriorRing();
@@ -105,12 +107,14 @@ SECTION("area_2outer_2inner") {
     REQUIRE(2 == mp->getNumGeometries());
 
     const OGRPolygon* p0 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(0));
+    REQUIRE(p0);
     REQUIRE(2 == p0->getNumInteriorRings());
 
     const OGRLineString* l0e = p0->getExteriorRing();
     REQUIRE(5 == l0e->getNumPoints());
 
     const OGRPolygon* p1 = dynamic_cast<const OGRPolygon*>(mp->getGeometryRef(1));
+    REQUIRE(p1);
     REQUIRE(0 == p1->getNumInteriorRings());
 
     const OGRLineString* l1e = p1->getExteriorRing();
diff --git a/test/t/index/test_file_based_index.cpp b/test/t/index/test_file_based_index.cpp
new file mode 100644
index 0000000..42cf574
--- /dev/null
+++ b/test/t/index/test_file_based_index.cpp
@@ -0,0 +1,155 @@
+
+#include "catch.hpp"
+
+#include <osmium/osm/types.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/index/detail/tmpfile.hpp>
+#include <osmium/util/file.hpp>
+
+#include <osmium/index/map/dense_file_array.hpp>
+#include <osmium/index/map/sparse_file_array.hpp>
+
+#include <osmium/index/node_locations_map.hpp>
+
+TEST_CASE("File based index") {
+
+    int fd = osmium::detail::create_tmp_file();
+
+    REQUIRE(osmium::util::file_size(fd) == 0);
+
+    const osmium::unsigned_object_id_type id1 = 6;
+    const osmium::unsigned_object_id_type id2 = 3;
+    const osmium::Location loc1(1.2, 4.5);
+    const osmium::Location loc2(3.5, -7.2);
+
+    SECTION("dense index") {
+        using index_type = osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, osmium::Location>;
+        constexpr const size_t S = sizeof(index_type::element_type);
+
+        {
+            index_type index(fd);
+
+            REQUIRE(index.size() == 0);
+
+            REQUIRE_THROWS_AS(index.get(  0), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  1), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  3), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  5), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  6), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  7), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+            index.set(id1, loc1);
+            REQUIRE(index.size() == 7);
+
+            index.set(id2, loc2);
+            REQUIRE(index.size() == 7);
+
+            index.sort();
+
+            REQUIRE(loc1 == index.get(id1));
+            REQUIRE(loc2 == index.get(id2));
+
+            REQUIRE_THROWS_AS(index.get(  0), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  1), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  5), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  7), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+            REQUIRE(index.size() == 7);
+            REQUIRE(std::distance(index.cbegin(), index.cend()) == 7);
+
+            REQUIRE(osmium::util::file_size(fd) >= (6 * S));
+        }
+
+        {
+            index_type index(fd);
+            REQUIRE(osmium::util::file_size(fd) >= (6 * S));
+
+            REQUIRE(index.size() == 7);
+
+            REQUIRE(loc1 == index.get(id1));
+            REQUIRE(loc2 == index.get(id2));
+
+            REQUIRE_THROWS_AS(index.get(  0), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  1), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  5), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  7), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+            REQUIRE(index.size() == 7);
+            REQUIRE(std::distance(index.cbegin(), index.cend()) == 7);
+
+            auto it = index.cbegin();
+            REQUIRE(*it++ == osmium::Location{});
+            REQUIRE(*it++ == osmium::Location{});
+            REQUIRE(*it++ == osmium::Location{});
+            REQUIRE(*it++ == loc2);
+            REQUIRE(*it++ == osmium::Location{});
+            REQUIRE(*it++ == osmium::Location{});
+            REQUIRE(*it++ == loc1);
+            REQUIRE(it++ == index.cend());
+        }
+    }
+
+    SECTION("sparse index") {
+        using index_type = osmium::index::map::SparseFileArray<osmium::unsigned_object_id_type, osmium::Location>;
+        constexpr const size_t S = sizeof(index_type::element_type);
+
+        {
+            index_type index(fd);
+
+            REQUIRE(index.size() == 0);
+
+            REQUIRE_THROWS_AS(index.get(  0), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  1), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  3), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  5), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  6), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  7), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+            index.set(id1, loc1);
+            REQUIRE(index.size() == 1);
+
+            index.set(id2, loc2);
+            REQUIRE(index.size() == 2);
+
+            index.sort();
+
+            REQUIRE(loc1 == index.get(id1));
+            REQUIRE(loc2 == index.get(id2));
+
+            REQUIRE_THROWS_AS(index.get(  0), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  1), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  5), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  7), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+            REQUIRE(index.size() == 2);
+            REQUIRE(std::distance(index.cbegin(), index.cend()) == 2);
+
+            REQUIRE(osmium::util::file_size(fd) >= (2 * S));
+        }
+
+        {
+            index_type index(fd);
+            REQUIRE(osmium::util::file_size(fd) >= (2 * S));
+
+            REQUIRE(index.size() == 2);
+
+            REQUIRE(loc1 == index.get(id1));
+            REQUIRE(loc2 == index.get(id2));
+
+            REQUIRE_THROWS_AS(index.get(  0), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  1), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  5), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(  7), osmium::not_found);
+            REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
+
+            REQUIRE(index.size() == 2);
+            REQUIRE(std::distance(index.cbegin(), index.cend()) == 2);
+        }
+    }
+}
+
diff --git a/test/t/index/test_id_to_location.cpp b/test/t/index/test_id_to_location.cpp
index 4aca238..90cce3b 100644
--- a/test/t/index/test_id_to_location.cpp
+++ b/test/t/index/test_id_to_location.cpp
@@ -29,6 +29,8 @@ void test_func_all(TIndex& index) {
 
     index.sort();
 
+    REQUIRE_THROWS_AS(index.get(0), osmium::not_found);
+    REQUIRE_THROWS_AS(index.get(1), osmium::not_found);
     REQUIRE_THROWS_AS(index.get(5), osmium::not_found);
     REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
 }
@@ -48,12 +50,20 @@ void test_func_real(TIndex& index) {
     REQUIRE(loc1 == index.get(id1));
     REQUIRE(loc2 == index.get(id2));
 
+    REQUIRE_THROWS_AS(index.get(0), osmium::not_found);
+    REQUIRE_THROWS_AS(index.get(1), osmium::not_found);
     REQUIRE_THROWS_AS(index.get(5), osmium::not_found);
     REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
 
     index.clear();
 
     REQUIRE_THROWS_AS(index.get(id1), osmium::not_found);
+    REQUIRE_THROWS_AS(index.get(id2), osmium::not_found);
+
+    REQUIRE_THROWS_AS(index.get(0), osmium::not_found);
+    REQUIRE_THROWS_AS(index.get(1), osmium::not_found);
+    REQUIRE_THROWS_AS(index.get(5), osmium::not_found);
+    REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
 }
 
 TEST_CASE("IdToLocation") {
diff --git a/test/t/io/test_writer.cpp b/test/t/io/test_writer.cpp
index b56dfeb..d3c2836 100644
--- a/test/t/io/test_writer.cpp
+++ b/test/t/io/test_writer.cpp
@@ -18,9 +18,9 @@ TEST_CASE("Writer") {
     osmium::memory::Buffer buffer = reader.read();
     REQUIRE(buffer);
     REQUIRE(buffer.committed() > 0);
-    auto num = std::distance(buffer.cbegin<osmium::OSMObject>(), buffer.cend<osmium::OSMObject>());
+    auto num = std::distance(buffer.select<osmium::OSMObject>().cbegin(), buffer.select<osmium::OSMObject>().cend());
     REQUIRE(num > 0);
-    REQUIRE(buffer.cbegin<osmium::OSMObject>()->id() == 1);
+    REQUIRE(buffer.select<osmium::OSMObject>().cbegin()->id() == 1);
 
     std::string filename;
 
@@ -81,9 +81,8 @@ TEST_CASE("Writer") {
         osmium::memory::Buffer buffer_check = reader_check.read();
         REQUIRE(buffer_check);
         REQUIRE(buffer_check.committed() > 0);
-        REQUIRE(std::distance(buffer_check.cbegin<osmium::OSMObject>(), buffer_check.cend<osmium::OSMObject>()) == num);
-        REQUIRE(buffer_check.cbegin<osmium::OSMObject>()->id() == 1);
-
+        REQUIRE(buffer_check.select<osmium::OSMObject>().size() == num);
+        REQUIRE(buffer_check.select<osmium::OSMObject>().cbegin()->id() == 1);
     }
 
     SECTION("Interrupted writer after open") {
diff --git a/test/t/io/test_writer_with_mock_compression.cpp b/test/t/io/test_writer_with_mock_compression.cpp
index c2d3bbd..10c19c4 100644
--- a/test/t/io/test_writer_with_mock_compression.cpp
+++ b/test/t/io/test_writer_with_mock_compression.cpp
@@ -56,8 +56,7 @@ TEST_CASE("Write with mock compressor") {
     osmium::memory::Buffer buffer = reader.read();
     REQUIRE(buffer);
     REQUIRE(buffer.committed() > 0);
-    auto num = std::distance(buffer.cbegin<osmium::OSMObject>(), buffer.cend<osmium::OSMObject>());
-    REQUIRE(num > 0);
+    REQUIRE(buffer.select<osmium::OSMObject>().size() > 0);
 
     SECTION("fail on construction") {
 
diff --git a/test/t/io/test_writer_with_mock_encoder.cpp b/test/t/io/test_writer_with_mock_encoder.cpp
index a43d591..d059f6b 100644
--- a/test/t/io/test_writer_with_mock_encoder.cpp
+++ b/test/t/io/test_writer_with_mock_encoder.cpp
@@ -62,8 +62,7 @@ TEST_CASE("Test Writer with MockOutputFormat") {
     osmium::memory::Buffer buffer = reader.read();
     REQUIRE(buffer);
     REQUIRE(buffer.committed() > 0);
-    auto num = std::distance(buffer.cbegin<osmium::OSMObject>(), buffer.cend<osmium::OSMObject>());
-    REQUIRE(num > 0);
+    REQUIRE(buffer.select<osmium::OSMObject>().size() > 0);
 
     SECTION("error in header") {
 
diff --git a/test/t/tags/test_tag_list.cpp b/test/t/tags/test_tag_list.cpp
index 470a200..3bdf5e9 100644
--- a/test/t/tags/test_tag_list.cpp
+++ b/test/t/tags/test_tag_list.cpp
@@ -167,10 +167,16 @@ TEST_CASE("create tag list") {
         });
     }
 
-    const osmium::TagList& tl = *buffer.begin<osmium::TagList>();
+    const osmium::TagList& tl = *buffer.select<osmium::TagList>().cbegin();
     REQUIRE(osmium::item_type::tag_list == tl.type());
     REQUIRE(2 == tl.size());
 
+    REQUIRE(tl.has_key("highway"));
+    REQUIRE_FALSE(tl.has_key("unknown"));
+    REQUIRE(tl.has_tag("highway", "primary"));
+    REQUIRE_FALSE(tl.has_tag("highway", "false"));
+    REQUIRE_FALSE(tl.has_tag("foo", "bar"));
+
     auto it = tl.begin();
     REQUIRE(std::string("highway")     == it->key());
     REQUIRE(std::string("primary")     == it->value());
diff --git a/test/t/util/test_memory_mapping.cpp b/test/t/util/test_memory_mapping.cpp
index 29893f7..647d2a0 100644
--- a/test/t/util/test_memory_mapping.cpp
+++ b/test/t/util/test_memory_mapping.cpp
@@ -33,15 +33,10 @@ TEST_CASE("anonymous mapping") {
         mapping.unmap(); // second unmap is okay
     }
 
-    SECTION("memory mapping of zero length should work") {
-        osmium::util::MemoryMapping mapping(0, osmium::util::MemoryMapping::mapping_mode::write_private);
-        REQUIRE(mapping.get_addr() != nullptr);
-
-        REQUIRE(mapping.size() == osmium::util::get_pagesize());
-
-        REQUIRE(!!mapping);
-        mapping.unmap();
-        REQUIRE(!mapping);
+    SECTION("memory mapping of zero length should fail") {
+        REQUIRE_THROWS({
+             osmium::util::MemoryMapping mapping(0, osmium::util::MemoryMapping::mapping_mode::write_private);
+        });
     }
 
     SECTION("moving a memory mapping should work") {

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