[libosmium] 01/07: Imported Upstream version 2.11.0

Bas Couwenberg sebastic at debian.org
Sat Jan 14 12:31:10 UTC 2017


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

sebastic pushed a commit to branch master
in repository libosmium.

commit c88273e13f85529eb02760b0e974c9ff7ea540c3
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sat Jan 14 12:06:49 2017 +0100

    Imported Upstream version 2.11.0
---
 CHANGELOG.md                                       |  37 ++
 CMakeLists.txt                                     |   4 +-
 CONTRIBUTING.md                                    |   2 +-
 Makefile                                           |  25 -
 NOTES_FOR_DEVELOPERS.md                            |  18 +-
 README.md                                          |  46 +-
 doc/Doxyfile.in                                    |   6 +-
 doc/doc.md                                         |  21 +
 doc/doc.txt                                        |  26 -
 examples/CMakeLists.txt                            |   1 +
 examples/README.md                                 |   7 +-
 examples/osmium_amenity_list.cpp                   | 167 +++++++
 examples/osmium_area_test.cpp                      |   1 +
 include/osmium/area/assembler.hpp                  |   2 +-
 include/osmium/area/detail/node_ref_segment.hpp    |   2 +-
 include/osmium/area/detail/proto_ring.hpp          |   2 +-
 include/osmium/area/detail/segment_list.hpp        |   2 +-
 include/osmium/area/detail/vector.hpp              |   2 +-
 include/osmium/area/multipolygon_collector.hpp     |   7 +-
 include/osmium/area/problem_reporter.hpp           |   2 +-
 include/osmium/area/problem_reporter_exception.hpp |   2 +-
 include/osmium/area/problem_reporter_ogr.hpp       |   2 +-
 include/osmium/area/problem_reporter_stream.hpp    |   2 +-
 include/osmium/area/stats.hpp                      |   2 +-
 include/osmium/builder/attr.hpp                    |   2 +-
 include/osmium/builder/builder.hpp                 |   2 +-
 include/osmium/builder/builder_helper.hpp          |   2 +-
 include/osmium/builder/osm_object_builder.hpp      |   2 +-
 include/osmium/diff_handler.hpp                    |   2 +-
 include/osmium/diff_iterator.hpp                   |   2 +-
 include/osmium/diff_visitor.hpp                    |   6 +-
 include/osmium/dynamic_handler.hpp                 |   5 +-
 include/osmium/experimental/flex_reader.hpp        |   2 +-
 include/osmium/fwd.hpp                             |   2 +-
 include/osmium/geom/coordinates.hpp                |  78 ++-
 include/osmium/geom/factory.hpp                    |   2 +-
 include/osmium/geom/geojson.hpp                    |   2 +-
 include/osmium/geom/geos.hpp                       |   2 +-
 include/osmium/geom/haversine.hpp                  |   4 +-
 include/osmium/geom/mercator_projection.hpp        |  53 ++-
 include/osmium/geom/ogr.hpp                        |   2 +-
 include/osmium/geom/projection.hpp                 |   2 +-
 include/osmium/geom/rapid_geojson.hpp              |   2 +-
 include/osmium/geom/relations.hpp                  |   2 +-
 include/osmium/geom/tile.hpp                       |  82 +++-
 include/osmium/geom/util.hpp                       |   2 +-
 include/osmium/geom/wkb.hpp                        |   2 +-
 include/osmium/geom/wkt.hpp                        |   2 +-
 include/osmium/handler.hpp                         |   2 +-
 include/osmium/handler/chain.hpp                   |   6 +-
 include/osmium/handler/check_order.hpp             |   8 +-
 include/osmium/handler/disk_store.hpp              |   2 +-
 include/osmium/handler/dump.hpp                    |   2 +-
 include/osmium/handler/node_locations_for_ways.hpp |   6 +-
 include/osmium/handler/object_relations.hpp        |   2 +-
 include/osmium/index/bool_vector.hpp               |   2 +-
 include/osmium/index/detail/create_map_with_fd.hpp |   2 +-
 include/osmium/index/detail/mmap_vector_anon.hpp   |   2 +-
 include/osmium/index/detail/mmap_vector_base.hpp   |  27 +-
 include/osmium/index/detail/mmap_vector_file.hpp   |   2 +-
 include/osmium/index/detail/tmpfile.hpp            |   2 +-
 include/osmium/index/detail/vector_map.hpp         |  57 ++-
 include/osmium/index/detail/vector_multimap.hpp    |   2 +-
 include/osmium/index/id_set.hpp                    |   2 +-
 include/osmium/index/index.hpp                     |   2 +-
 include/osmium/index/map.hpp                       |  23 +-
 include/osmium/index/map/all.hpp                   |   2 +-
 include/osmium/index/map/dense_file_array.hpp      |   2 +-
 include/osmium/index/map/dense_mem_array.hpp       |   2 +-
 include/osmium/index/map/dense_mmap_array.hpp      |   2 +-
 include/osmium/index/map/dummy.hpp                 |   8 +-
 include/osmium/index/map/sparse_file_array.hpp     |   2 +-
 include/osmium/index/map/sparse_mem_array.hpp      |   2 +-
 include/osmium/index/map/sparse_mem_map.hpp        |  14 +-
 include/osmium/index/map/sparse_mem_table.hpp      |  14 +-
 include/osmium/index/map/sparse_mmap_array.hpp     |   2 +-
 include/osmium/index/multimap.hpp                  |   2 +-
 include/osmium/index/multimap/all.hpp              |   2 +-
 include/osmium/index/multimap/hybrid.hpp           |   2 +-
 .../osmium/index/multimap/sparse_file_array.hpp    |   2 +-
 include/osmium/index/multimap/sparse_mem_array.hpp |   2 +-
 .../osmium/index/multimap/sparse_mem_multimap.hpp  |   2 +-
 .../osmium/index/multimap/sparse_mmap_array.hpp    |   2 +-
 include/osmium/index/node_locations_map.hpp        |   2 +-
 include/osmium/index/relations_map.hpp             | 292 ++++++++++++
 include/osmium/io/any_compression.hpp              |   2 +-
 include/osmium/io/any_input.hpp                    |   2 +-
 include/osmium/io/any_output.hpp                   |   2 +-
 include/osmium/io/bzip2_compression.hpp            |   2 +-
 include/osmium/io/compression.hpp                  |   2 +-
 include/osmium/io/debug_output.hpp                 |   2 +-
 include/osmium/io/detail/debug_output_format.hpp   |   2 +-
 include/osmium/io/detail/input_format.hpp          |   2 +-
 include/osmium/io/detail/o5m_input_format.hpp      |   2 +-
 include/osmium/io/detail/opl_input_format.hpp      |   2 +-
 include/osmium/io/detail/opl_output_format.hpp     |   2 +-
 include/osmium/io/detail/opl_parser_functions.hpp  |   2 +-
 include/osmium/io/detail/output_format.hpp         |   2 +-
 include/osmium/io/detail/pbf.hpp                   |   2 +-
 include/osmium/io/detail/pbf_decoder.hpp           |   2 +-
 include/osmium/io/detail/pbf_input_format.hpp      |   2 +-
 include/osmium/io/detail/pbf_output_format.hpp     |   2 +-
 include/osmium/io/detail/protobuf_tags.hpp         |   2 +-
 include/osmium/io/detail/queue_util.hpp            |   2 +-
 include/osmium/io/detail/read_thread.hpp           |   2 +-
 include/osmium/io/detail/read_write.hpp            |   2 +-
 include/osmium/io/detail/string_table.hpp          |   2 +-
 include/osmium/io/detail/string_util.hpp           |   2 +-
 include/osmium/io/detail/write_thread.hpp          |   2 +-
 include/osmium/io/detail/xml_input_format.hpp      |   2 +-
 include/osmium/io/detail/xml_output_format.hpp     |   2 +-
 include/osmium/io/detail/zlib.hpp                  |   2 +-
 include/osmium/io/error.hpp                        |   2 +-
 include/osmium/io/file.hpp                         |   2 +-
 include/osmium/io/file_compression.hpp             |   2 +-
 include/osmium/io/file_format.hpp                  |   2 +-
 include/osmium/io/gzip_compression.hpp             |   2 +-
 include/osmium/io/header.hpp                       |   2 +-
 include/osmium/io/input_iterator.hpp               |   2 +-
 include/osmium/io/o5m_input.hpp                    |   2 +-
 include/osmium/io/opl_input.hpp                    |   2 +-
 include/osmium/io/opl_output.hpp                   |   2 +-
 include/osmium/io/output_iterator.hpp              |   2 +-
 include/osmium/io/overwrite.hpp                    |   2 +-
 include/osmium/io/pbf_input.hpp                    |   2 +-
 include/osmium/io/pbf_output.hpp                   |   2 +-
 include/osmium/io/reader.hpp                       |   2 +-
 include/osmium/io/reader_iterator.hpp              |   2 +-
 include/osmium/io/writer.hpp                       |   2 +-
 include/osmium/io/writer_options.hpp               |   2 +-
 include/osmium/io/xml_input.hpp                    |   2 +-
 include/osmium/io/xml_output.hpp                   |   2 +-
 include/osmium/memory/buffer.hpp                   |   4 +-
 include/osmium/memory/collection.hpp               |   8 +-
 include/osmium/memory/item.hpp                     |   6 +-
 include/osmium/memory/item_iterator.hpp            |  89 +---
 include/osmium/object_pointer_collection.hpp       |   2 +-
 include/osmium/opl.hpp                             |   2 +-
 include/osmium/osm.hpp                             |   2 +-
 include/osmium/osm/area.hpp                        |  34 +-
 include/osmium/osm/box.hpp                         |   2 +-
 include/osmium/osm/changeset.hpp                   |   8 +-
 include/osmium/osm/crc.hpp                         |   2 +-
 include/osmium/osm/diff_object.hpp                 |   2 +-
 include/osmium/osm/entity.hpp                      |  14 +-
 include/osmium/osm/entity_bits.hpp                 |  22 +-
 include/osmium/osm/item_type.hpp                   |   2 +-
 include/osmium/osm/location.hpp                    |   2 +-
 include/osmium/osm/node.hpp                        |   6 +-
 include/osmium/osm/node_ref.hpp                    |   2 +-
 include/osmium/osm/node_ref_list.hpp               |  32 +-
 include/osmium/osm/object.hpp                      |  16 +-
 include/osmium/osm/object_comparisons.hpp          |   2 +-
 include/osmium/osm/relation.hpp                    |  11 +-
 include/osmium/osm/segment.hpp                     |   2 +-
 include/osmium/osm/tag.hpp                         |   2 +-
 include/osmium/osm/timestamp.hpp                   |   2 +-
 include/osmium/osm/types.hpp                       |   2 +-
 include/osmium/osm/types_from_string.hpp           |   6 +-
 include/osmium/osm/undirected_segment.hpp          |   2 +-
 include/osmium/osm/way.hpp                         |  23 +-
 include/osmium/relations/collector.hpp             |  44 +-
 include/osmium/relations/detail/member_meta.hpp    |   2 +-
 include/osmium/relations/detail/relation_meta.hpp  |   2 +-
 include/osmium/tags/filter.hpp                     |   2 +-
 include/osmium/tags/regex_filter.hpp               |   9 +-
 include/osmium/tags/taglist.hpp                    |   2 +-
 include/osmium/thread/function_wrapper.hpp         |   2 +-
 include/osmium/thread/pool.hpp                     |   2 +-
 include/osmium/thread/queue.hpp                    |   2 +-
 include/osmium/thread/util.hpp                     |   2 +-
 include/osmium/util/cast.hpp                       |   2 +-
 include/osmium/util/compatibility.hpp              |   2 +-
 include/osmium/util/config.hpp                     |   4 +-
 include/osmium/util/delta.hpp                      |   2 +-
 include/osmium/util/double.hpp                     |   6 +-
 include/osmium/util/endian.hpp                     |   6 +-
 include/osmium/util/file.hpp                       |   2 +-
 include/osmium/util/iterator.hpp                   |   2 +-
 include/osmium/util/memory.hpp                     |   9 +-
 include/osmium/util/memory_mapping.hpp             |   7 +-
 include/osmium/util/minmax.hpp                     |   6 +-
 include/osmium/util/misc.hpp                       |   4 +-
 include/osmium/util/options.hpp                    |   2 +-
 include/osmium/util/progress_bar.hpp               |   2 +-
 include/osmium/util/string.hpp                     |   2 +-
 include/osmium/util/timer.hpp                      |   2 +-
 include/osmium/util/verbose_output.hpp             |   2 +-
 include/osmium/version.hpp                         |   8 +-
 include/osmium/visitor.hpp                         | 168 +++----
 include/protozero/byteswap.hpp                     |   2 +-
 include/protozero/pbf_builder.hpp                  |   7 +-
 include/protozero/pbf_reader.hpp                   |  72 ++-
 include/protozero/pbf_writer.hpp                   |  66 ++-
 include/protozero/types.hpp                        |  15 +-
 include/protozero/varint.hpp                       |  14 +-
 include/protozero/version.hpp                      |   7 +-
 test/CMakeLists.txt                                |   3 +
 test/examples/t/amenity_list/CMakeLists.txt        |   7 +
 test/examples/t/amenity_list/node.osm              |   7 +
 test/include/catch.hpp                             |  14 +-
 test/t/area/test_area_id.cpp                       |  35 +-
 test/t/area/test_node_ref_segment.cpp              | 292 ++++++------
 test/t/builder/test_attr.cpp                       |  18 +-
 test/t/builder/test_object_builder.cpp             |  20 +-
 test/t/geom/test_coordinates.cpp                   |  71 +++
 test/t/geom/test_crs.cpp                           |   2 -
 test/t/geom/test_tile.cpp                          |  16 +
 test/t/geom/test_wkb.cpp                           |  16 +-
 test/t/index/test_id_to_location.cpp               |  20 +
 test/t/index/test_relations_map.cpp                |  50 ++
 test/t/io/test_bzip2.cpp                           |  34 +-
 test/t/io/test_file_formats.cpp                    | 528 ++++++++++-----------
 test/t/io/test_output_iterator.cpp                 |  23 +-
 test/t/io/test_reader.cpp                          | 264 +++++------
 test/t/io/test_reader_with_mock_decompression.cpp  |  18 +-
 test/t/io/test_reader_with_mock_parser.cpp         |  14 +-
 test/t/io/test_string_table.cpp                    |   2 +-
 test/t/io/test_writer.cpp                          |  24 +-
 test/t/io/test_writer_with_mock_compression.cpp    |   8 +-
 test/t/io/test_writer_with_mock_encoder.cpp        |   8 +-
 test/t/memory/test_type_is_compatible.cpp          |  72 +++
 test/t/osm/test_area.cpp                           |   6 +
 test/t/osm/test_node_ref.cpp                       | 133 +++---
 test/t/osm/test_way.cpp                            |  14 +-
 test/t/tags/test_filter.cpp                        | 163 ++++---
 test/t/util/test_memory.cpp                        |   3 +
 227 files changed, 2530 insertions(+), 1409 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 94a6b15..d12bb76 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,43 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 ### Fixed
 
 
+## [2.11.0] - 2017-01-14
+
+### Added
+
+- New index::RelationsMap(Stash|Index) classes implementing an index for
+  looking up parent relation IDs given a member relation ID.
+- Add `get_noexcept()` method to all index maps. For cases where ids are
+  often not in the index using this can speed up a program considerably.
+- New non-const WayNodeList::operator[].
+- Default constructed "invalid" Coordinates.
+- Tile constructor from web mercator coordinates and some helper
+  functions for tile arithmetic.
+- Tag matcher matching keys using a regex.
+- New `envelope()` functions on `NodeRefList`, `Way`, and `Area` returning a
+  `Box` object with the geometric envelope of the object.
+- Add `amenity_list` example.
+
+### Changed
+
+- Replaced the implementation for the web mercator projection using the usual
+  tan-formula with a polynomial approximation which is much faster and good
+  enough for OSM data which only has ~1cm resolution anyway. See
+  https://github.com/osmcode/mercator-projection for all the details and
+  benchmarks. You can disable this by defining the macro
+  `OSMIUM_USE_SLOW_MERCATOR_PROJECTION` before including any of the Osmium
+  headers.
+- Removed the outdated `Makefile`. Always use CMake directly to build.
+- Refactoring of `osmium::apply()` removing the resursive templates for faster
+  compile times and allowing rvalue handlers.
+- Lots of code and test cleanups and more documentation.
+
+### Fixed
+
+- Handle endianess on FreeBSD properly.
+- Fixed doxygen config for reproducible builds.
+
+
 ## [2.10.3] - 2016-11-20
 
 ### Changed
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3d65bea..21478ec 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 10)
-set(LIBOSMIUM_VERSION_PATCH 3)
+set(LIBOSMIUM_VERSION_MINOR 11)
+set(LIBOSMIUM_VERSION_PATCH 0)
 
 set(LIBOSMIUM_VERSION
     "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}")
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index a054f36..7e51d4c 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,5 +8,5 @@ Some rules for contributing to this project:
 
 * We'd love for you to send pull requests for fixes you have made or new features
   you have added. Please read the [notes for developers](NOTES_FOR_DEVELOPERS.md)
-  beforehand which contain some coding guidelines.
+  beforehand which contains some coding guidelines.
 
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 7b2b83d..0000000
--- a/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-
-all:
-	mkdir -p build && cd build && cmake .. && $(MAKE)
-
-doc:
-	mkdir -p build && cd build && cmake .. && $(MAKE) doc
-
-clean:
-	if test -d build; then cd build && $(MAKE) clean; fi
-
-distclean:
-	rm -fr build
-
-#deb:
-#	debuild -I -us -uc
-#
-#deb-clean:
-#	debuild clean
-
-indent:
-	astyle --style=java --indent-namespaces --indent-switches --pad-header --lineend=linux --suffix=none --recursive include/\*.hpp examples/\*.cpp test/\*.cpp
-#	astyle --style=java --indent-namespaces --indent-switches --pad-header --unpad-paren --align-pointer=type --lineend=linux --suffix=none --recursive include/\*.hpp examples/\*.cpp test/\*.cpp
-
-.PHONY: clean distclean deb deb-clean doc indent
-
diff --git a/NOTES_FOR_DEVELOPERS.md b/NOTES_FOR_DEVELOPERS.md
index 3c1f73b..5f84494 100644
--- a/NOTES_FOR_DEVELOPERS.md
+++ b/NOTES_FOR_DEVELOPERS.md
@@ -72,9 +72,7 @@ different.
 * The ellipsis in variadic template never has a space to the left of it and
   always has a space to the right: `template <typename... TArgs>` etc.
 
-Keep to the indentation and other styles used in the code. Use `make indent`
-in the toplevel directory to fix indentation and styling. It calls `astyle`
-with the right parameters. This program is in the `astyle` Debian package.
+Keep to the indentation and other styles used in the code.
 
 
 ## C++11
@@ -85,14 +83,6 @@ compilers don't support them yet. This list might change as we get more data
 about which compilers support which feature and what operating system versions
 or distributions have which versions of these compilers installed.
 
-GCC 4.6   - too old, not supported (Ubuntu 12.04 LTS)
-GCC 4.7.2 - can probably not be supported (Debian wheezy)
-GCC 4.7.3 - probably works
-GCC 4.8   - works and is supported from here on
-clang 3.0 - too old, not supported (Debian wheezy, Ubuntu 12.04 LTS)
-clang 3.2 - probably works
-clang 3.5 - works and is supported from here on
-
 Use `include/osmium/util/compatibility.hpp` if there are compatibility problems
 between compilers due to different C++11 support.
 
@@ -137,7 +127,7 @@ default cmake config. Run `ctest` to run them. Many more tests are needed.
 
 All namespaces, classes, functions, attributes, etc. should be documented.
 
-Osmium uses the Doxygen (www.doxygen.org) source code documentation system.
-If it is installed, the CMake configuration will add a new build target, so
-you can build it with `make doc`.
+Osmium uses the [Doxygen](http://www.doxygen.org) source code documentation
+system. If it is installed, the CMake configuration will add a new build
+target, so you can build it with `make doc`.
 
diff --git a/README.md b/README.md
index d526f13..ce27fea 100644
--- a/README.md
+++ b/README.md
@@ -4,15 +4,14 @@ http://osmcode.org/libosmium
 
 A fast and flexible C++ library for working with OpenStreetMap data.
 
+Libosmium works on Linux, Mac OSX and Windows.
+
 [![Build Status](https://secure.travis-ci.org/osmcode/libosmium.svg)](https://travis-ci.org/osmcode/libosmium)
 [![Build status](https://ci.appveyor.com/api/projects/status/github/osmcode/libosmium?svg=true)](https://ci.appveyor.com/project/Mapbox/libosmium)
 
-Libosmium is developed on Linux, but also works on OSX and Windows (with some
-limitations).
+Please see the [Libosmium manual](http://osmcode.org/libosmium/manual.html)
+for more details than this README can provide.
 
-There are a few applications that use the Osmium library in the examples
-directory. See the [osmium-contrib](http://github.com/osmcode/osmium-contrib)
-repository for more example code.
 
 ## Prerequisites
 
@@ -24,8 +23,9 @@ Different parts of Libosmium (and the applications built on top of it) need
 different libraries. You DO NOT NEED to install all of them, just install those
 you need for your programs.
 
-For details see the
-[list of dependencies](https://github.com/osmcode/libosmium/wiki/Libosmium-dependencies).
+For details see the [list of
+dependencies](http://osmcode.org/libosmium/manual.html#dependencies) in the
+manual.
 
 The following external (header-only) libraries are included in the libosmium
 repository:
@@ -33,10 +33,6 @@ repository:
 * [protozero](https://github.com/mapbox/protozero)
 * [utfcpp](http://utfcpp.sourceforge.net/)
 
-If you want (some of) those libraries to be installed along with libosmium
-itself when calling `make install`, you have to use the CMake options
-`INSTALL_GDALCPP`, `INSTALL_PROTOZERO`, and/or `INSTALL_UTFCPP`.
-
 
 ## Directories
 
@@ -44,7 +40,7 @@ itself when calling `make install`, you have to use the CMake options
 
 * cmake: CMake configuration scripts.
 
-* doc: Config for documentation.
+* doc: Config for API reference documentation.
 
 * examples: Osmium example applications.
 
@@ -69,37 +65,23 @@ cmake:
 
 This will build the examples and tests. Call `ctest` to run the tests.
 
-For more see the
-[Libosmium Wiki](https://github.com/osmcode/libosmium/wiki/Building-Libosmium).
+For more detals see the
+[Building Libosmium](http://osmcode.org/libosmium/manual.html#building-libosmium)
+chapter in the manual.
 
 
 ## Testing
 
 See the
-[Libosmium Wiki](https://github.com/osmcode/libosmium/wiki/Testing-Libosmium)
+[Libosmium Manual](http://osmcode.org/libosmium/manual.html#running-tests)
 for instructions.
 
 
-## Osmium on 32bit Machines
-
-Osmium works well on 64 bit machines, but on 32 bit machines there are some
-problems. Be aware that not everything will work on 32 bit architectures.
-This is mostly due to the 64 bit needed for node IDs. Also Osmium hasn't been
-tested well on 32 bit systems. Here are some issues you might run into:
-
-* Google Sparsehash does not work on 32 bit machines in our use case.
-* The `mmap` system call is called with a `size_t` argument, so it can't
-  give you more than 4GByte of memory on 32 bit systems. This might be a
-  problem.
-
-Please report any issues you have and we might be able to solve them.
-
-
 ## Switching from the old Osmium
 
 If you have been using the old version of Osmium at
-https://github.com/joto/osmium you might want to read about the
-[changes needed](https://github.com/osmcode/libosmium/wiki/Changes-from-old-versions-of-Osmium).
+https://github.com/joto/osmium you might want to read about the [changes
+needed](http://osmcode.org/libosmium/manual.html#changes-from-old-versions-of-osmium).
 
 
 ## License
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index d5ed13d..2030d1c 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -152,7 +152,7 @@ FULL_PATH_NAMES        = YES
 # will be relative from the directory where doxygen is started.
 # This tag requires that the tag FULL_PATH_NAMES is set to YES.
 
-STRIP_FROM_PATH        = @PROJECT_SOURCE_DIR@/include
+STRIP_FROM_PATH        = @PROJECT_SOURCE_DIR@
 
 # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
 # path mentioned in the documentation of a class, which tells the reader which
@@ -755,7 +755,7 @@ WARN_LOGFILE           =
 # Note: If this tag is empty the current directory is searched.
 
 INPUT                  = @PROJECT_SOURCE_DIR@/include/osmium \
-                         @PROJECT_SOURCE_DIR@/doc/doc.txt
+                         @PROJECT_SOURCE_DIR@/doc/doc.md
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -891,7 +891,7 @@ FILTER_SOURCE_PATTERNS =
 # (index.html). This can be useful if you have a project on for instance GitHub
 # and want to reuse the introduction page also for the doxygen output.
 
-USE_MDFILE_AS_MAINPAGE =
+USE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/doc/doc.md
 
 #---------------------------------------------------------------------------
 # Configuration options related to source browsing
diff --git a/doc/doc.md b/doc/doc.md
new file mode 100644
index 0000000..309f23f
--- /dev/null
+++ b/doc/doc.md
@@ -0,0 +1,21 @@
+
+Osmium is a fast and flexible C++ library for working with OpenStreetMap
+data.
+
+This is the API documentation that was automatically created from the
+source code. For more information about the Osmium Library see
+http://osmcode.org/libosmium .
+
+Osmium is free software and available under the Boost Software License.
+The source code is available at https://github.com/osmcode/libosmium .
+
+Osmium is a header-only library. You do not need to compile and link it,
+just include the headers you need.
+
+Everything in namespaces called "detail" is for internal Osmium use only,
+do not depend on it in your code. Do not include any include files in
+directories named "detail" directly. Include files in directories called
+"experimental" and everything in namespaces called "experimental" is
+unsupported and may change at any time regardless of the status of the rest
+of the library.
+
diff --git a/doc/doc.txt b/doc/doc.txt
deleted file mode 100644
index 1f06f4f..0000000
--- a/doc/doc.txt
+++ /dev/null
@@ -1,26 +0,0 @@
-
-/**
- * @mainpage
- *
- * Osmium is a fast and flexible C++ library for working with OpenStreetMap
- * data.
- *
- * This is the API documentation that was automatically created from the
- * source code. For more information about the Osmium Library see
- * http://osmcode.org/libosmium .
- *
- * Osmium is free software and available under the Boost Software License.
- * The source code is available at https://github.com/osmcode/libosmium .
- *
- * Osmium is a header-only library. You do not need to compile and link it,
- * just include the headers you need.
- *
- * Everything in namespaces called "detail" is for internal Osmium use only,
- * do not depend on it in your code. Do not include any include files in
- * directories named "detail" directly. Include files in directories called
- * "experimental" and everything in namespaces called "experimental" is
- * unsupported and may change at any time regardless of the status of the rest
- * of the library.
- *
- */
-
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index ac07896..442fda7 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -9,6 +9,7 @@
 message(STATUS "Configuring examples")
 
 set(EXAMPLES
+    amenity_list
     area_test
     change_tags
     convert
diff --git a/examples/README.md b/examples/README.md
index 55bd406..282d03d 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -3,10 +3,10 @@
 
 The programs in this directory are intended as examples for developers. They
 contain extensive comments explaining what's going on. Note that the examples
-only cover a small part of what Osmium can do, you should also read the
-documentation and API documentation.
+only cover a small part of what Osmium can do, you should also read the manuals
+and API documentation.
 
-All programs can be run without arguments and they will tell you how to run
+All programs can be run without arguments and they will tell you how to use
 them.
 
 ## Very simple examples
@@ -18,6 +18,7 @@ them.
 
 ## Still reasonably simple examples
 
+* `osmium_amenity_list`
 * `osmium_read_with_progress`
 * `osmium_filter_discussions`
 * `osmium_convert`
diff --git a/examples/osmium_amenity_list.cpp b/examples/osmium_amenity_list.cpp
new file mode 100644
index 0000000..502b44b
--- /dev/null
+++ b/examples/osmium_amenity_list.cpp
@@ -0,0 +1,167 @@
+/*
+
+  EXAMPLE osmium_amenity_list
+
+  Create a list of all amenities in the OSM input data. The type of amenity
+  (tag value) and, if available, the name is printed. For nodes, the location
+  is printed, for areas the center location.
+
+  DEMONSTRATES USE OF:
+  * file input
+  * location indexes and the NodeLocationsForWays handler
+  * the MultipolygonCollector and Assembler to assemble areas (multipolygons)
+  * your own handler that works with areas (multipolygons)
+  * accessing tags
+  * osmium::geom::Coordinates
+
+  SIMPLER EXAMPLES you might want to understand first:
+  * osmium_read
+  * osmium_count
+  * osmium_debug
+
+  LICENSE
+  The code in this example file is released into the Public Domain.
+
+*/
+
+#include <cstdio>   // for std::printf
+#include <cstdlib>  // for std::exit
+#include <iostream> // for std::cerr
+#include <string>   // for std::string
+
+// For memory based sparse index
+#include <osmium/index/map/sparse_mem_array.hpp>
+
+// For the NodeLocationForWays handler
+#include <osmium/handler/node_locations_for_ways.hpp>
+using index_type = osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
+using location_handler_type = osmium::handler::NodeLocationsForWays<index_type>;
+
+// For assembling multipolygons
+#include <osmium/area/assembler.hpp>
+#include <osmium/area/multipolygon_collector.hpp>
+
+// Allow any format of input files (XML, PBF, ...)
+#include <osmium/io/any_input.hpp>
+
+// For osmium::apply()
+#include <osmium/visitor.hpp>
+
+// For osmium::geom::Coordinates
+#include <osmium/geom/coordinates.hpp>
+
+class AmenityHandler : public osmium::handler::Handler {
+
+    // Print info about one amenity to stdout.
+    void print_amenity(const char* type, const char* name, const osmium::geom::Coordinates& c) {
+        std::printf("%8.4f,%8.4f %-15s %s\n", c.x, c.y, type, name ? name : "");
+    }
+
+    // Calculate the center point of a NodeRefList.
+    osmium::geom::Coordinates calc_center(const osmium::NodeRefList& nr_list) {
+        // Coordinates simply store an X and Y coordinate pair as doubles.
+        // (Unlike osmium::Location which stores them more efficiently as
+        // 32 bit integers.) Use Coordinates when you want to do calculations
+        // or store projected coordinates.
+        osmium::geom::Coordinates c{0.0, 0.0};
+
+        for (const auto& nr : nr_list) {
+            c.x += nr.lon();
+            c.y += nr.lat();
+        }
+
+        c.x /= nr_list.size();
+        c.y /= nr_list.size();
+
+        return c;
+    }
+
+public:
+
+    void node(const osmium::Node& node) {
+        // Getting a tag value can be expensive, because a list of tags has
+        // to be gone through and each tag has to be checked. So we store the
+        // result and reuse it.
+        const char* amenity = node.tags()["amenity"];
+        if (amenity) {
+            print_amenity(amenity, node.tags()["name"], node.location());
+        }
+    }
+
+    void area(const osmium::Area& area) {
+        const char* amenity = area.tags()["amenity"];
+        if (amenity) {
+            // Use the center of the first outer ring. Because we set
+            // create_empty_areas = false in the assembler config, we can
+            // be sure there will always be at least one outer ring.
+            const auto center = calc_center(*area.cbegin<osmium::OuterRing>());
+
+            print_amenity(amenity, area.tags()["name"], center);
+        }
+    }
+
+}; // class AmenityHandler
+
+int main(int argc, char* argv[]) {
+    if (argc != 2) {
+        std::cerr << "Usage: " << argv[0] << " OSMFILE\n";
+        std::exit(1);
+    }
+
+    // The input file name
+    const std::string input_file_name{argv[1]};
+
+    // Configuration for the multipolygon assembler. We disable the option to
+    // create empty areas when invalid multipolygons are encountered. This
+    // means areas created have a valid geometry and invalid multipolygons
+    // are simply ignored.
+    osmium::area::Assembler::config_type assembler_config;
+    assembler_config.create_empty_areas = false;
+
+    // Initialize the MultipolygonCollector. Its job is to collect all
+    // relations and member ways needed for each area. It then calls an
+    // instance of the osmium::area::Assembler class (with the given config)
+    // to actually assemble one area.
+    osmium::area::MultipolygonCollector<osmium::area::Assembler> collector{assembler_config};
+
+    // We read the input file twice. In the first pass, only relations are
+    // read and fed into the multipolygon collector. The read_meta::no option
+    // disables reading of meta data (such as version numbers, timestamps, etc.)
+    // which are not needed in this case. Disabling this can speed up your
+    // program.
+    std::cerr << "Pass 1...\n";
+    osmium::io::Reader reader1{input_file_name, osmium::osm_entity_bits::relation, osmium::io::read_meta::no};
+    collector.read_relations(reader1);
+    reader1.close();
+    std::cerr << "Pass 1 done\n";
+
+    // The index storing all node locations.
+    index_type index;
+
+    // The handler that stores all node locations in the index and adds them
+    // to the ways.
+    location_handler_type location_handler{index};
+
+    // If a location is not available in the index, we ignore it. It might
+    // not be needed (if it is not part of a multipolygon relation), so why
+    // create an error?
+    location_handler.ignore_errors();
+
+    // Create our handler.
+    AmenityHandler data_handler;
+
+    // On the second pass we read all objects and run them first through the
+    // node location handler and then the multipolygon collector. The collector
+    // will put the areas it has created into the "buffer" which are then
+    // fed through our handler.
+    std::cerr << "Pass 2...\n";
+    osmium::io::Reader reader2{input_file_name, osmium::io::read_meta::no};
+
+    osmium::apply(reader2, location_handler, data_handler, collector.handler([&data_handler](const osmium::memory::Buffer& area_buffer) {
+        osmium::apply(area_buffer, data_handler);
+    }));
+
+    reader2.close();
+    std::cerr << "Pass 2 done\n";
+}
+
diff --git a/examples/osmium_area_test.cpp b/examples/osmium_area_test.cpp
index 0c849a7..4bfff72 100644
--- a/examples/osmium_area_test.cpp
+++ b/examples/osmium_area_test.cpp
@@ -18,6 +18,7 @@
   * osmium_read
   * osmium_count
   * osmium_debug
+  * osmium_amenity_list
 
   LICENSE
   The code in this example file is released into the Public Domain.
diff --git a/include/osmium/area/assembler.hpp b/include/osmium/area/assembler.hpp
index 092f4b4..3509b57 100644
--- a/include/osmium/area/assembler.hpp
+++ b/include/osmium/area/assembler.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/area/detail/node_ref_segment.hpp b/include/osmium/area/detail/node_ref_segment.hpp
index 1c03d3e..585ac95 100644
--- a/include/osmium/area/detail/node_ref_segment.hpp
+++ b/include/osmium/area/detail/node_ref_segment.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/area/detail/proto_ring.hpp b/include/osmium/area/detail/proto_ring.hpp
index bce6817..e1ad796 100644
--- a/include/osmium/area/detail/proto_ring.hpp
+++ b/include/osmium/area/detail/proto_ring.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/area/detail/segment_list.hpp b/include/osmium/area/detail/segment_list.hpp
index 97d512a..64b2b4e 100644
--- a/include/osmium/area/detail/segment_list.hpp
+++ b/include/osmium/area/detail/segment_list.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/area/detail/vector.hpp b/include/osmium/area/detail/vector.hpp
index fae1280..14bf0cb 100644
--- a/include/osmium/area/detail/vector.hpp
+++ b/include/osmium/area/detail/vector.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/area/multipolygon_collector.hpp b/include/osmium/area/multipolygon_collector.hpp
index 8b37052..cc790e9 100644
--- a/include/osmium/area/multipolygon_collector.hpp
+++ b/include/osmium/area/multipolygon_collector.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -69,6 +69,7 @@ namespace osmium {
          * class given as template argument.
          *
          * @tparam TAssembler Multipolygon Assembler class.
+         * @pre The Ids of all objects must be unique in the input data.
          */
         template <typename TAssembler>
         class MultipolygonCollector : public osmium::relations::Collector<MultipolygonCollector<TAssembler>, false, true, false> {
@@ -87,7 +88,7 @@ namespace osmium {
 
             void flush_output_buffer() {
                 if (this->callback()) {
-                    osmium::memory::Buffer buffer(initial_output_buffer_size);
+                    osmium::memory::Buffer buffer{initial_output_buffer_size};
                     using std::swap;
                     swap(buffer, m_output_buffer);
                     this->callback()(std::move(buffer));
@@ -195,7 +196,7 @@ namespace osmium {
             }
 
             osmium::memory::Buffer read() {
-                osmium::memory::Buffer buffer(initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes);
+                osmium::memory::Buffer buffer{initial_output_buffer_size, osmium::memory::Buffer::auto_grow::yes};
 
                 using std::swap;
                 swap(buffer, m_output_buffer);
diff --git a/include/osmium/area/problem_reporter.hpp b/include/osmium/area/problem_reporter.hpp
index 6c56231..8a72c1a 100644
--- a/include/osmium/area/problem_reporter.hpp
+++ b/include/osmium/area/problem_reporter.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/area/problem_reporter_exception.hpp b/include/osmium/area/problem_reporter_exception.hpp
index 009a5f4..152be40 100644
--- a/include/osmium/area/problem_reporter_exception.hpp
+++ b/include/osmium/area/problem_reporter_exception.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/area/problem_reporter_ogr.hpp b/include/osmium/area/problem_reporter_ogr.hpp
index 0889a58..914c8ba 100644
--- a/include/osmium/area/problem_reporter_ogr.hpp
+++ b/include/osmium/area/problem_reporter_ogr.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/area/problem_reporter_stream.hpp b/include/osmium/area/problem_reporter_stream.hpp
index c601867..0c008f1 100644
--- a/include/osmium/area/problem_reporter_stream.hpp
+++ b/include/osmium/area/problem_reporter_stream.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/area/stats.hpp b/include/osmium/area/stats.hpp
index 79cc002..07be783 100644
--- a/include/osmium/area/stats.hpp
+++ b/include/osmium/area/stats.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/builder/attr.hpp b/include/osmium/builder/attr.hpp
index 8e0b4a3..bcd4861 100644
--- a/include/osmium/builder/attr.hpp
+++ b/include/osmium/builder/attr.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/builder/builder.hpp b/include/osmium/builder/builder.hpp
index 044da1e..107f4a9 100644
--- a/include/osmium/builder/builder.hpp
+++ b/include/osmium/builder/builder.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/builder/builder_helper.hpp b/include/osmium/builder/builder_helper.hpp
index 5e0f218..8bfa21e 100644
--- a/include/osmium/builder/builder_helper.hpp
+++ b/include/osmium/builder/builder_helper.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/builder/osm_object_builder.hpp b/include/osmium/builder/osm_object_builder.hpp
index 0a8fa6f..1dd5f97 100644
--- a/include/osmium/builder/osm_object_builder.hpp
+++ b/include/osmium/builder/osm_object_builder.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/diff_handler.hpp b/include/osmium/diff_handler.hpp
index a5fc34f..19c75b8 100644
--- a/include/osmium/diff_handler.hpp
+++ b/include/osmium/diff_handler.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/diff_iterator.hpp b/include/osmium/diff_iterator.hpp
index fc92b36..9a70399 100644
--- a/include/osmium/diff_iterator.hpp
+++ b/include/osmium/diff_iterator.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/diff_visitor.hpp b/include/osmium/diff_visitor.hpp
index 95660de..f796ac6 100644
--- a/include/osmium/diff_visitor.hpp
+++ b/include/osmium/diff_visitor.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -84,8 +84,8 @@ namespace osmium {
 
     template <typename TSource, typename... THandlers>
     inline void apply_diff(TSource& source, THandlers&... handlers) {
-        apply_diff(osmium::io::InputIterator<TSource, osmium::OSMObject> {source},
-                   osmium::io::InputIterator<TSource, osmium::OSMObject> {},
+        apply_diff(osmium::io::InputIterator<TSource, osmium::OSMObject>{source},
+                   osmium::io::InputIterator<TSource, osmium::OSMObject>{},
                    handlers...);
     }
 
diff --git a/include/osmium/dynamic_handler.hpp b/include/osmium/dynamic_handler.hpp
index 01d1554..9219ade 100644
--- a/include/osmium/dynamic_handler.hpp
+++ b/include/osmium/dynamic_handler.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -104,7 +104,8 @@ auto _name_##_dispatch(THandler& handler, const osmium::_type_& object, long) ->
             }
 
             template <typename THandler>
-            void flush_dispatch(THandler&, long) {}
+            void flush_dispatch(THandler&, long) {
+            }
 
             template <typename THandler>
             class HandlerWrapper : public HandlerWrapperBase {
diff --git a/include/osmium/experimental/flex_reader.hpp b/include/osmium/experimental/flex_reader.hpp
index 0a2a668..08f426c 100644
--- a/include/osmium/experimental/flex_reader.hpp
+++ b/include/osmium/experimental/flex_reader.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/fwd.hpp b/include/osmium/fwd.hpp
index 84def8e..df2bdd9 100644
--- a/include/osmium/fwd.hpp
+++ b/include/osmium/fwd.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/geom/coordinates.hpp b/include/osmium/geom/coordinates.hpp
index 0f13876..1b244ef 100644
--- a/include/osmium/geom/coordinates.hpp
+++ b/include/osmium/geom/coordinates.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -33,7 +33,9 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
+#include <cmath>
 #include <iosfwd>
+#include <limits>
 #include <string>
 
 #include <osmium/osm/location.hpp>
@@ -48,18 +50,71 @@ namespace osmium {
             double x;
             double y;
 
-            explicit Coordinates(double cx, double cy) noexcept : x(cx), y(cy) {
+            /**
+             * Default constructor creates invalid coordinates.
+             */
+            Coordinates() noexcept :
+                x(std::numeric_limits<double>::quiet_NaN()),
+                y(std::numeric_limits<double>::quiet_NaN()) {
             }
 
-            Coordinates(const osmium::Location& location) : x(location.lon()), y(location.lat()) {
+            /**
+             * Create Coordinates from doubles. If any of them is NaN, the
+             * coordinates are invalid.
+             */
+            explicit Coordinates(double cx, double cy) noexcept :
+                x(cx),
+                y(cy) {
             }
 
+            /**
+             * Create Coordinates from a Location. Will throw
+             * osmium::invalid_location if the location is not valid.
+             */
+            Coordinates(const osmium::Location& location) :
+                x(location.lon()),
+                y(location.lat()) {
+            }
+
+            /**
+             * Coordinates are invalid if they have been default constructed.
+             */
+            bool valid() const noexcept {
+                return !std::isnan(x) && !std::isnan(y);
+            }
+
+            /**
+             * Convert coordinates to text and append to given string. If the
+             * coordinate is invalid, the fixed string "invalid" will be
+             * added to the string.
+             *
+             * @param s String to append the coordinates to.
+             * @param infix Character to print between the two coordinates.
+             * @param precision Number of digits after the decimal point the
+             *        coordinate will be rounded to.
+             */
             void append_to_string(std::string& s, const char infix, int precision) const {
-                osmium::util::double2string(s, x, precision);
-                s += infix;
-                osmium::util::double2string(s, y, precision);
+                if (valid()) {
+                    osmium::util::double2string(s, x, precision);
+                    s += infix;
+                    osmium::util::double2string(s, y, precision);
+                } else {
+                    s.append("invalid");
+                }
             }
 
+            /**
+             * Convert coordinates to text and append to given string. If the
+             * coordinate is invalid, the fixed string "invalid" will be
+             * added to the string between the prefix and suffix characters.
+             *
+             * @param s String to append the coordinates to.
+             * @param prefix Character to print before the first coordinate.
+             * @param infix Character to print between the two coordinates.
+             * @param suffix Character to print after the second coordinate.
+             * @param precision Number of digits after the decimal point the
+             *        coordinate will be rounded to.
+             */
             void append_to_string(std::string& s, const char prefix, const char infix, const char suffix, int precision) const {
                 s += prefix;
                 append_to_string(s, infix, precision);
@@ -69,11 +124,18 @@ namespace osmium {
         }; // struct coordinates
 
         /**
-         * Compare whether two Coordinates are identical. Might not give the
-         * right result if the coordinates have been the result of some
+         * Check whether two Coordinates are equal. Invalid coordinates are
+         * equal to other invalid coordinates but not equal to any valid
+         * coordinates.
+         *
+         * Because this is comparing floating point values, it might not give
+         * the right result if the coordinates have been the result of some
          * calculation that introduced rounding errors.
          */
         inline bool operator==(const Coordinates& lhs, const Coordinates& rhs) noexcept {
+            if (!lhs.valid() && !rhs.valid()) {
+                return true;
+            }
 #pragma GCC diagnostic push
 #pragma GCC diagnostic ignored "-Wfloat-equal"
             return lhs.x == rhs.x && lhs.y == rhs.y;
diff --git a/include/osmium/geom/factory.hpp b/include/osmium/geom/factory.hpp
index f2db402..6f8d954 100644
--- a/include/osmium/geom/factory.hpp
+++ b/include/osmium/geom/factory.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/geom/geojson.hpp b/include/osmium/geom/geojson.hpp
index e9f722f..e05deba 100644
--- a/include/osmium/geom/geojson.hpp
+++ b/include/osmium/geom/geojson.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/geom/geos.hpp b/include/osmium/geom/geos.hpp
index d59e7ce..b472a38 100644
--- a/include/osmium/geom/geos.hpp
+++ b/include/osmium/geom/geos.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/geom/haversine.hpp b/include/osmium/geom/haversine.hpp
index ef3f1ff..6be72d2 100644
--- a/include/osmium/geom/haversine.hpp
+++ b/include/osmium/geom/haversine.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -60,6 +60,8 @@ namespace osmium {
 
             /**
              * Calculate distance in meters between two sets of coordinates.
+             *
+             * @pre @code c1.valid() && c2.valid() @endcode
              */
             inline double distance(const osmium::geom::Coordinates& c1, const osmium::geom::Coordinates& c2) {
                 double lonh = sin(deg_to_rad(c1.x - c2.x) * 0.5);
diff --git a/include/osmium/geom/mercator_projection.hpp b/include/osmium/geom/mercator_projection.hpp
index a0c3808..814bce6 100644
--- a/include/osmium/geom/mercator_projection.hpp
+++ b/include/osmium/geom/mercator_projection.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -53,10 +53,49 @@ namespace osmium {
                 return earth_radius_for_epsg3857 * deg_to_rad(lon);
             }
 
-            inline double lat_to_y(double lat) { // not constexpr because math functions aren't
+            inline double lat_to_y_with_tan(double lat) { // not constexpr because math functions aren't
                 return earth_radius_for_epsg3857 * std::log(std::tan(osmium::geom::PI/4 + deg_to_rad(lat)/2));
             }
 
+#ifdef OSMIUM_USE_SLOW_MERCATOR_PROJECTION
+            inline double lat_to_y(double lat) {
+                return lat_to_y_with_tan(lat);
+            }
+#else
+
+            // This is a much faster implementation than the canonical
+            // implementation using the tan() function. For details
+            // see https://github.com/osmcode/mercator-projection .
+            inline double lat_to_y(double lat) { // not constexpr because math functions aren't
+                if (lat < -78.0 || lat > 78.0) {
+                    return lat_to_y_with_tan(lat);
+                }
+
+                return earth_radius_for_epsg3857 *
+                    ((((((((((-3.1112583378460085319e-23  * lat +
+                               2.0465852743943268009e-19) * lat +
+                               6.4905282018672673884e-18) * lat +
+                              -1.9685447939983315591e-14) * lat +
+                              -2.2022588158115104182e-13) * lat +
+                               5.1617537365509453239e-10) * lat +
+                               2.5380136069803016519e-9)  * lat +
+                              -5.1448323697228488745e-6)  * lat +
+                              -9.4888671473357768301e-6)  * lat +
+                               1.7453292518154191887e-2)  * lat)
+                    /
+                    ((((((((((-1.9741136066814230637e-22  * lat +
+                              -1.258514031244679556e-20)  * lat +
+                               4.8141483273572351796e-17) * lat +
+                               8.6876090870176172185e-16) * lat +
+                              -2.3298743439377541768e-12) * lat +
+                              -1.9300094785736130185e-11) * lat +
+                               4.3251609106864178231e-8)  * lat +
+                               1.7301944508516974048e-7)  * lat +
+                              -3.4554675198786337842e-4)  * lat +
+                              -5.4367203601085991108e-4)  * lat + 1.0);
+            }
+#endif
+
             constexpr inline double x_to_lon(double x) {
                 return rad_to_deg(x) / earth_radius_for_epsg3857;
             }
@@ -73,10 +112,20 @@ namespace osmium {
          */
         constexpr double MERCATOR_MAX_LAT = 85.0511288;
 
+        /**
+         * Convert the coordinates from WGS84 lon/lat to web mercator.
+         *
+         * @pre @code c.valid() @endcode
+         */
         inline Coordinates lonlat_to_mercator(const Coordinates& c) {
             return Coordinates(detail::lon_to_x(c.x), detail::lat_to_y(c.y));
         }
 
+        /**
+         * Convert the coordinates from web mercator to WGS84 lon/lat.
+         *
+         * @pre @code c.valid() @endcode
+         */
         inline Coordinates mercator_to_lonlat(const Coordinates& c) {
             return Coordinates(detail::x_to_lon(c.x), detail::y_to_lat(c.y));
         }
diff --git a/include/osmium/geom/ogr.hpp b/include/osmium/geom/ogr.hpp
index a91fbe5..3574cdc 100644
--- a/include/osmium/geom/ogr.hpp
+++ b/include/osmium/geom/ogr.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/geom/projection.hpp b/include/osmium/geom/projection.hpp
index eaa9b53..60e0ecf 100644
--- a/include/osmium/geom/projection.hpp
+++ b/include/osmium/geom/projection.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/geom/rapid_geojson.hpp b/include/osmium/geom/rapid_geojson.hpp
index 7817389..897d6c3 100644
--- a/include/osmium/geom/rapid_geojson.hpp
+++ b/include/osmium/geom/rapid_geojson.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/geom/relations.hpp b/include/osmium/geom/relations.hpp
index 5e6e773..f11ba51 100644
--- a/include/osmium/geom/relations.hpp
+++ b/include/osmium/geom/relations.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/geom/tile.hpp b/include/osmium/geom/tile.hpp
index 2c33017..55ea87b 100644
--- a/include/osmium/geom/tile.hpp
+++ b/include/osmium/geom/tile.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -47,15 +47,53 @@ namespace osmium {
         namespace detail {
 
             template <typename T>
-            inline T restrict_to_range(T value, T min, T max) {
-                if (value < min) return min;
-                if (value > max) return max;
-                return value;
+            inline constexpr const T& clamp(const T& value, const T& min, const T& max) {
+                return value < min ? min : (max < value ? max : value);
             }
 
         } // namespace detail
 
         /**
+         * Returns the number of tiles (in each direction) for the given zoom
+         * level.
+         */
+        inline constexpr uint32_t num_tiles_in_zoom(uint32_t zoom) noexcept {
+            return 1u << zoom;
+        }
+
+        /**
+         * Returns the width or hight of a tile in web mercator coordinates for
+         * the given zoom level.
+         */
+        inline constexpr double tile_extent_in_zoom(uint32_t zoom) noexcept {
+            return detail::max_coordinate_epsg3857 * 2 / num_tiles_in_zoom(zoom);
+        }
+
+        /**
+         * Get the tile x number from an x coordinate in web mercator
+         * projection in the given zoom level. Tiles are numbered from left
+         * to right.
+         */
+        inline constexpr uint32_t mercx_to_tilex(uint32_t zoom, double x) noexcept {
+            return static_cast<uint32_t>(detail::clamp<int32_t>(
+                static_cast<int32_t>((x + detail::max_coordinate_epsg3857) / tile_extent_in_zoom(zoom)),
+                0, num_tiles_in_zoom(zoom) -1
+            ));
+        }
+
+        /**
+         * Get the tile y number from an y coordinate in web mercator
+         * projection in the given zoom level. Tiles are numbered from top
+         * to bottom.
+         */
+        inline constexpr uint32_t mercy_to_tiley(uint32_t zoom, double y) noexcept {
+            return static_cast<uint32_t>(detail::clamp<int32_t>(
+                static_cast<int32_t>((detail::max_coordinate_epsg3857 - y) / tile_extent_in_zoom(zoom)),
+                0, num_tiles_in_zoom(zoom) -1
+            ));
+        }
+
+        /**
          * A tile in the usual Mercator projection.
          */
         struct Tile {
@@ -77,10 +115,13 @@ namespace osmium {
              *
              * @pre @code zoom <= 30 && x < 2^zoom && y < 2^zoom @endcode
              */
-            explicit Tile(uint32_t zoom, uint32_t tx, uint32_t ty) noexcept : x(tx), y(ty), z(zoom) {
+            explicit Tile(uint32_t zoom, uint32_t tx, uint32_t ty) noexcept :
+                x(tx),
+                y(ty),
+                z(zoom) {
                 assert(zoom <= 30u);
-                assert(x < (1u << zoom));
-                assert(y < (1u << zoom));
+                assert(x < num_tiles_in_zoom(zoom));
+                assert(y < num_tiles_in_zoom(zoom));
             }
 
             /**
@@ -95,11 +136,24 @@ namespace osmium {
                 z(zoom) {
                 assert(zoom <= 30u);
                 assert(location.valid());
-                const osmium::geom::Coordinates c = lonlat_to_mercator(location);
-                const int32_t n = 1 << zoom;
-                const double scale = detail::max_coordinate_epsg3857 * 2 / n;
-                x = uint32_t(detail::restrict_to_range<int32_t>(int32_t((c.x + detail::max_coordinate_epsg3857) / scale), 0, n-1));
-                y = uint32_t(detail::restrict_to_range<int32_t>(int32_t((detail::max_coordinate_epsg3857 - c.y) / scale), 0, n-1));
+                const auto coordinates = lonlat_to_mercator(location);
+                x = mercx_to_tilex(zoom, coordinates.x);
+                y = mercy_to_tiley(zoom, coordinates.y);
+            }
+
+            /**
+             * Create a tile with the given zoom level that contains the given
+             * coordinates in Mercator projection.
+             *
+             * The values are not checked for validity.
+             *
+             * @pre @code coordinates.valid() && zoom <= 30 @endcode
+             */
+            explicit Tile(uint32_t zoom, const osmium::geom::Coordinates& coordinates) :
+                z(zoom) {
+                assert(zoom <= 30u);
+                x = mercx_to_tilex(zoom, coordinates.x);
+                y = mercy_to_tiley(zoom, coordinates.y);
             }
 
             /**
@@ -111,7 +165,7 @@ namespace osmium {
                 if (z > 30) {
                     return false;
                 }
-                const uint32_t max = 1 << z;
+                const auto max = num_tiles_in_zoom(z);
                 return x < max && y < max;
             }
 
diff --git a/include/osmium/geom/util.hpp b/include/osmium/geom/util.hpp
index b5682f9..90a705a 100644
--- a/include/osmium/geom/util.hpp
+++ b/include/osmium/geom/util.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/geom/wkb.hpp b/include/osmium/geom/wkb.hpp
index d6b7798..39105fc 100644
--- a/include/osmium/geom/wkb.hpp
+++ b/include/osmium/geom/wkb.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/geom/wkt.hpp b/include/osmium/geom/wkt.hpp
index e360f71..96a5168 100644
--- a/include/osmium/geom/wkt.hpp
+++ b/include/osmium/geom/wkt.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/handler.hpp b/include/osmium/handler.hpp
index 1263910..a164391 100644
--- a/include/osmium/handler.hpp
+++ b/include/osmium/handler.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/handler/chain.hpp b/include/osmium/handler/chain.hpp
index 109e353..2ab9d29 100644
--- a/include/osmium/handler/chain.hpp
+++ b/include/osmium/handler/chain.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -74,7 +74,7 @@ namespace osmium {
             struct call_flush {
                 void operator()(THandlers& handlers) {
                     std::get<N>(handlers).flush();
-                    call_flush<N+1, SIZE, THandlers>()(handlers);
+                    call_flush<N + 1, SIZE, THandlers>()(handlers);
                 }
             }; // struct call_flush
 
@@ -107,7 +107,7 @@ namespace osmium {
                 call_relation<0, sizeof...(THandler), handlers_type>()(m_handlers, relation);
             }
 
-            void changeset( osmium::Changeset& changeset) {
+            void changeset(osmium::Changeset& changeset) {
                 call_changeset<0, sizeof...(THandler), handlers_type>()(m_handlers, changeset);
             }
 
diff --git a/include/osmium/handler/check_order.hpp b/include/osmium/handler/check_order.hpp
index a9b6834..11995d7 100644
--- a/include/osmium/handler/check_order.hpp
+++ b/include/osmium/handler/check_order.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -67,8 +67,10 @@ namespace osmium {
          * Handler that can be used to check that an OSM file is ordered
          * correctly. Ordered in this case refers to the usual order in OSM
          * files: First nodes in the order of their IDs, then ways in the order
-         * of their IDs, then relations in the order or their IDs. IDs have to
-         * be unique for each type.
+         * of their IDs, then relations in the order or their IDs.
+         *
+         * IDs have to be unique for each type. This check will fail for
+         * history files.
          *
          * To use this add a CheckOrder member variable to your handler and
          * call the node(), way(), and relation() methods from your node(),
diff --git a/include/osmium/handler/disk_store.hpp b/include/osmium/handler/disk_store.hpp
index d112ac0..882d7a1 100644
--- a/include/osmium/handler/disk_store.hpp
+++ b/include/osmium/handler/disk_store.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/handler/dump.hpp b/include/osmium/handler/dump.hpp
index 3863b77..b3f2ed7 100644
--- a/include/osmium/handler/dump.hpp
+++ b/include/osmium/handler/dump.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/handler/node_locations_for_ways.hpp b/include/osmium/handler/node_locations_for_ways.hpp
index 61c6de2..12c2b5f 100644
--- a/include/osmium/handler/node_locations_for_ways.hpp
+++ b/include/osmium/handler/node_locations_for_ways.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -83,9 +83,9 @@ namespace osmium {
 
             osmium::unsigned_object_id_type m_last_id{0};
 
-            bool m_ignore_errors {false};
+            bool m_ignore_errors{false};
 
-            bool m_must_sort {false};
+            bool m_must_sort{false};
 
             // It is okay to have this static dummy instance, even when using several threads,
             // because it is read-only.
diff --git a/include/osmium/handler/object_relations.hpp b/include/osmium/handler/object_relations.hpp
index 5f1956d..c99e1bc 100644
--- a/include/osmium/handler/object_relations.hpp
+++ b/include/osmium/handler/object_relations.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/bool_vector.hpp b/include/osmium/index/bool_vector.hpp
index 2ef80f9..9c99ade 100644
--- a/include/osmium/index/bool_vector.hpp
+++ b/include/osmium/index/bool_vector.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/detail/create_map_with_fd.hpp b/include/osmium/index/detail/create_map_with_fd.hpp
index 72b326a..e724f18 100644
--- a/include/osmium/index/detail/create_map_with_fd.hpp
+++ b/include/osmium/index/detail/create_map_with_fd.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/detail/mmap_vector_anon.hpp b/include/osmium/index/detail/mmap_vector_anon.hpp
index cd6942f..1192d8f 100644
--- a/include/osmium/index/detail/mmap_vector_anon.hpp
+++ b/include/osmium/index/detail/mmap_vector_anon.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/detail/mmap_vector_base.hpp b/include/osmium/index/detail/mmap_vector_base.hpp
index 8f52e98..6cf6406 100644
--- a/include/osmium/index/detail/mmap_vector_base.hpp
+++ b/include/osmium/index/detail/mmap_vector_base.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -103,22 +103,27 @@ namespace osmium {
                 return m_size == 0;
             }
 
-            const T* data() const {
+            const_pointer data() const {
                 return m_mapping.begin();
             }
 
-            T* data() {
+            pointer data() {
                 return m_mapping.begin();
             }
 
-            T& operator[](size_t n) {
+            const_reference operator[](size_t n) const {
                 assert(n < m_size);
                 return data()[n];
             }
 
-            T at(size_t n) const {
+            reference operator[](size_t n) {
+                assert(n < m_size);
+                return data()[n];
+            }
+
+            value_type at(size_t n) const {
                 if (n >= m_size) {
-                    throw std::out_of_range("out of range");
+                    throw std::out_of_range{"out of range"};
                 }
                 return data()[n];
             }
@@ -128,21 +133,21 @@ namespace osmium {
             }
 
             void shrink_to_fit() {
-                while (m_size > 0 && data()[m_size - 1] == osmium::index::empty_value<T>()) {
+                while (m_size > 0 && data()[m_size - 1] == osmium::index::empty_value<value_type>()) {
                     --m_size;
                 }
             }
 
-            void push_back(const T& value) {
-                resize(m_size+1);
-                data()[m_size-1] = value;
+            void push_back(const_reference value) {
+                resize(m_size + 1);
+                data()[m_size - 1] = value;
             }
 
             void reserve(size_t new_capacity) {
                 if (new_capacity > capacity()) {
                     const size_t old_capacity = capacity();
                     m_mapping.resize(new_capacity);
-                    std::fill(data() + old_capacity, data() + new_capacity, osmium::index::empty_value<T>());
+                    std::fill(data() + old_capacity, data() + new_capacity, osmium::index::empty_value<value_type>());
                 }
             }
 
diff --git a/include/osmium/index/detail/mmap_vector_file.hpp b/include/osmium/index/detail/mmap_vector_file.hpp
index 9e72f5a..6ba7e2e 100644
--- a/include/osmium/index/detail/mmap_vector_file.hpp
+++ b/include/osmium/index/detail/mmap_vector_file.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/detail/tmpfile.hpp b/include/osmium/index/detail/tmpfile.hpp
index 0f3d120..9da7b70 100644
--- a/include/osmium/index/detail/tmpfile.hpp
+++ b/include/osmium/index/detail/tmpfile.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/detail/vector_map.hpp b/include/osmium/index/detail/vector_map.hpp
index 9f4af1f..ed0f760 100644
--- a/include/osmium/index/detail/vector_map.hpp
+++ b/include/osmium/index/detail/vector_map.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -81,16 +81,22 @@ namespace osmium {
                     m_vector[id] = value;
                 }
 
-                const TValue get(const TId id) const final {
-                    try {
-                        const TValue& value = m_vector.at(id);
-                        if (value == osmium::index::empty_value<TValue>()) {
-                            throw osmium::not_found{id};
-                        }
-                        return value;
-                    } catch (const std::out_of_range&) {
+                TValue get(const TId id) const final {
+                    if (id >= m_vector.size()) {
                         throw osmium::not_found{id};
                     }
+                    const TValue value = m_vector[id];
+                    if (value == osmium::index::empty_value<TValue>()) {
+                        throw osmium::not_found{id};
+                    }
+                    return value;
+                }
+
+                TValue get_noexcept(const TId id) const noexcept final {
+                    if (id >= m_vector.size()) {
+                        return osmium::index::empty_value<TValue>();
+                    }
+                    return m_vector[id];
                 }
 
                 size_t size() const final {
@@ -155,6 +161,16 @@ namespace osmium {
 
                 vector_type m_vector;
 
+                typename vector_type::const_iterator find_id(const TId id) const noexcept {
+                    const element_type element {
+                        id,
+                        osmium::index::empty_value<TValue>()
+                    };
+                    return std::lower_bound(m_vector.begin(), m_vector.end(), element, [](const element_type& a, const element_type& b) {
+                        return a.first < b.first;
+                    });
+                }
+
             public:
 
                 VectorBasedSparseMap() :
@@ -171,19 +187,22 @@ namespace osmium {
                     m_vector.push_back(element_type(id, value));
                 }
 
-                const TValue get(const TId id) const final {
-                    const element_type element {
-                        id,
-                        osmium::index::empty_value<TValue>()
-                    };
-                    const auto result = std::lower_bound(m_vector.begin(), m_vector.end(), element, [](const element_type& a, const element_type& b) {
-                        return a.first < b.first;
-                    });
+                TValue get(const TId id) const final {
+                    const auto result = find_id(id);
                     if (result == m_vector.end() || result->first != id) {
                         throw osmium::not_found{id};
-                    } else {
-                        return result->second;
                     }
+
+                    return result->second;
+                }
+
+                TValue get_noexcept(const TId id) const noexcept final {
+                    const auto result = find_id(id);
+                    if (result == m_vector.end() || result->first != id) {
+                        return osmium::index::empty_value<TValue>();
+                    }
+
+                    return result->second;
                 }
 
                 size_t size() const final {
diff --git a/include/osmium/index/detail/vector_multimap.hpp b/include/osmium/index/detail/vector_multimap.hpp
index f859f5b..b015123 100644
--- a/include/osmium/index/detail/vector_multimap.hpp
+++ b/include/osmium/index/detail/vector_multimap.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/id_set.hpp b/include/osmium/index/id_set.hpp
index b9a9f9d..50426b0 100644
--- a/include/osmium/index/id_set.hpp
+++ b/include/osmium/index/id_set.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/index.hpp b/include/osmium/index/index.hpp
index 65871f2..565009c 100644
--- a/include/osmium/index/index.hpp
+++ b/include/osmium/index/index.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/map.hpp b/include/osmium/index/map.hpp
index d26a4aa..bcda589 100644
--- a/include/osmium/index/map.hpp
+++ b/include/osmium/index/map.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -126,8 +126,25 @@ namespace osmium {
                 /// Set the field with id to value.
                 virtual void set(const TId id, const TValue value) = 0;
 
-                /// Retrieve value by id. Does not check for overflow or empty fields.
-                virtual const TValue get(const TId id) const = 0;
+                /**
+                 * Retrieve value by id.
+                 *
+                 * @param id The id to look for.
+                 * @returns Value.
+                 * @throws osmium::not_found if the id could not be found.
+                 */
+                virtual TValue get(const TId id) const = 0;
+
+                /**
+                 * Retrieve value by id.
+                 *
+                 * @param id The id to look for.
+                 * @returns Value or, if not found, the empty value as defined
+                 *          by osmium::index::empty_value<TValue>() which is
+                 *          usually the default constructed value of type
+                 *          TValue.
+                 */
+                virtual TValue get_noexcept(const TId id) const noexcept = 0;
 
                 /**
                  * Get the approximate number of items in the storage. The storage
diff --git a/include/osmium/index/map/all.hpp b/include/osmium/index/map/all.hpp
index 348d771..1e4827f 100644
--- a/include/osmium/index/map/all.hpp
+++ b/include/osmium/index/map/all.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/map/dense_file_array.hpp b/include/osmium/index/map/dense_file_array.hpp
index 6694d11..cdde373 100644
--- a/include/osmium/index/map/dense_file_array.hpp
+++ b/include/osmium/index/map/dense_file_array.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/map/dense_mem_array.hpp b/include/osmium/index/map/dense_mem_array.hpp
index 6c33f7d..af610a8 100644
--- a/include/osmium/index/map/dense_mem_array.hpp
+++ b/include/osmium/index/map/dense_mem_array.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/map/dense_mmap_array.hpp b/include/osmium/index/map/dense_mmap_array.hpp
index e63170a..85057dc 100644
--- a/include/osmium/index/map/dense_mmap_array.hpp
+++ b/include/osmium/index/map/dense_mmap_array.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/map/dummy.hpp b/include/osmium/index/map/dummy.hpp
index 529ad25..de9a184 100644
--- a/include/osmium/index/map/dummy.hpp
+++ b/include/osmium/index/map/dummy.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -62,10 +62,14 @@ namespace osmium {
                     // intentionally left blank
                 }
 
-                const TValue get(const TId id) const final {
+                TValue get(const TId id) const final {
                     throw osmium::not_found{id};
                 }
 
+                TValue get_noexcept(const TId /*id*/) const noexcept final {
+                    return osmium::index::empty_value<TValue>();
+                }
+
                 size_t size() const final {
                     return 0;
                 }
diff --git a/include/osmium/index/map/sparse_file_array.hpp b/include/osmium/index/map/sparse_file_array.hpp
index 495833c..778b010 100644
--- a/include/osmium/index/map/sparse_file_array.hpp
+++ b/include/osmium/index/map/sparse_file_array.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/map/sparse_mem_array.hpp b/include/osmium/index/map/sparse_mem_array.hpp
index 29fadf6..752e3b0 100644
--- a/include/osmium/index/map/sparse_mem_array.hpp
+++ b/include/osmium/index/map/sparse_mem_array.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/map/sparse_mem_map.hpp b/include/osmium/index/map/sparse_mem_map.hpp
index 43fea36..f57df09 100644
--- a/include/osmium/index/map/sparse_mem_map.hpp
+++ b/include/osmium/index/map/sparse_mem_map.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -76,14 +76,22 @@ namespace osmium {
                     m_elements[id] = value;
                 }
 
-                const TValue get(const TId id) const final {
-                    auto it = m_elements.find(id);
+                TValue get(const TId id) const final {
+                    const auto it = m_elements.find(id);
                     if (it == m_elements.end()) {
                         throw osmium::not_found{id};
                     }
                     return it->second;
                 }
 
+                TValue get_noexcept(const TId id) const noexcept final {
+                    const auto it = m_elements.find(id);
+                    if (it == m_elements.end()) {
+                        return osmium::index::empty_value<TValue>();
+                    }
+                    return it->second;
+                }
+
                 size_t size() const noexcept final {
                     return m_elements.size();
                 }
diff --git a/include/osmium/index/map/sparse_mem_table.hpp b/include/osmium/index/map/sparse_mem_table.hpp
index 68f5797..f6a5fa3 100644
--- a/include/osmium/index/map/sparse_mem_table.hpp
+++ b/include/osmium/index/map/sparse_mem_table.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -97,13 +97,21 @@ namespace osmium {
                     m_elements[id] = value;
                 }
 
-                const TValue get(const TId id) const final {
+                TValue get(const TId id) const final {
                     if (id >= m_elements.size()) {
                         throw osmium::not_found{id};
                     }
-                    if (m_elements[id] == osmium::index::empty_value<TValue>()) {
+                    const TValue value = m_elements[id];
+                    if (value == osmium::index::empty_value<TValue>()) {
                         throw osmium::not_found{id};
                     }
+                    return value;
+                }
+
+                TValue get_noexcept(const TId id) const noexcept final {
+                    if (id >= m_elements.size()) {
+                        return osmium::index::empty_value<TValue>();
+                    }
                     return m_elements[id];
                 }
 
diff --git a/include/osmium/index/map/sparse_mmap_array.hpp b/include/osmium/index/map/sparse_mmap_array.hpp
index 939c389..1c15abc 100644
--- a/include/osmium/index/map/sparse_mmap_array.hpp
+++ b/include/osmium/index/map/sparse_mmap_array.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/multimap.hpp b/include/osmium/index/multimap.hpp
index 9361d6a..1b0964e 100644
--- a/include/osmium/index/multimap.hpp
+++ b/include/osmium/index/multimap.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/multimap/all.hpp b/include/osmium/index/multimap/all.hpp
index 3fc99d4..1420b30 100644
--- a/include/osmium/index/multimap/all.hpp
+++ b/include/osmium/index/multimap/all.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/multimap/hybrid.hpp b/include/osmium/index/multimap/hybrid.hpp
index 06a5f6e..c49cad1 100644
--- a/include/osmium/index/multimap/hybrid.hpp
+++ b/include/osmium/index/multimap/hybrid.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/multimap/sparse_file_array.hpp b/include/osmium/index/multimap/sparse_file_array.hpp
index c362001..a596e03 100644
--- a/include/osmium/index/multimap/sparse_file_array.hpp
+++ b/include/osmium/index/multimap/sparse_file_array.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/multimap/sparse_mem_array.hpp b/include/osmium/index/multimap/sparse_mem_array.hpp
index f718edd..a152bf2 100644
--- a/include/osmium/index/multimap/sparse_mem_array.hpp
+++ b/include/osmium/index/multimap/sparse_mem_array.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/multimap/sparse_mem_multimap.hpp b/include/osmium/index/multimap/sparse_mem_multimap.hpp
index 7a67cc7..6aa26cf 100644
--- a/include/osmium/index/multimap/sparse_mem_multimap.hpp
+++ b/include/osmium/index/multimap/sparse_mem_multimap.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/multimap/sparse_mmap_array.hpp b/include/osmium/index/multimap/sparse_mmap_array.hpp
index 6319c17..ef86b36 100644
--- a/include/osmium/index/multimap/sparse_mmap_array.hpp
+++ b/include/osmium/index/multimap/sparse_mmap_array.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/node_locations_map.hpp b/include/osmium/index/node_locations_map.hpp
index c6ce37d..0642875 100644
--- a/include/osmium/index/node_locations_map.hpp
+++ b/include/osmium/index/node_locations_map.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/index/relations_map.hpp b/include/osmium/index/relations_map.hpp
new file mode 100644
index 0000000..5f46e66
--- /dev/null
+++ b/include/osmium/index/relations_map.hpp
@@ -0,0 +1,292 @@
+#ifndef OSMIUM_INDEX_RELATIONS_MAP_HPP
+#define OSMIUM_INDEX_RELATIONS_MAP_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2017 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 <algorithm>
+#include <cassert>
+#include <cstdint>
+#include <tuple>
+#include <vector>
+
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+    namespace index {
+
+        namespace detail {
+
+            template <typename TKey, typename TKeyInternal, typename TValue, typename TValueInternal>
+            class flat_map {
+
+            public:
+
+                using key_type   = TKey;
+                using value_type = TValue;
+
+            private:
+
+                struct kv_pair {
+                    TKeyInternal key;
+                    TValueInternal value;
+
+                    explicit kv_pair(key_type key_id) :
+                        key(static_cast<TKeyInternal>(key_id)),
+                        value() {
+                    }
+
+                    kv_pair(key_type key_id, value_type value_id) :
+                        key(static_cast<TKeyInternal>(key_id)),
+                        value(static_cast<TValueInternal>(value_id)) {
+                    }
+
+                    bool operator<(const kv_pair& other) const noexcept {
+                        return std::tie(key, value) < std::tie(other.key, other.value);
+                    }
+
+                    bool operator==(const kv_pair& other) const noexcept {
+                        return std::tie(key, value) == std::tie(other.key, other.value);
+                    }
+                }; // struct kv_pair
+
+                std::vector<kv_pair> m_map;
+
+            public:
+
+                using const_iterator = typename std::vector<kv_pair>::const_iterator;
+
+                void set(key_type key, value_type value) {
+                    m_map.emplace_back(key, value);
+                }
+
+                void sort_unique() {
+                    std::sort(m_map.begin(), m_map.end());
+                    const auto last = std::unique(m_map.begin(), m_map.end());
+                    m_map.erase(last, m_map.end());
+                }
+
+                std::pair<const_iterator, const_iterator> get(key_type key) const noexcept {
+                    return std::equal_range(m_map.begin(), m_map.end(), kv_pair{key}, [](const kv_pair& lhs, const kv_pair& rhs) {
+                        return lhs.key < rhs.key;
+                    });
+                }
+
+                bool empty() const noexcept {
+                    return m_map.empty();
+                }
+
+                size_t size() const noexcept {
+                    return m_map.size();
+                }
+
+            }; // class flat_map
+
+        } // namespace detail
+
+        /**
+        * Index for looking up parent relation IDs given a member relation ID.
+        * You can not instantiate such an index yourself, instead you need to
+        * instantiate a RelationsMapStash, fill it and then create an index from
+        * it:
+        *
+        * @code
+        * RelationsMapStash stash;
+        * ...
+        * for_each_relation(const osmium::Relation& relation) {
+        *    stash.add_members(relation);
+        * }
+        * ...
+        * const auto index = stash.build_index();
+        * ...
+        * osmium::unsigned_object_id_type member_id = ...;
+        * index.for_each_parent(member_id, [](osmium::unsigned_object_id_type id) {
+        *   ...
+        * });
+        * ...
+        * @endcode
+        *
+        */
+        class RelationsMapIndex {
+
+            friend class RelationsMapStash;
+
+            using map_type = detail::flat_map<osmium::unsigned_object_id_type, uint32_t,
+                                            osmium::unsigned_object_id_type, uint32_t>;
+
+            map_type m_map;
+
+            RelationsMapIndex(map_type&& map) :
+                m_map(std::move(map)) {
+            }
+
+        public:
+
+            RelationsMapIndex() = delete;
+
+            RelationsMapIndex(const RelationsMapIndex&) = delete;
+            RelationsMapIndex& operator=(const RelationsMapIndex&) = delete;
+
+            RelationsMapIndex(RelationsMapIndex&&) = default;
+            RelationsMapIndex& operator=(RelationsMapIndex&&) = default;
+
+            /**
+            * Find the given relation id in the index and call the given function
+            * with all parent relation ids.
+            *
+            * @code
+            * osmium::unsigned_object_id_type member_id = 17;
+            * index.for_each_parent(member_id, [](osmium::unsigned_object_id_type id) {
+            *   ...
+            * });
+            * @endcode
+            *
+            * Complexity: Logarithmic in the number of elements in the index.
+            *             (Lookup uses binary search.)
+            */
+            template <typename Func>
+            void for_each_parent(osmium::unsigned_object_id_type member_id, Func&& func) const {
+                const auto parents = m_map.get(member_id);
+                for (auto it = parents.first; it != parents.second; ++it) {
+                    std::forward<Func>(func)(it->value);
+                }
+            }
+
+            /**
+            * Is this index empty?
+            *
+            * Complexity: Constant.
+            */
+            bool empty() const noexcept {
+                return m_map.empty();
+            }
+
+            /**
+            * How many entries are in this index?
+            *
+            * Complexity: Constant.
+            */
+            size_t size() const noexcept {
+                return m_map.size();
+            }
+
+        }; // RelationsMapIndex
+
+        /**
+        * The RelationsMapStash is used to build up the data needed to create
+        * an index of member relation ID to parent relation ID. See the
+        * RelationsMapIndex class for more.
+        */
+        class RelationsMapStash {
+
+            using map_type = detail::flat_map<osmium::unsigned_object_id_type, uint32_t,
+                                            osmium::unsigned_object_id_type, uint32_t>;
+
+            map_type m_map;
+
+#ifndef NDEBUG
+            bool m_valid = true;
+#endif
+
+        public:
+
+            RelationsMapStash() = default;
+
+            RelationsMapStash(const RelationsMapStash&) = delete;
+            RelationsMapStash& operator=(const RelationsMapStash&) = delete;
+
+            RelationsMapStash(RelationsMapStash&&) = default;
+            RelationsMapStash& operator=(RelationsMapStash&&) = default;
+
+            /**
+            * Add mapping from member to parent relation in the stash.
+            */
+            void add(osmium::unsigned_object_id_type member_id, osmium::unsigned_object_id_type relation_id) {
+                assert(m_valid && "You can't use the RelationsMap any more after calling build_index()");
+                m_map.set(member_id, relation_id);
+            }
+
+            /**
+            * Add mapping from all members to given parent relation in the stash.
+            */
+            void add_members(const osmium::Relation& relation) {
+                assert(m_valid && "You can't use the RelationsMap any more after calling build_index()");
+                for (const auto& member : relation.members()) {
+                    if (member.type() == osmium::item_type::relation) {
+                        m_map.set(member.positive_ref(), relation.positive_id());
+                    }
+                }
+            }
+
+            /**
+            * Is this stash empty?
+            *
+            * Complexity: Constant.
+            */
+            bool empty() const noexcept {
+                assert(m_valid && "You can't use the RelationsMap any more after calling build_index()");
+                return m_map.empty();
+            }
+
+            /**
+            * How many entries are in this stash?
+            *
+            * Complexity: Constant.
+            */
+            size_t size() const noexcept {
+                assert(m_valid && "You can't use the RelationsMap any more after calling build_index()");
+                return m_map.size();
+            }
+
+            /**
+            * Build an index with the contents of this stash and return it.
+            *
+            * After you get the index you can not use the stash any more!
+            */
+            RelationsMapIndex build_index() {
+                assert(m_valid && "You can't use the RelationsMap any more after calling build_index()");
+                m_map.sort_unique();
+#ifndef NDEBUG
+                m_valid = false;
+#endif
+                return RelationsMapIndex{std::move(m_map)};
+            }
+
+        }; // class RelationsMapStash
+
+    } // namespace index
+
+} // namespace osmium
+
+#endif // OSMIUM_INDEX_RELATIONS_MAP_HPP
diff --git a/include/osmium/io/any_compression.hpp b/include/osmium/io/any_compression.hpp
index 7f9f3d5..9ccc3f7 100644
--- a/include/osmium/io/any_compression.hpp
+++ b/include/osmium/io/any_compression.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/any_input.hpp b/include/osmium/io/any_input.hpp
index e4617e8..043c99d 100644
--- a/include/osmium/io/any_input.hpp
+++ b/include/osmium/io/any_input.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/any_output.hpp b/include/osmium/io/any_output.hpp
index 53cc5bb..32cc344 100644
--- a/include/osmium/io/any_output.hpp
+++ b/include/osmium/io/any_output.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/bzip2_compression.hpp b/include/osmium/io/bzip2_compression.hpp
index 63b4cad..84b2825 100644
--- a/include/osmium/io/bzip2_compression.hpp
+++ b/include/osmium/io/bzip2_compression.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/compression.hpp b/include/osmium/io/compression.hpp
index a38bba6..68011e8 100644
--- a/include/osmium/io/compression.hpp
+++ b/include/osmium/io/compression.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/debug_output.hpp b/include/osmium/io/debug_output.hpp
index 84db9cd..80c3954 100644
--- a/include/osmium/io/debug_output.hpp
+++ b/include/osmium/io/debug_output.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/debug_output_format.hpp b/include/osmium/io/detail/debug_output_format.hpp
index dc5323a..7e2f07e 100644
--- a/include/osmium/io/detail/debug_output_format.hpp
+++ b/include/osmium/io/detail/debug_output_format.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/input_format.hpp b/include/osmium/io/detail/input_format.hpp
index 67b05a8..beb5b68 100644
--- a/include/osmium/io/detail/input_format.hpp
+++ b/include/osmium/io/detail/input_format.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/o5m_input_format.hpp b/include/osmium/io/detail/o5m_input_format.hpp
index de4595e..36db4e4 100644
--- a/include/osmium/io/detail/o5m_input_format.hpp
+++ b/include/osmium/io/detail/o5m_input_format.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/opl_input_format.hpp b/include/osmium/io/detail/opl_input_format.hpp
index 1bd8b07..135f1a5 100644
--- a/include/osmium/io/detail/opl_input_format.hpp
+++ b/include/osmium/io/detail/opl_input_format.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/opl_output_format.hpp b/include/osmium/io/detail/opl_output_format.hpp
index acdfe29..bfcbcb1 100644
--- a/include/osmium/io/detail/opl_output_format.hpp
+++ b/include/osmium/io/detail/opl_output_format.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/opl_parser_functions.hpp b/include/osmium/io/detail/opl_parser_functions.hpp
index ee35b36..622a7ca 100644
--- a/include/osmium/io/detail/opl_parser_functions.hpp
+++ b/include/osmium/io/detail/opl_parser_functions.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/output_format.hpp b/include/osmium/io/detail/output_format.hpp
index 0fe4915..ccb0efd 100644
--- a/include/osmium/io/detail/output_format.hpp
+++ b/include/osmium/io/detail/output_format.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/pbf.hpp b/include/osmium/io/detail/pbf.hpp
index e23f8b9..acd1fc4 100644
--- a/include/osmium/io/detail/pbf.hpp
+++ b/include/osmium/io/detail/pbf.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/pbf_decoder.hpp b/include/osmium/io/detail/pbf_decoder.hpp
index 5164ce3..e47cff6 100644
--- a/include/osmium/io/detail/pbf_decoder.hpp
+++ b/include/osmium/io/detail/pbf_decoder.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/pbf_input_format.hpp b/include/osmium/io/detail/pbf_input_format.hpp
index 31e778a..be5f860 100644
--- a/include/osmium/io/detail/pbf_input_format.hpp
+++ b/include/osmium/io/detail/pbf_input_format.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/pbf_output_format.hpp b/include/osmium/io/detail/pbf_output_format.hpp
index 43aa8cc..d431a67 100644
--- a/include/osmium/io/detail/pbf_output_format.hpp
+++ b/include/osmium/io/detail/pbf_output_format.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/protobuf_tags.hpp b/include/osmium/io/detail/protobuf_tags.hpp
index 3eb7902..8bb2e56 100644
--- a/include/osmium/io/detail/protobuf_tags.hpp
+++ b/include/osmium/io/detail/protobuf_tags.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/queue_util.hpp b/include/osmium/io/detail/queue_util.hpp
index 386cd22..cb486ea 100644
--- a/include/osmium/io/detail/queue_util.hpp
+++ b/include/osmium/io/detail/queue_util.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/read_thread.hpp b/include/osmium/io/detail/read_thread.hpp
index a933787..007b37e 100644
--- a/include/osmium/io/detail/read_thread.hpp
+++ b/include/osmium/io/detail/read_thread.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/read_write.hpp b/include/osmium/io/detail/read_write.hpp
index a086e5b..d6aa8bc 100644
--- a/include/osmium/io/detail/read_write.hpp
+++ b/include/osmium/io/detail/read_write.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/string_table.hpp b/include/osmium/io/detail/string_table.hpp
index f1ddc87..ba725e5 100644
--- a/include/osmium/io/detail/string_table.hpp
+++ b/include/osmium/io/detail/string_table.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/string_util.hpp b/include/osmium/io/detail/string_util.hpp
index 0334b0e..df6c3e2 100644
--- a/include/osmium/io/detail/string_util.hpp
+++ b/include/osmium/io/detail/string_util.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/write_thread.hpp b/include/osmium/io/detail/write_thread.hpp
index 85ef811..1e22155 100644
--- a/include/osmium/io/detail/write_thread.hpp
+++ b/include/osmium/io/detail/write_thread.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/xml_input_format.hpp b/include/osmium/io/detail/xml_input_format.hpp
index 242ef9b..96a348b 100644
--- a/include/osmium/io/detail/xml_input_format.hpp
+++ b/include/osmium/io/detail/xml_input_format.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/xml_output_format.hpp b/include/osmium/io/detail/xml_output_format.hpp
index 3f47b0f..44d6831 100644
--- a/include/osmium/io/detail/xml_output_format.hpp
+++ b/include/osmium/io/detail/xml_output_format.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/detail/zlib.hpp b/include/osmium/io/detail/zlib.hpp
index 15ece7c..f26fb48 100644
--- a/include/osmium/io/detail/zlib.hpp
+++ b/include/osmium/io/detail/zlib.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/error.hpp b/include/osmium/io/error.hpp
index fb6ccf7..f02a298 100644
--- a/include/osmium/io/error.hpp
+++ b/include/osmium/io/error.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/file.hpp b/include/osmium/io/file.hpp
index 812c494..d537044 100644
--- a/include/osmium/io/file.hpp
+++ b/include/osmium/io/file.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/file_compression.hpp b/include/osmium/io/file_compression.hpp
index 1cfb0f0..9b36795 100644
--- a/include/osmium/io/file_compression.hpp
+++ b/include/osmium/io/file_compression.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/file_format.hpp b/include/osmium/io/file_format.hpp
index 72b4abc..01b560b 100644
--- a/include/osmium/io/file_format.hpp
+++ b/include/osmium/io/file_format.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/gzip_compression.hpp b/include/osmium/io/gzip_compression.hpp
index 5e3e233..27e18ee 100644
--- a/include/osmium/io/gzip_compression.hpp
+++ b/include/osmium/io/gzip_compression.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/header.hpp b/include/osmium/io/header.hpp
index abd85f8..c0a2043 100644
--- a/include/osmium/io/header.hpp
+++ b/include/osmium/io/header.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/input_iterator.hpp b/include/osmium/io/input_iterator.hpp
index 4cde92f..d35f58c 100644
--- a/include/osmium/io/input_iterator.hpp
+++ b/include/osmium/io/input_iterator.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/o5m_input.hpp b/include/osmium/io/o5m_input.hpp
index c59e3a6..405bd8c 100644
--- a/include/osmium/io/o5m_input.hpp
+++ b/include/osmium/io/o5m_input.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/opl_input.hpp b/include/osmium/io/opl_input.hpp
index ee9e447..954f8f6 100644
--- a/include/osmium/io/opl_input.hpp
+++ b/include/osmium/io/opl_input.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/opl_output.hpp b/include/osmium/io/opl_output.hpp
index c79e260..d833052 100644
--- a/include/osmium/io/opl_output.hpp
+++ b/include/osmium/io/opl_output.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/output_iterator.hpp b/include/osmium/io/output_iterator.hpp
index cf9291d..d0e9d3f 100644
--- a/include/osmium/io/output_iterator.hpp
+++ b/include/osmium/io/output_iterator.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/overwrite.hpp b/include/osmium/io/overwrite.hpp
index bc6a503..0837ff5 100644
--- a/include/osmium/io/overwrite.hpp
+++ b/include/osmium/io/overwrite.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/pbf_input.hpp b/include/osmium/io/pbf_input.hpp
index ab1f324..02b2277 100644
--- a/include/osmium/io/pbf_input.hpp
+++ b/include/osmium/io/pbf_input.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/pbf_output.hpp b/include/osmium/io/pbf_output.hpp
index 0a50f2c..db493a1 100644
--- a/include/osmium/io/pbf_output.hpp
+++ b/include/osmium/io/pbf_output.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/reader.hpp b/include/osmium/io/reader.hpp
index 89c8564..c39eaaa 100644
--- a/include/osmium/io/reader.hpp
+++ b/include/osmium/io/reader.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/reader_iterator.hpp b/include/osmium/io/reader_iterator.hpp
index 116442f..1fc69fc 100644
--- a/include/osmium/io/reader_iterator.hpp
+++ b/include/osmium/io/reader_iterator.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/writer.hpp b/include/osmium/io/writer.hpp
index c12d317..043f68f 100644
--- a/include/osmium/io/writer.hpp
+++ b/include/osmium/io/writer.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/writer_options.hpp b/include/osmium/io/writer_options.hpp
index 9f64164..bcc52fd 100644
--- a/include/osmium/io/writer_options.hpp
+++ b/include/osmium/io/writer_options.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/xml_input.hpp b/include/osmium/io/xml_input.hpp
index 87f4ecc..e20bdde 100644
--- a/include/osmium/io/xml_input.hpp
+++ b/include/osmium/io/xml_input.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/io/xml_output.hpp b/include/osmium/io/xml_output.hpp
index 344487d..cfb5e8a 100644
--- a/include/osmium/io/xml_output.hpp
+++ b/include/osmium/io/xml_output.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/memory/buffer.hpp b/include/osmium/memory/buffer.hpp
index bcf0bd0..370d01e 100644
--- a/include/osmium/memory/buffer.hpp
+++ b/include/osmium/memory/buffer.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -116,7 +116,7 @@ namespace osmium {
 #ifndef NDEBUG
             uint8_t m_builder_count{0};
 #endif
-            auto_grow m_auto_grow {auto_grow::no};
+            auto_grow m_auto_grow{auto_grow::no};
             std::function<void(Buffer&)> m_full;
 
         public:
diff --git a/include/osmium/memory/collection.hpp b/include/osmium/memory/collection.hpp
index fb413ff..21b7ab3 100644
--- a/include/osmium/memory/collection.hpp
+++ b/include/osmium/memory/collection.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -33,8 +33,8 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
-#include <iterator>
 #include <iosfwd>
+#include <iterator>
 #include <type_traits>
 
 #include <osmium/memory/item.hpp>
@@ -127,6 +127,10 @@ namespace osmium {
 
             static constexpr osmium::item_type itemtype = TCollectionItemType;
 
+            constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+                return t == itemtype;
+            }
+
             Collection() :
                 Item(sizeof(Collection<TMember, TCollectionItemType>), TCollectionItemType) {
             }
diff --git a/include/osmium/memory/item.hpp b/include/osmium/memory/item.hpp
index b72ca4d..6714ce2 100644
--- a/include/osmium/memory/item.hpp
+++ b/include/osmium/memory/item.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -146,6 +146,10 @@ namespace osmium {
 
         public:
 
+            constexpr static bool is_compatible_to(osmium::item_type /*t*/) noexcept {
+                return true;
+            }
+
             unsigned char* next() noexcept {
                 return data() + padded_size();
             }
diff --git a/include/osmium/memory/item_iterator.hpp b/include/osmium/memory/item_iterator.hpp
index 27ebc59..3cab11d 100644
--- a/include/osmium/memory/item_iterator.hpp
+++ b/include/osmium/memory/item_iterator.hpp
@@ -1,11 +1,11 @@
-#ifndef OSMIUM_ITEM_ITERATOR_HPP
-#define OSMIUM_ITEM_ITERATOR_HPP
+#ifndef OSMIUM_MEMORY_ITEM_ITERATOR_HPP
+#define OSMIUM_MEMORY_ITEM_ITERATOR_HPP
 
 /*
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -35,8 +35,8 @@ DEALINGS IN THE SOFTWARE.
 
 #include <cassert>
 #include <cstddef>
-#include <iterator>
 #include <iosfwd>
+#include <iterator>
 #include <type_traits>
 
 #include <osmium/memory/item.hpp>
@@ -44,86 +44,13 @@ DEALINGS IN THE SOFTWARE.
 
 namespace osmium {
 
-    class Area;
-    class Changeset;
-    class InnerRing;
-    class Node;
-    class OSMEntity;
-    class OSMObject;
-    class OuterRing;
-    class Relation;
-    class RelationMemberList;
-    class TagList;
-    class Way;
-    class WayNodeList;
-
     namespace memory {
 
         namespace detail {
 
             template <typename T>
-            inline bool type_is_compatible(osmium::item_type) noexcept {
-                return true;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::Node>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::node;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::Way>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::way;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::Relation>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::relation;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::Area>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::area;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::Changeset>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::changeset;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::OSMObject>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::node || t == osmium::item_type::way || t == osmium::item_type::relation || t == osmium::item_type::area;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::OSMEntity>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::node || t == osmium::item_type::way || t == osmium::item_type::relation || t == osmium::item_type::area || t == osmium::item_type::changeset;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::TagList>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::tag_list;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::WayNodeList>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::way_node_list;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::RelationMemberList>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::relation_member_list || t == osmium::item_type::relation_member_list_with_full_members;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::OuterRing>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::outer_ring;
-            }
-
-            template <>
-            inline bool type_is_compatible<osmium::InnerRing>(osmium::item_type t) noexcept {
-                return t == osmium::item_type::inner_ring;
+            constexpr inline bool type_is_compatible(osmium::item_type t) noexcept {
+                return T::is_compatible_to(t);
             }
 
         } // namespace detail
@@ -143,7 +70,7 @@ namespace osmium {
 
             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())) {
+                       !detail::type_is_compatible<TMember>(reinterpret_cast<const osmium::memory::Item*>(m_data)->type())) {
                     m_data = reinterpret_cast<TMember*>(m_data)->next();
                 }
             }
@@ -320,4 +247,4 @@ namespace osmium {
 
 } // namespace osmium
 
-#endif // OSMIUM_ITEM_ITERATOR_HPP
+#endif // OSMIUM_MEMORY_ITEM_ITERATOR_HPP
diff --git a/include/osmium/object_pointer_collection.hpp b/include/osmium/object_pointer_collection.hpp
index 71e1502..5accf42 100644
--- a/include/osmium/object_pointer_collection.hpp
+++ b/include/osmium/object_pointer_collection.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/opl.hpp b/include/osmium/opl.hpp
index 5666fa0..c77a6c0 100644
--- a/include/osmium/opl.hpp
+++ b/include/osmium/opl.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm.hpp b/include/osmium/osm.hpp
index fa8a92d..2a524ff 100644
--- a/include/osmium/osm.hpp
+++ b/include/osmium/osm.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/area.hpp b/include/osmium/osm/area.hpp
index d146e97..a980552 100644
--- a/include/osmium/osm/area.hpp
+++ b/include/osmium/osm/area.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -41,10 +41,11 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/memory/collection.hpp>
 #include <osmium/memory/item.hpp>
 #include <osmium/memory/item_iterator.hpp>
+#include <osmium/osm/box.hpp>
 #include <osmium/osm/item_type.hpp>
+#include <osmium/osm/node_ref_list.hpp>
 #include <osmium/osm/object.hpp>
 #include <osmium/osm/types.hpp>
-#include <osmium/osm/node_ref_list.hpp>
 #include <osmium/util/compatibility.hpp>
 
 namespace osmium {
@@ -63,6 +64,10 @@ namespace osmium {
 
         static constexpr osmium::item_type itemtype = osmium::item_type::outer_ring;
 
+        constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+            return t == itemtype;
+        }
+
         OuterRing():
             NodeRefList(itemtype) {
         }
@@ -80,6 +85,10 @@ namespace osmium {
 
         static constexpr osmium::item_type itemtype = osmium::item_type::inner_ring;
 
+        constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+            return t == itemtype;
+        }
+
         InnerRing():
             NodeRefList(itemtype) {
         }
@@ -129,6 +138,10 @@ namespace osmium {
 
         static constexpr osmium::item_type itemtype = osmium::item_type::area;
 
+        constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+            return t == itemtype;
+        }
+
         /**
          * Was this area created from a way? (In contrast to areas
          * created from a relation and their members.)
@@ -156,7 +169,7 @@ namespace osmium {
          * @returns Pair (number outer rings, number inner rings)
          */
         std::pair<size_t, size_t> num_rings() const {
-            std::pair<size_t, size_t> counter { 0, 0 };
+            std::pair<size_t, size_t> counter{0, 0};
 
             for (auto it = cbegin(); it != cend(); ++it) {
                 switch (it->type()) {
@@ -234,13 +247,26 @@ namespace osmium {
          * 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.
+         * all inner 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()};
         }
 
+        /**
+         * Calculate the envelope of this area.
+         *
+         * Complexity: Linear in the number of nodes in the outer rings.
+         */
+        osmium::Box envelope() const noexcept {
+            osmium::Box box;
+            for (const auto& ring : outer_rings()) {
+                box.extend(ring.envelope());
+            }
+            return box;
+        }
+
     }; // 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/box.hpp b/include/osmium/osm/box.hpp
index 52ca93d..a451e5a 100644
--- a/include/osmium/osm/box.hpp
+++ b/include/osmium/osm/box.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/changeset.hpp b/include/osmium/osm/changeset.hpp
index 828a2c2..7fbd466 100644
--- a/include/osmium/osm/changeset.hpp
+++ b/include/osmium/osm/changeset.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -183,6 +183,12 @@ namespace osmium {
 
     public:
 
+        static constexpr osmium::item_type itemtype = osmium::item_type::changeset;
+
+        constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+            return t == itemtype;
+        }
+
         // Dummy to avoid warning because of unused private fields. Do not use.
         int32_t do_not_use() const noexcept {
             return m_padding1 + m_padding2;
diff --git a/include/osmium/osm/crc.hpp b/include/osmium/osm/crc.hpp
index bf057fd..5de52ed 100644
--- a/include/osmium/osm/crc.hpp
+++ b/include/osmium/osm/crc.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/diff_object.hpp b/include/osmium/osm/diff_object.hpp
index 21cf139..cbf1e7c 100644
--- a/include/osmium/osm/diff_object.hpp
+++ b/include/osmium/osm/diff_object.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/entity.hpp b/include/osmium/osm/entity.hpp
index 140a559..75905c0 100644
--- a/include/osmium/osm/entity.hpp
+++ b/include/osmium/osm/entity.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -42,9 +42,9 @@ namespace osmium {
     namespace detail {
 
         template <typename TSubitem, typename TIter>
-        inline TSubitem& subitem_of_type(TIter it, TIter end) {
+        inline TSubitem& subitem_of_type(TIter it, const TIter& end) {
             for (; it != end; ++it) {
-                if (it->type() == TSubitem::itemtype) {
+                if (TSubitem::is_compatible_to(it->type())) {
                     return reinterpret_cast<TSubitem&>(*it);
                 }
             }
@@ -65,6 +65,14 @@ namespace osmium {
 
     public:
 
+        constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+            return t == osmium::item_type::node ||
+                   t == osmium::item_type::way ||
+                   t == osmium::item_type::relation ||
+                   t == osmium::item_type::area ||
+                   t == osmium::item_type::changeset;
+        }
+
         explicit OSMEntity(osmium::memory::item_size_type size, osmium::item_type type) :
             Item(size, type) {
         }
diff --git a/include/osmium/osm/entity_bits.hpp b/include/osmium/osm/entity_bits.hpp
index 05afe3b..2742bc9 100644
--- a/include/osmium/osm/entity_bits.hpp
+++ b/include/osmium/osm/entity_bits.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -64,16 +64,16 @@ namespace osmium {
                                     // but now we can't change it any more
                                     // without breaking lots of code
 
-            nothing    = 0x00,
-            node       = 0x01,
-            way        = 0x02,
-            relation   = 0x04,
-            nwr        = 0x07, ///< node, way, or relation object
-            area       = 0x08,
-            nwra       = 0x0f, ///< node, way, relation, or area object
-            object     = 0x0f, ///< node, way, relation, or area object
-            changeset  = 0x10,
-            all        = 0x1f  ///< object or changeset
+            nothing   = 0x00,
+            node      = 0x01,
+            way       = 0x02,
+            relation  = 0x04,
+            nwr       = 0x07, ///< node, way, or relation object
+            area      = 0x08,
+            nwra      = 0x0f, ///< node, way, relation, or area object
+            object    = 0x0f, ///< node, way, relation, or area object
+            changeset = 0x10,
+            all       = 0x1f ///< object or changeset
 
         }; // enum type
 
diff --git a/include/osmium/osm/item_type.hpp b/include/osmium/osm/item_type.hpp
index 272b2dd..2b6dec2 100644
--- a/include/osmium/osm/item_type.hpp
+++ b/include/osmium/osm/item_type.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/location.hpp b/include/osmium/osm/location.hpp
index bc03a2e..b2fdc1b 100644
--- a/include/osmium/osm/location.hpp
+++ b/include/osmium/osm/location.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/node.hpp b/include/osmium/osm/node.hpp
index f3df5e9..0e61bfb 100644
--- a/include/osmium/osm/node.hpp
+++ b/include/osmium/osm/node.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -60,6 +60,10 @@ namespace osmium {
 
         static constexpr osmium::item_type itemtype = osmium::item_type::node;
 
+        constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+            return t == itemtype;
+        }
+
         osmium::Location location() const noexcept {
             return m_location;
         }
diff --git a/include/osmium/osm/node_ref.hpp b/include/osmium/osm/node_ref.hpp
index 05b9ca7..c06f38f 100644
--- a/include/osmium/osm/node_ref.hpp
+++ b/include/osmium/osm/node_ref.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/node_ref_list.hpp b/include/osmium/osm/node_ref_list.hpp
index f430b63..4411a24 100644
--- a/include/osmium/osm/node_ref_list.hpp
+++ b/include/osmium/osm/node_ref_list.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -38,6 +38,7 @@ DEALINGS IN THE SOFTWARE.
 #include <iterator>
 
 #include <osmium/memory/item.hpp>
+#include <osmium/osm/box.hpp>
 #include <osmium/osm/item_type.hpp>
 #include <osmium/osm/location.hpp>
 #include <osmium/osm/node_ref.hpp>
@@ -101,6 +102,21 @@ namespace osmium {
         }
 
         /**
+         * Access specified element.
+         *
+         * Complexity: Constant.
+         *
+         * @pre @code n < size() @endcode
+         *
+         * @param n Get the n-th element of the collection.
+         */
+        NodeRef& operator[](size_type n) noexcept {
+            assert(n < size());
+            NodeRef* node_ref = &*(begin());
+            return node_ref[n];
+        }
+
+        /**
          * Access the first element.
          *
          * Complexity: Constant.
@@ -162,6 +178,20 @@ namespace osmium {
             return front().location() == back().location();
         }
 
+        /**
+         * Calculate the envelope of this node ref list. If the locations
+         * are not set, the resulting box will be invalid.
+         *
+         * Complexity: Linear in the number of elements.
+         */
+        osmium::Box envelope() const noexcept {
+            osmium::Box box;
+            for (const auto& node_ref : *this) {
+                box.extend(node_ref.location());
+            }
+            return box;
+        }
+
         /// Returns an iterator to the beginning.
         iterator begin() noexcept {
             return iterator(data() + sizeof(NodeRefList));
diff --git a/include/osmium/osm/object.hpp b/include/osmium/osm/object.hpp
index 01fe249..66f891a 100644
--- a/include/osmium/osm/object.hpp
+++ b/include/osmium/osm/object.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -37,6 +37,7 @@ DEALINGS IN THE SOFTWARE.
 #include <cstring>
 #include <stdexcept>
 #include <tuple>
+#include <type_traits>
 
 #include <osmium/memory/collection.hpp>
 #include <osmium/memory/item.hpp>
@@ -114,6 +115,13 @@ namespace osmium {
 
     public:
 
+        constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+            return t == osmium::item_type::node ||
+                   t == osmium::item_type::way ||
+                   t == osmium::item_type::relation ||
+                   t == osmium::item_type::area;
+        }
+
         /// Get ID of this object.
         object_id_type id() const noexcept {
             return m_id;
@@ -383,20 +391,22 @@ namespace osmium {
         /**
          * Get a range of subitems of a specific type.
          *
-         * @tparam The type (must be derived from osmium::memory::Item.
+         * @tparam The type (must be derived from osmium::memory::Item).
          */
         template <typename T>
         osmium::memory::ItemIteratorRange<T> subitems() {
+            static_assert(std::is_base_of<osmium::memory::Item, T>::value, "T must be derived from osmium::memory::Item");
             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.
+         * @tparam The type (must be derived from osmium::memory::Item).
          */
         template <typename T>
         osmium::memory::ItemIteratorRange<const T> subitems() const {
+            static_assert(std::is_base_of<osmium::memory::Item, T>::value, "T must be derived from osmium::memory::Item");
             return osmium::memory::ItemIteratorRange<const T>{subitems_position(), next()};
         }
 
diff --git a/include/osmium/osm/object_comparisons.hpp b/include/osmium/osm/object_comparisons.hpp
index e48773b..69dbf79 100644
--- a/include/osmium/osm/object_comparisons.hpp
+++ b/include/osmium/osm/object_comparisons.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/relation.hpp b/include/osmium/osm/relation.hpp
index 8c09680..deac43a 100644
--- a/include/osmium/osm/relation.hpp
+++ b/include/osmium/osm/relation.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -153,6 +153,11 @@ namespace osmium {
 
     public:
 
+        constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+            return t == osmium::item_type::relation_member_list ||
+                   t == osmium::item_type::relation_member_list_with_full_members;
+        }
+
         RelationMemberList() :
             osmium::memory::Collection<RelationMember, osmium::item_type::relation_member_list>() {
         }
@@ -174,6 +179,10 @@ namespace osmium {
 
         static constexpr osmium::item_type itemtype = osmium::item_type::relation;
 
+        constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+            return t == itemtype;
+        }
+
         RelationMemberList& members() {
             return osmium::detail::subitem_of_type<RelationMemberList>(begin(), end());
         }
diff --git a/include/osmium/osm/segment.hpp b/include/osmium/osm/segment.hpp
index c36533e..8d66aae 100644
--- a/include/osmium/osm/segment.hpp
+++ b/include/osmium/osm/segment.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/tag.hpp b/include/osmium/osm/tag.hpp
index e2537ce..3aaaf56 100644
--- a/include/osmium/osm/tag.hpp
+++ b/include/osmium/osm/tag.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/timestamp.hpp b/include/osmium/osm/timestamp.hpp
index 5f52430..b1c8edc 100644
--- a/include/osmium/osm/timestamp.hpp
+++ b/include/osmium/osm/timestamp.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/types.hpp b/include/osmium/osm/types.hpp
index ec46bb2..6a801c1 100644
--- a/include/osmium/osm/types.hpp
+++ b/include/osmium/osm/types.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/types_from_string.hpp b/include/osmium/osm/types_from_string.hpp
index 190dd29..d2a1421 100644
--- a/include/osmium/osm/types_from_string.hpp
+++ b/include/osmium/osm/types_from_string.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -97,7 +97,7 @@ namespace osmium {
             }
             osmium::item_type t = osmium::char_to_item_type(*input);
             if (osmium::osm_entity_bits::from_item_type(t) & types) {
-                return std::make_pair(t, string_to_object_id(input+1));
+                return std::make_pair(t, string_to_object_id(input + 1));
             }
         }
         throw std::range_error(std::string("not a valid id: '") + input + "'");
@@ -105,7 +105,7 @@ namespace osmium {
 
     namespace detail {
 
-        inline unsigned long string_to_ulong(const char* input, const char *name) {
+        inline unsigned long string_to_ulong(const char* input, const char* name) {
             if (*input != '\0' && *input != '-' && !std::isspace(*input)) {
                 char* end;
                 auto value = std::strtoul(input, &end, 10);
diff --git a/include/osmium/osm/undirected_segment.hpp b/include/osmium/osm/undirected_segment.hpp
index c2442ae..773b0d7 100644
--- a/include/osmium/osm/undirected_segment.hpp
+++ b/include/osmium/osm/undirected_segment.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/osm/way.hpp b/include/osmium/osm/way.hpp
index e4415ef..6b75b21 100644
--- a/include/osmium/osm/way.hpp
+++ b/include/osmium/osm/way.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -35,6 +35,7 @@ DEALINGS IN THE SOFTWARE.
 
 #include <osmium/memory/collection.hpp>
 #include <osmium/memory/item.hpp>
+#include <osmium/osm/box.hpp>
 #include <osmium/osm/entity.hpp>
 #include <osmium/osm/item_type.hpp>
 #include <osmium/osm/node_ref.hpp>
@@ -57,6 +58,10 @@ namespace osmium {
 
         static constexpr osmium::item_type itemtype = osmium::item_type::way_node_list;
 
+        constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+            return t == itemtype;
+        }
+
         WayNodeList():
             NodeRefList(itemtype) {
         }
@@ -76,6 +81,12 @@ namespace osmium {
 
     public:
 
+        static constexpr osmium::item_type itemtype = osmium::item_type::way;
+
+        constexpr static bool is_compatible_to(osmium::item_type t) noexcept {
+            return t == itemtype;
+        }
+
         WayNodeList& nodes() {
             return osmium::detail::subitem_of_type<WayNodeList>(begin(), end());
         }
@@ -111,6 +122,16 @@ namespace osmium {
             return nodes().ends_have_same_location();
         }
 
+        /**
+         * Calculate the envelope of this way. If the locations of the nodes
+         * are not set, the resulting box will be invalid.
+         *
+         * Complexity: Linear in the number of nodes.
+         */
+        osmium::Box envelope() const noexcept {
+            return nodes().envelope();
+        }
+
     }; // class Way
 
     static_assert(sizeof(Way) % osmium::memory::align_bytes == 0, "Class osmium::Way has wrong size to be aligned properly!");
diff --git a/include/osmium/relations/collector.hpp b/include/osmium/relations/collector.hpp
index 53f6de9..bede764 100644
--- a/include/osmium/relations/collector.hpp
+++ b/include/osmium/relations/collector.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -65,37 +65,33 @@ namespace osmium {
      */
     namespace relations {
 
-        namespace detail {
-
-        } // namespace detail
-
         /**
-         * The Collector class collects members of a relation. This is a generic
-         * base class that can be used to assemble all kinds of relations. It has numerous
-         * hooks you can implement in derived classes to customize its behaviour.
+         * The Collector class collects members of a relation. This is a
+         * generic base class that can be used to assemble all kinds of
+         * relations. It has numerous hooks you can implement in derived
+         * classes to customize its behaviour.
          *
-         * The collector provides two handlers (HandlerPass1 and HandlerPass2) for a first
-         * and second pass through an input file, respectively. In the first pass all
-         * relations we are interested in are stored in RelationMeta objects in the
-         * m_relations vector. All members we are interested in are stored in MemberMeta
-         * objects in the m_member_meta vectors.
-         * The MemberMeta objects also store the information where the relations containing
-         * those members are to be found.
+         * The collector provides two handlers (HandlerPass1 and HandlerPass2)
+         * for a first and second pass through an input file, respectively. In
+         * the first pass all relations we are interested in are stored in
+         * RelationMeta objects in the m_relations vector. All members we are
+         * interested in are stored in MemberMeta objects in the m_member_meta
+         * vectors. The MemberMeta objects also store the information where the
+         * relations containing those members are to be found.
          *
-         * Later the m_member_meta vectors are sorted according to the
-         * member ids so that a binary search (with std::equal_range) can be used in the second
-         * pass to find the parent relations for each node, way, or relation coming along.
-         * The member objects are stored together with their relation and once a relation
-         * is complete the complete_relation() method is called which you must overwrite in
-         * a derived class of Collector.
+         * Later the m_member_meta vectors are sorted according to the member
+         * ids so that a binary search (with std::equal_range) can be used in
+         * the second pass to find the parent relations for each node, way, or
+         * relation coming along. The member objects are stored together with
+         * their relation and once a relation is complete the
+         * complete_relation() method is called which you must overwrite in a
+         * derived class of Collector.
          *
          * @tparam TCollector Derived class of this class.
-         *
          * @tparam TNodes Are we interested in member nodes?
-         *
          * @tparam TWays Are we interested in member ways?
-         *
          * @tparam TRelations Are we interested in member relations?
+         * @pre The Ids of all objects must be unique in the input data.
          */
         template <typename TCollector, bool TNodes, bool TWays, bool TRelations>
         class Collector {
diff --git a/include/osmium/relations/detail/member_meta.hpp b/include/osmium/relations/detail/member_meta.hpp
index 7624a60..a150556 100644
--- a/include/osmium/relations/detail/member_meta.hpp
+++ b/include/osmium/relations/detail/member_meta.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/relations/detail/relation_meta.hpp b/include/osmium/relations/detail/relation_meta.hpp
index b71c5a5..03476a5 100644
--- a/include/osmium/relations/detail/relation_meta.hpp
+++ b/include/osmium/relations/detail/relation_meta.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/tags/filter.hpp b/include/osmium/tags/filter.hpp
index 27a8360..22abb14 100644
--- a/include/osmium/tags/filter.hpp
+++ b/include/osmium/tags/filter.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/tags/regex_filter.hpp b/include/osmium/tags/regex_filter.hpp
index 3df94dd..72c3217 100644
--- a/include/osmium/tags/regex_filter.hpp
+++ b/include/osmium/tags/regex_filter.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -43,6 +43,13 @@ namespace osmium {
     namespace tags {
 
         template <>
+        struct match_key<std::regex> {
+            bool operator()(const std::regex& rule_key, const char* tag_key) {
+                return std::regex_match(tag_key, rule_key);
+            }
+        }; // struct match_key<std::regex>
+
+        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);
diff --git a/include/osmium/tags/taglist.hpp b/include/osmium/tags/taglist.hpp
index d786279..30db9e9 100644
--- a/include/osmium/tags/taglist.hpp
+++ b/include/osmium/tags/taglist.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/thread/function_wrapper.hpp b/include/osmium/thread/function_wrapper.hpp
index 4366c0a..2ada084 100644
--- a/include/osmium/thread/function_wrapper.hpp
+++ b/include/osmium/thread/function_wrapper.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/thread/pool.hpp b/include/osmium/thread/pool.hpp
index 613f227..06d96f5 100644
--- a/include/osmium/thread/pool.hpp
+++ b/include/osmium/thread/pool.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/thread/queue.hpp b/include/osmium/thread/queue.hpp
index 5ae9108..3896fef 100644
--- a/include/osmium/thread/queue.hpp
+++ b/include/osmium/thread/queue.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/thread/util.hpp b/include/osmium/thread/util.hpp
index 2eeb999..4eb59c9 100644
--- a/include/osmium/thread/util.hpp
+++ b/include/osmium/thread/util.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/util/cast.hpp b/include/osmium/util/cast.hpp
index c31ffce..76c10b2 100644
--- a/include/osmium/util/cast.hpp
+++ b/include/osmium/util/cast.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/util/compatibility.hpp b/include/osmium/util/compatibility.hpp
index b83cc15..f891173 100644
--- a/include/osmium/util/compatibility.hpp
+++ b/include/osmium/util/compatibility.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/util/config.hpp b/include/osmium/util/config.hpp
index e041235..8dba911 100644
--- a/include/osmium/util/config.hpp
+++ b/include/osmium/util/config.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -67,7 +67,7 @@ namespace osmium {
         }
 
         inline size_t get_max_queue_size(const char* queue_name, size_t default_value) noexcept {
-            std::string name {"OSMIUM_MAX_"};
+            std::string name{"OSMIUM_MAX_"};
             name += queue_name;
             name += "_QUEUE_SIZE";
             const char* env = getenv(name.c_str());
diff --git a/include/osmium/util/delta.hpp b/include/osmium/util/delta.hpp
index 8894dd7..ec0ddd6 100644
--- a/include/osmium/util/delta.hpp
+++ b/include/osmium/util/delta.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/util/double.hpp b/include/osmium/util/double.hpp
index 9714bf6..837faab 100644
--- a/include/osmium/util/double.hpp
+++ b/include/osmium/util/double.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -67,10 +67,10 @@ namespace osmium {
 #endif
             assert(len > 0 && len < max_double_length);
 
-            while (buffer[len-1] == '0') {
+            while (buffer[len - 1] == '0') {
                 --len;
             }
-            if (buffer[len-1] == '.') {
+            if (buffer[len - 1] == '.') {
                 --len;
             }
 
diff --git a/include/osmium/util/endian.hpp b/include/osmium/util/endian.hpp
index fe23a55..3ca2c2f 100644
--- a/include/osmium/util/endian.hpp
+++ b/include/osmium/util/endian.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -35,7 +35,9 @@ DEALINGS IN THE SOFTWARE.
 
 // Windows is only available for little endian architectures
 // http://stackoverflow.com/questions/6449468/can-i-safely-assume-that-windows-installations-will-always-be-little-endian
-#if !defined(_WIN32) && !defined(__APPLE__)
+#if defined(__FreeBSD__)
+# include <sys/endian.h>
+#elif !defined(_WIN32) && !defined(__APPLE__)
 # include <endian.h>
 #else
 # define __LITTLE_ENDIAN 1234
diff --git a/include/osmium/util/file.hpp b/include/osmium/util/file.hpp
index 4c951e7..a26d88f 100644
--- a/include/osmium/util/file.hpp
+++ b/include/osmium/util/file.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/util/iterator.hpp b/include/osmium/util/iterator.hpp
index 42d23e8..1cc6e0e 100644
--- a/include/osmium/util/iterator.hpp
+++ b/include/osmium/util/iterator.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/util/memory.hpp b/include/osmium/util/memory.hpp
index 777a6e0..1c2b9d8 100644
--- a/include/osmium/util/memory.hpp
+++ b/include/osmium/util/memory.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -42,18 +42,17 @@ namespace osmium {
     class MemoryUsage {
 
         int m_current = 0;
-        int m_peak = 0;
+        int m_peak    = 0;
 
 #ifdef __linux__
         static int parse_number(const std::string& line) {
             const auto f = line.find_first_of("0123456789");
             const auto l = line.find_last_of("0123456789");
-            return std::atoi(line.substr(f, l-f+1).c_str());
+            return std::atoi(line.substr(f, l - f + 1).c_str());
         }
 #endif
 
     public:
-
         /**
          * Get the memory usage for the current process. The constructor will
          * get the memory usage. Use the current() and peak() calls to access
@@ -69,7 +68,7 @@ namespace osmium {
 
             if (status_file.is_open()) {
                 std::string line;
-                while (! status_file.eof() ) {
+                while (!status_file.eof()) {
                     std::getline(status_file, line);
                     if (line.substr(0, 6) == "VmPeak") {
                         m_peak = parse_number(line);
diff --git a/include/osmium/util/memory_mapping.hpp b/include/osmium/util/memory_mapping.hpp
index 4fc8f01..9996c8a 100644
--- a/include/osmium/util/memory_mapping.hpp
+++ b/include/osmium/util/memory_mapping.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -94,14 +94,15 @@ namespace osmium {
          */
         class MemoryMapping {
 
-public:
+        public:
+
             enum class mapping_mode {
                 readonly      = 0,
                 write_private = 1,
                 write_shared  = 2
             };
 
-private:
+        private:
 
             /// The size of the mapping
             size_t m_size;
diff --git a/include/osmium/util/minmax.hpp b/include/osmium/util/minmax.hpp
index b4ff4ea..833bc2a 100644
--- a/include/osmium/util/minmax.hpp
+++ b/include/osmium/util/minmax.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -39,7 +39,7 @@ namespace osmium {
 
     template <typename T>
     inline T min_op_start_value() {
-         return std::numeric_limits<T>::max();
+        return std::numeric_limits<T>::max();
     }
 
     /**
@@ -78,7 +78,7 @@ namespace osmium {
 
     template <typename T>
     inline T max_op_start_value() {
-         return std::numeric_limits<T>::min();
+        return std::numeric_limits<T>::min();
     }
 
     /**
diff --git a/include/osmium/util/misc.hpp b/include/osmium/util/misc.hpp
index 8acecb5..14192b4 100644
--- a/include/osmium/util/misc.hpp
+++ b/include/osmium/util/misc.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -41,7 +41,7 @@ namespace osmium {
      * Like std::tie(), but takes its arguments as const references. Used
      * as a helper function when sorting.
      */
-    template<typename... Ts>
+    template <typename... Ts>
     inline std::tuple<const Ts&...>
     const_tie(const Ts&... args) noexcept {
         return std::tuple<const Ts&...>(args...);
diff --git a/include/osmium/util/options.hpp b/include/osmium/util/options.hpp
index 79818a9..ddc1138 100644
--- a/include/osmium/util/options.hpp
+++ b/include/osmium/util/options.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/util/progress_bar.hpp b/include/osmium/util/progress_bar.hpp
index 0e528fc..6e6d012 100644
--- a/include/osmium/util/progress_bar.hpp
+++ b/include/osmium/util/progress_bar.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/util/string.hpp b/include/osmium/util/string.hpp
index 2cdb983..595307e 100644
--- a/include/osmium/util/string.hpp
+++ b/include/osmium/util/string.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/util/timer.hpp b/include/osmium/util/timer.hpp
index 8cae80a..13ea8b9 100644
--- a/include/osmium/util/timer.hpp
+++ b/include/osmium/util/timer.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/util/verbose_output.hpp b/include/osmium/util/verbose_output.hpp
index f85d265..a47d29a 100644
--- a/include/osmium/util/verbose_output.hpp
+++ b/include/osmium/util/verbose_output.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
diff --git a/include/osmium/version.hpp b/include/osmium/version.hpp
index 4c66b99..d32641a 100644
--- a/include/osmium/version.hpp
+++ b/include/osmium/version.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -34,9 +34,9 @@ DEALINGS IN THE SOFTWARE.
 */
 
 #define LIBOSMIUM_VERSION_MAJOR 2
-#define LIBOSMIUM_VERSION_MINOR 10
-#define LIBOSMIUM_VERSION_PATCH 3
+#define LIBOSMIUM_VERSION_MINOR 11
+#define LIBOSMIUM_VERSION_PATCH 0
 
-#define LIBOSMIUM_VERSION_STRING "2.10.3"
+#define LIBOSMIUM_VERSION_STRING "2.11.0"
 
 #endif // OSMIUM_VERSION_HPP
diff --git a/include/osmium/visitor.hpp b/include/osmium/visitor.hpp
index 4270968..fa2d765 100644
--- a/include/osmium/visitor.hpp
+++ b/include/osmium/visitor.hpp
@@ -5,7 +5,7 @@
 
 This file is part of Osmium (http://osmcode.org/libosmium).
 
-Copyright 2013-2016 Jochen Topf <jochen at topf.org> and others (see README).
+Copyright 2013-2017 Jochen Topf <jochen at topf.org> and others (see README).
 
 Boost Software License - Version 1.0 - August 17th, 2003
 
@@ -34,6 +34,7 @@ DEALINGS IN THE SOFTWARE.
 */
 
 #include <type_traits>
+#include <utility>
 
 #include <osmium/fwd.hpp>
 #include <osmium/io/reader_iterator.hpp> // IWYU pragma: keep
@@ -44,208 +45,191 @@ DEALINGS IN THE SOFTWARE.
 
 namespace osmium {
 
-    namespace memory {
-        class Item;
-    } // namespace memory
-
     namespace detail {
 
         template <typename T, typename U>
         using ConstIfConst = typename std::conditional<std::is_const<T>::value, typename std::add_const<U>::type, U>::type;
 
         template <typename THandler, typename TItem>
-        inline void apply_item_recurse(TItem& item, THandler& handler) {
+        inline void apply_item_impl(TItem& item, THandler&& handler) {
             switch (item.type()) {
                 case osmium::item_type::undefined:
                     break;
                 case osmium::item_type::node:
-                    handler.osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
-                    handler.node(static_cast<ConstIfConst<TItem, osmium::Node>&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
+                    std::forward<THandler>(handler).node(static_cast<ConstIfConst<TItem, osmium::Node>&>(item));
                     break;
                 case osmium::item_type::way:
-                    handler.osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
-                    handler.way(static_cast<ConstIfConst<TItem, osmium::Way>&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
+                    std::forward<THandler>(handler).way(static_cast<ConstIfConst<TItem, osmium::Way>&>(item));
                     break;
                 case osmium::item_type::relation:
-                    handler.osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
-                    handler.relation(static_cast<ConstIfConst<TItem, osmium::Relation>&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
+                    std::forward<THandler>(handler).relation(static_cast<ConstIfConst<TItem, osmium::Relation>&>(item));
                     break;
                 case osmium::item_type::area:
-                    handler.osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
-                    handler.area(static_cast<ConstIfConst<TItem, osmium::Area>&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<ConstIfConst<TItem, osmium::OSMObject>&>(item));
+                    std::forward<THandler>(handler).area(static_cast<ConstIfConst<TItem, osmium::Area>&>(item));
                     break;
                 case osmium::item_type::changeset:
-                    handler.changeset(static_cast<ConstIfConst<TItem, osmium::Changeset>&>(item));
+                    std::forward<THandler>(handler).changeset(static_cast<ConstIfConst<TItem, osmium::Changeset>&>(item));
                     break;
                 case osmium::item_type::tag_list:
-                    handler.tag_list(static_cast<ConstIfConst<TItem, osmium::TagList>&>(item));
+                    std::forward<THandler>(handler).tag_list(static_cast<ConstIfConst<TItem, osmium::TagList>&>(item));
                     break;
                 case osmium::item_type::way_node_list:
-                    handler.way_node_list(static_cast<ConstIfConst<TItem, osmium::WayNodeList>&>(item));
+                    std::forward<THandler>(handler).way_node_list(static_cast<ConstIfConst<TItem, osmium::WayNodeList>&>(item));
                     break;
                 case osmium::item_type::relation_member_list:
                 case osmium::item_type::relation_member_list_with_full_members:
-                    handler.relation_member_list(static_cast<ConstIfConst<TItem, osmium::RelationMemberList>&>(item));
+                    std::forward<THandler>(handler).relation_member_list(static_cast<ConstIfConst<TItem, osmium::RelationMemberList>&>(item));
                     break;
                 case osmium::item_type::outer_ring:
-                    handler.outer_ring(static_cast<ConstIfConst<TItem, osmium::OuterRing>&>(item));
+                    std::forward<THandler>(handler).outer_ring(static_cast<ConstIfConst<TItem, osmium::OuterRing>&>(item));
                     break;
                 case osmium::item_type::inner_ring:
-                    handler.inner_ring(static_cast<ConstIfConst<TItem, osmium::InnerRing>&>(item));
+                    std::forward<THandler>(handler).inner_ring(static_cast<ConstIfConst<TItem, osmium::InnerRing>&>(item));
                     break;
                 case osmium::item_type::changeset_discussion:
-                    handler.changeset_discussion(static_cast<ConstIfConst<TItem, osmium::ChangesetDiscussion>&>(item));
+                    std::forward<THandler>(handler).changeset_discussion(static_cast<ConstIfConst<TItem, osmium::ChangesetDiscussion>&>(item));
                     break;
             }
         }
 
         template <typename THandler>
-        inline void apply_item_recurse(const osmium::OSMEntity& item, THandler& handler) {
+        inline void apply_item_impl(const osmium::OSMEntity& item, THandler&& handler) {
             switch (item.type()) {
                 case osmium::item_type::node:
-                    handler.osm_object(static_cast<const osmium::OSMObject&>(item));
-                    handler.node(static_cast<const osmium::Node&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<const osmium::OSMObject&>(item));
+                    std::forward<THandler>(handler).node(static_cast<const osmium::Node&>(item));
                     break;
                 case osmium::item_type::way:
-                    handler.osm_object(static_cast<const osmium::OSMObject&>(item));
-                    handler.way(static_cast<const osmium::Way&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<const osmium::OSMObject&>(item));
+                    std::forward<THandler>(handler).way(static_cast<const osmium::Way&>(item));
                     break;
                 case osmium::item_type::relation:
-                    handler.osm_object(static_cast<const osmium::OSMObject&>(item));
-                    handler.relation(static_cast<const osmium::Relation&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<const osmium::OSMObject&>(item));
+                    std::forward<THandler>(handler).relation(static_cast<const osmium::Relation&>(item));
                     break;
                 case osmium::item_type::area:
-                    handler.osm_object(static_cast<const osmium::OSMObject&>(item));
-                    handler.area(static_cast<const osmium::Area&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<const osmium::OSMObject&>(item));
+                    std::forward<THandler>(handler).area(static_cast<const osmium::Area&>(item));
                     break;
                 case osmium::item_type::changeset:
-                    handler.changeset(static_cast<const osmium::Changeset&>(item));
+                    std::forward<THandler>(handler).changeset(static_cast<const osmium::Changeset&>(item));
                     break;
                 default:
-                    throw osmium::unknown_type();
+                    throw osmium::unknown_type{};
             }
         }
 
         template <typename THandler>
-        inline void apply_item_recurse(osmium::OSMEntity& item, THandler& handler) {
+        inline void apply_item_impl(osmium::OSMEntity& item, THandler&& handler) {
             switch (item.type()) {
                 case osmium::item_type::node:
-                    handler.osm_object(static_cast<osmium::OSMObject&>(item));
-                    handler.node(static_cast<osmium::Node&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<osmium::OSMObject&>(item));
+                    std::forward<THandler>(handler).node(static_cast<osmium::Node&>(item));
                     break;
                 case osmium::item_type::way:
-                    handler.osm_object(static_cast<osmium::OSMObject&>(item));
-                    handler.way(static_cast<osmium::Way&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<osmium::OSMObject&>(item));
+                    std::forward<THandler>(handler).way(static_cast<osmium::Way&>(item));
                     break;
                 case osmium::item_type::relation:
-                    handler.osm_object(static_cast<osmium::OSMObject&>(item));
-                    handler.relation(static_cast<osmium::Relation&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<osmium::OSMObject&>(item));
+                    std::forward<THandler>(handler).relation(static_cast<osmium::Relation&>(item));
                     break;
                 case osmium::item_type::area:
-                    handler.osm_object(static_cast<osmium::OSMObject&>(item));
-                    handler.area(static_cast<osmium::Area&>(item));
+                    std::forward<THandler>(handler).osm_object(static_cast<osmium::OSMObject&>(item));
+                    std::forward<THandler>(handler).area(static_cast<osmium::Area&>(item));
                     break;
                 case osmium::item_type::changeset:
-                    handler.changeset(static_cast<osmium::Changeset&>(item));
+                    std::forward<THandler>(handler).changeset(static_cast<osmium::Changeset&>(item));
                     break;
                 default:
-                    throw osmium::unknown_type();
+                    throw osmium::unknown_type{};
             }
         }
 
         template <typename THandler>
-        inline void apply_item_recurse(const osmium::OSMObject& item, THandler& handler) {
+        inline void apply_item_impl(const osmium::OSMObject& item, THandler&& handler) {
             switch (item.type()) {
                 case osmium::item_type::node:
-                    handler.osm_object(item);
-                    handler.node(static_cast<const osmium::Node&>(item));
+                    std::forward<THandler>(handler).osm_object(item);
+                    std::forward<THandler>(handler).node(static_cast<const osmium::Node&>(item));
                     break;
                 case osmium::item_type::way:
-                    handler.osm_object(item);
-                    handler.way(static_cast<const osmium::Way&>(item));
+                    std::forward<THandler>(handler).osm_object(item);
+                    std::forward<THandler>(handler).way(static_cast<const osmium::Way&>(item));
                     break;
                 case osmium::item_type::relation:
-                    handler.osm_object(item);
-                    handler.relation(static_cast<const osmium::Relation&>(item));
+                    std::forward<THandler>(handler).osm_object(item);
+                    std::forward<THandler>(handler).relation(static_cast<const osmium::Relation&>(item));
                     break;
                 case osmium::item_type::area:
-                    handler.osm_object(item);
-                    handler.area(static_cast<const osmium::Area&>(item));
+                    std::forward<THandler>(handler).osm_object(item);
+                    std::forward<THandler>(handler).area(static_cast<const osmium::Area&>(item));
                     break;
                 default:
-                    throw osmium::unknown_type();
+                    throw osmium::unknown_type{};
             }
         }
 
         template <typename THandler>
-        inline void apply_item_recurse(osmium::OSMObject& item, THandler& handler) {
+        inline void apply_item_impl(osmium::OSMObject& item, THandler&& handler) {
             switch (item.type()) {
                 case osmium::item_type::node:
-                    handler.osm_object(item);
-                    handler.node(static_cast<osmium::Node&>(item));
+                    std::forward<THandler>(handler).osm_object(item);
+                    std::forward<THandler>(handler).node(static_cast<osmium::Node&>(item));
                     break;
                 case osmium::item_type::way:
-                    handler.osm_object(item);
-                    handler.way(static_cast<osmium::Way&>(item));
+                    std::forward<THandler>(handler).osm_object(item);
+                    std::forward<THandler>(handler).way(static_cast<osmium::Way&>(item));
                     break;
                 case osmium::item_type::relation:
-                    handler.osm_object(item);
-                    handler.relation(static_cast<osmium::Relation&>(item));
+                    std::forward<THandler>(handler).osm_object(item);
+                    std::forward<THandler>(handler).relation(static_cast<osmium::Relation&>(item));
                     break;
                 case osmium::item_type::area:
-                    handler.osm_object(item);
-                    handler.area(static_cast<osmium::Area&>(item));
+                    std::forward<THandler>(handler).osm_object(item);
+                    std::forward<THandler>(handler).area(static_cast<osmium::Area&>(item));
                     break;
                 default:
-                    throw osmium::unknown_type();
+                    throw osmium::unknown_type{};
             }
         }
 
-        template <typename THandler, typename TItem, typename... TRest>
-        inline void apply_item_recurse(TItem& item, THandler& handler, TRest&... more) {
-            apply_item_recurse(item, handler);
-            apply_item_recurse(item, more...);
-        }
-
-        template <typename THandler>
-        inline void flush_recurse(THandler& handler) {
-            handler.flush();
-        }
-
-        template <typename THandler, typename... TRest>
-        inline void flush_recurse(THandler& handler, TRest&... more) {
-            flush_recurse(handler);
-            flush_recurse(more...);
-        }
-
     } // namespace detail
 
-    template <typename... THandlers>
-    inline void apply_item(const osmium::memory::Item& item, THandlers&... handlers) {
-        detail::apply_item_recurse(item, handlers...);
+    template <typename TItem, typename... THandlers>
+    inline void apply_item(TItem& item, THandlers&&... handlers) {
+        (void)std::initializer_list<int>{
+            (detail::apply_item_impl(item, std::forward<THandlers>(handlers)), 0)...
+        };
     }
 
     template <typename... THandlers>
-    inline void apply_item(osmium::memory::Item& item, THandlers&... handlers) {
-        detail::apply_item_recurse(item, handlers...);
+    inline void apply_flush(THandlers&&... handlers) {
+        (void)std::initializer_list<int>{
+            (std::forward<THandlers>(handlers).flush(), 0)...
+        };
     }
 
     template <typename TIterator, typename... THandlers>
-    inline void apply(TIterator it, TIterator end, THandlers&... handlers) {
+    inline void apply(TIterator it, TIterator end, THandlers&&... handlers) {
         for (; it != end; ++it) {
-            detail::apply_item_recurse(*it, handlers...);
+            apply_item(*it, std::forward<THandlers>(handlers)...);
         }
-        detail::flush_recurse(handlers...);
+        apply_flush(std::forward<THandlers>(handlers)...);
     }
 
     template <typename TContainer, typename... THandlers>
-    inline void apply(TContainer& c, THandlers&... handlers) {
-        apply(std::begin(c), std::end(c), handlers...);
+    inline void apply(TContainer& c, THandlers&&... handlers) {
+        apply(std::begin(c), std::end(c), std::forward<THandlers>(handlers)...);
     }
 
     template <typename... THandlers>
-    inline void apply(const osmium::memory::Buffer& buffer, THandlers&... handlers) {
-        apply(buffer.cbegin(), buffer.cend(), handlers...);
+    inline void apply(const osmium::memory::Buffer& buffer, THandlers&&... handlers) {
+        apply(buffer.cbegin(), buffer.cend(), std::forward<THandlers>(handlers)...);
     }
 
 } // namespace osmium
diff --git a/include/protozero/byteswap.hpp b/include/protozero/byteswap.hpp
index 3afb348..bca4844 100644
--- a/include/protozero/byteswap.hpp
+++ b/include/protozero/byteswap.hpp
@@ -16,8 +16,8 @@ documentation.
  * @brief Contains functions to swap bytes in values (for different endianness).
  */
 
-#include <cstdint>
 #include <cassert>
+#include <cstdint>
 
 #include <protozero/config.hpp>
 
diff --git a/include/protozero/pbf_builder.hpp b/include/protozero/pbf_builder.hpp
index 39af53f..c40727c 100644
--- a/include/protozero/pbf_builder.hpp
+++ b/include/protozero/pbf_builder.hpp
@@ -18,8 +18,8 @@ documentation.
 
 #include <type_traits>
 
-#include <protozero/types.hpp>
 #include <protozero/pbf_writer.hpp>
+#include <protozero/types.hpp>
 
 namespace protozero {
 
@@ -87,6 +87,11 @@ public:
         pbf_writer::add_bytes(pbf_tag_type(tag), value);
     }
 
+    template <typename... Ts>
+    void add_bytes_vectored(T tag, Ts&&... values) {
+        pbf_writer::add_bytes_vectored(pbf_tag_type(tag), std::forward<Ts>(values)...);
+    }
+
     void add_string(T tag, const char* value, std::size_t size) {
         pbf_writer::add_string(pbf_tag_type(tag), value, size);
     }
diff --git a/include/protozero/pbf_reader.hpp b/include/protozero/pbf_reader.hpp
index 8754cab..98920fa 100644
--- a/include/protozero/pbf_reader.hpp
+++ b/include/protozero/pbf_reader.hpp
@@ -321,6 +321,9 @@ public:
      *    }
      * @endcode
      *
+     * Note that this will not check the wire type. The two-argument version
+     * of this function will also check the wire type.
+     *
      * @returns `true` if there is a next field with this tag.
      * @pre There must be no current field.
      * @post If it returns `true` there is a current field now with the given tag.
@@ -337,6 +340,45 @@ public:
     }
 
     /**
+     * Set next field with given tag and wire type in the message as the
+     * current field. Fields with other tags are skipped. This is usually
+     * called in a while loop for repeated fields:
+     *
+     * @code
+     *    pbf_reader message(...);
+     *    while (message.next(17, pbf_wire_type::varint)) {
+     *        // handle field
+     *    }
+     * @endcode
+     *
+     * or you can call it just once to get the one field with this tag:
+     *
+     * @code
+     *    pbf_reader message(...);
+     *    if (message.next(17, pbf_wire_type::varint)) {
+     *        // handle field
+     *    }
+     * @endcode
+     *
+     * Note that this will also check the wire type. The one-argument version
+     * of this function will not check the wire type.
+     *
+     * @returns `true` if there is a next field with this tag.
+     * @pre There must be no current field.
+     * @post If it returns `true` there is a current field now with the given tag.
+     */
+    bool next(pbf_tag_type next_tag, pbf_wire_type wire_type) {
+        while (next()) {
+            if (m_tag == next_tag && m_wire_type == wire_type) {
+                return true;
+            } else {
+                skip();
+            }
+        }
+        return false;
+    }
+
+    /**
      * The tag of the current field. The tag is the field number from the
      * description in the .proto file.
      *
@@ -369,6 +411,32 @@ public:
     }
 
     /**
+     * Get the tag and wire type of the current field in one integer suitable
+     * for comparison with a switch statement.
+     *
+     * Use it like this:
+     *
+     * @code
+     *    pbf_reader message(...);
+     *    while (message.next()) {
+     *        switch (message.tag_and_type()) {
+     *            case tag_and_type(17, pbf_wire_type::length_delimited):
+     *                ....
+     *                break;
+     *            case tag_and_type(21, pbf_wire_type::varint):
+     *                ....
+     *                break;
+     *            default:
+     *                message.skip();
+     *        }
+     *    }
+     * @endcode
+     */
+    uint32_t tag_and_type() const noexcept {
+        return protozero::tag_and_type(tag(), wire_type());
+    }
+
+    /**
      * Check the wire type of the current field.
      *
      * @returns `true` if the current field has the given wire type.
@@ -599,7 +667,7 @@ public:
         protozero_assert(tag() != 0 && "call next() before accessing field value");
         protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
         const auto len = get_len_and_skip();
-        return data_view{m_data-len, len};
+        return data_view{m_data - len, len};
     }
 
 #ifndef PROTOZERO_STRICT_API
@@ -615,7 +683,7 @@ public:
         protozero_assert(tag() != 0 && "call next() before accessing field value");
         protozero_assert(has_wire_type(pbf_wire_type::length_delimited) && "not of type string, bytes or message");
         const auto len = get_len_and_skip();
-        return std::make_pair(m_data-len, len);
+        return std::make_pair(m_data - len, len);
     }
 #endif
 
diff --git a/include/protozero/pbf_writer.hpp b/include/protozero/pbf_writer.hpp
index 77cc9d0..39dd795 100644
--- a/include/protozero/pbf_writer.hpp
+++ b/include/protozero/pbf_writer.hpp
@@ -226,8 +226,7 @@ public:
      */
     explicit pbf_writer(std::string& data) noexcept :
         m_data(&data),
-        m_parent_writer(nullptr),
-        m_pos(0) {
+        m_parent_writer(nullptr) {
     }
 
     /**
@@ -236,8 +235,7 @@ public:
      */
     pbf_writer() noexcept :
         m_data(nullptr),
-        m_parent_writer(nullptr),
-        m_pos(0) {
+        m_parent_writer(nullptr) {
     }
 
     /**
@@ -252,8 +250,7 @@ public:
      */
     pbf_writer(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size=0) :
         m_data(parent_writer.m_data),
-        m_parent_writer(&parent_writer),
-        m_pos(0) {
+        m_parent_writer(&parent_writer) {
         m_parent_writer->open_submessage(tag, size);
     }
 
@@ -505,6 +502,37 @@ public:
     }
 
     /**
+     * Add "bytes" field to data using vectored input. All the data in the
+     * 2nd and further arguments is "concatenated" with only a single copy
+     * into the final buffer.
+     *
+     * This will work with objects of any type supporting the data() and
+     * size() methods like std::string or protozero::data_view.
+     *
+     * Example:
+     * @code
+     * std::string data1 = "abc";
+     * std::string data2 = "xyz";
+     * writer.add_bytes_vectored(1, data1, data2);
+     * @endcode
+     *
+     * @tparam Ts List of types supporting data() and size() methods.
+     * @param tag Tag (field number) of the field
+     * @param values List of objects of types Ts with data to be appended.
+     */
+    template <typename... Ts>
+    void add_bytes_vectored(pbf_tag_type tag, Ts&&... values) {
+        protozero_assert(m_pos == 0 && "you can't add fields to a parent pbf_writer if there is an existing pbf_writer for a submessage");
+        protozero_assert(m_data);
+        size_t sum_size = 0;
+        (void)std::initializer_list<size_t>{sum_size += values.size()...};
+        protozero_assert(sum_size <= std::numeric_limits<pbf_length_type>::max());
+        add_length_varint(tag, pbf_length_type(sum_size));
+        m_data->reserve(m_data->size() + sum_size);
+        (void)std::initializer_list<int>{(m_data->append(values.data(), values.size()), 0)...};
+    }
+
+    /**
      * Add "string" field to data.
      *
      * @param tag Tag (field number) of the field
@@ -814,6 +842,12 @@ namespace detail {
 
     public:
 
+        packed_field(const packed_field&) = delete;
+        packed_field& operator=(const packed_field&) = delete;
+
+        packed_field(packed_field&&) = default;
+        packed_field& operator=(packed_field&&) = default;
+
         packed_field(pbf_writer& parent_writer, pbf_tag_type tag) :
             m_writer(parent_writer, tag) {
         }
@@ -833,12 +867,14 @@ namespace detail {
 
     public:
 
-        packed_field_fixed(pbf_writer& parent_writer, pbf_tag_type tag) :
-            packed_field(parent_writer, tag) {
+        template <typename P>
+        packed_field_fixed(pbf_writer& parent_writer, P tag) :
+            packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
         }
 
-        packed_field_fixed(pbf_writer& parent_writer, pbf_tag_type tag, std::size_t size) :
-            packed_field(parent_writer, tag, size * sizeof(T)) {
+        template <typename P>
+        packed_field_fixed(pbf_writer& parent_writer, P tag, std::size_t size) :
+            packed_field(parent_writer, static_cast<pbf_tag_type>(tag), size * sizeof(T)) {
         }
 
         void add_element(T value) {
@@ -852,8 +888,9 @@ namespace detail {
 
     public:
 
-        packed_field_varint(pbf_writer& parent_writer, pbf_tag_type tag) :
-            packed_field(parent_writer, tag) {
+        template <typename P>
+        packed_field_varint(pbf_writer& parent_writer, P tag) :
+            packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
         }
 
         void add_element(T value) {
@@ -867,8 +904,9 @@ namespace detail {
 
     public:
 
-        packed_field_svarint(pbf_writer& parent_writer, pbf_tag_type tag) :
-            packed_field(parent_writer, tag) {
+        template <typename P>
+        packed_field_svarint(pbf_writer& parent_writer, P tag) :
+            packed_field(parent_writer, static_cast<pbf_tag_type>(tag)) {
         }
 
         void add_element(T value) {
diff --git a/include/protozero/types.hpp b/include/protozero/types.hpp
index 431c207..5e14972 100644
--- a/include/protozero/types.hpp
+++ b/include/protozero/types.hpp
@@ -46,6 +46,17 @@ enum class pbf_wire_type : uint32_t {
 };
 
 /**
+ * Get the tag and wire type of the current field in one integer suitable
+ * for comparison with a switch statement.
+ *
+ * See pbf_reader.tag_and_type() for an example how to use this.
+ */
+template <typename T>
+constexpr inline uint32_t tag_and_type(T tag, pbf_wire_type wire_type) noexcept {
+    return (static_cast<uint32_t>(static_cast<pbf_tag_type>(tag)) << 3) | static_cast<uint32_t>(wire_type);
+}
+
+/**
  * The type used for length values, such as the length of a field.
  */
 using pbf_length_type = uint32_t;
@@ -166,7 +177,7 @@ inline void swap(data_view& lhs, data_view& rhs) noexcept {
  * @param lhs First object.
  * @param rhs Second object.
  */
-inline bool operator==(data_view& lhs, data_view& rhs) noexcept {
+inline bool operator==(const data_view& lhs, const data_view& rhs) noexcept {
     return lhs.size() == rhs.size() && !std::strcmp(lhs.data(), rhs.data());
 }
 
@@ -177,7 +188,7 @@ inline bool operator==(data_view& lhs, data_view& rhs) noexcept {
  * @param lhs First object.
  * @param rhs Second object.
  */
-inline bool operator!=(data_view& lhs, data_view& rhs) noexcept {
+inline bool operator!=(const data_view& lhs, const data_view& rhs) noexcept {
     return !(lhs == rhs);
 }
 
diff --git a/include/protozero/varint.hpp b/include/protozero/varint.hpp
index f6142d1..d115d5f 100644
--- a/include/protozero/varint.hpp
+++ b/include/protozero/varint.hpp
@@ -142,7 +142,7 @@ inline void skip_varint(const char** data, const char* end) {
  */
 template <typename T>
 inline int write_varint(T data, uint64_t value) {
-    int n=1;
+    int n = 1;
 
     while (value >= 0x80) {
         *data++ = char((value & 0x7f) | 0x80);
@@ -157,29 +157,29 @@ inline int write_varint(T data, uint64_t value) {
 /**
  * ZigZag encodes a 32 bit integer.
  */
-inline uint32_t encode_zigzag32(int32_t value) noexcept {
+inline constexpr uint32_t encode_zigzag32(int32_t value) noexcept {
     return (static_cast<uint32_t>(value) << 1) ^ (static_cast<uint32_t>(value >> 31));
 }
 
 /**
  * ZigZag encodes a 64 bit integer.
  */
-inline uint64_t encode_zigzag64(int64_t value) noexcept {
+inline constexpr uint64_t encode_zigzag64(int64_t value) noexcept {
     return (static_cast<uint64_t>(value) << 1) ^ (static_cast<uint64_t>(value >> 63));
 }
 
 /**
  * Decodes a 32 bit ZigZag-encoded integer.
  */
-inline int32_t decode_zigzag32(uint32_t value) noexcept {
-    return int32_t(value >> 1) ^ -int32_t(value & 1);
+inline constexpr int32_t decode_zigzag32(uint32_t value) noexcept {
+    return static_cast<int32_t>(value >> 1) ^ -static_cast<int32_t>(value & 1);
 }
 
 /**
  * Decodes a 64 bit ZigZag-encoded integer.
  */
-inline int64_t decode_zigzag64(uint64_t value) noexcept {
-    return int64_t(value >> 1) ^ -int64_t(value & 1);
+inline constexpr int64_t decode_zigzag64(uint64_t value) noexcept {
+    return static_cast<int64_t>(value >> 1) ^ -static_cast<int64_t>(value & 1);
 }
 
 } // end namespace protozero
diff --git a/include/protozero/version.hpp b/include/protozero/version.hpp
index d7cea48..9103bdc 100644
--- a/include/protozero/version.hpp
+++ b/include/protozero/version.hpp
@@ -20,16 +20,15 @@ documentation.
 #define PROTOZERO_VERSION_MAJOR 1
 
 /// The minor version number
-#define PROTOZERO_VERSION_MINOR 4
+#define PROTOZERO_VERSION_MINOR 5
 
 /// The patch number
-#define PROTOZERO_VERSION_PATCH 5
+#define PROTOZERO_VERSION_PATCH 1
 
 /// The complete version number
 #define PROTOZERO_VERSION_CODE (PROTOZERO_VERSION_MAJOR * 10000 + PROTOZERO_VERSION_MINOR * 100 + PROTOZERO_VERSION_PATCH)
 
 /// Version number as string
-#define PROTOZERO_VERSION_STRING "1.4.5"
-
+#define PROTOZERO_VERSION_STRING "1.5.1"
 
 #endif // PROTOZERO_VERSION_HPP
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index ca025c7..db0e934 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -138,10 +138,12 @@ add_unit_test(osm test_way)
 add_unit_test(memory test_buffer_basics)
 add_unit_test(memory test_buffer_node)
 add_unit_test(memory test_buffer_purge)
+add_unit_test(memory test_type_is_compatible)
 
 add_unit_test(builder test_attr)
 add_unit_test(builder test_object_builder)
 
+add_unit_test(geom test_coordinates)
 add_unit_test(geom test_crs ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY})
 add_unit_test(geom test_exception)
 add_unit_test(geom test_factory_with_projection ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY})
@@ -159,6 +161,7 @@ add_unit_test(index test_id_set)
 add_unit_test(index test_id_to_location ENABLE_IF ${SPARSEHASH_FOUND})
 add_unit_test(index test_file_based_index)
 add_unit_test(index test_object_pointer_collection)
+add_unit_test(index test_relations_map)
 
 add_unit_test(io test_compression_factory)
 add_unit_test(io test_bzip2 ENABLE_IF ${BZIP2_FOUND} LIBS ${BZIP2_LIBRARIES})
diff --git a/test/examples/t/amenity_list/CMakeLists.txt b/test/examples/t/amenity_list/CMakeLists.txt
new file mode 100644
index 0000000..c12fc7d
--- /dev/null
+++ b/test/examples/t/amenity_list/CMakeLists.txt
@@ -0,0 +1,7 @@
+
+add_test(NAME examples_amenity_list
+         COMMAND osmium_amenity_list ${CMAKE_CURRENT_SOURCE_DIR}/node.osm)
+
+set_tests_properties(examples_amenity_list PROPERTIES
+                     PASS_REGULAR_EXPRESSION "  8\\.8721, 53\\.0966 post_office")
+
diff --git a/test/examples/t/amenity_list/node.osm b/test/examples/t/amenity_list/node.osm
new file mode 100644
index 0000000..4480711
--- /dev/null
+++ b/test/examples/t/amenity_list/node.osm
@@ -0,0 +1,7 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<osm version="0.6">
+  <node id="24960505" version="5" timestamp="2013-02-28T23:26:39Z" uid="715371" user="cracklinrain" changeset="15203542" lat="53.096629" lon="8.8720536">
+    <tag k="amenity" v="post_office"/>
+    <tag k="wheelchair" v="yes"/>
+  </node>
+</osm>
diff --git a/test/include/catch.hpp b/test/include/catch.hpp
index 2e6fe8d..3d18ead 100644
--- a/test/include/catch.hpp
+++ b/test/include/catch.hpp
@@ -1,6 +1,6 @@
 /*
- *  Catch v1.5.8
- *  Generated: 2016-10-26 12:07:30.938259
+ *  Catch v1.5.9
+ *  Generated: 2016-11-29 12:14:38.049276
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
  *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -3428,6 +3428,7 @@ namespace Catch {
 #include <streambuf>
 #include <ostream>
 #include <fstream>
+#include <memory>
 
 namespace Catch {
 
@@ -3995,9 +3996,12 @@ namespace Clara {
         inline void convertInto( std::string const& _source, std::string& _dest ) {
             _dest = _source;
         }
+        char toLowerCh(char c) {
+            return static_cast<char>( ::tolower( c ) );
+        }
         inline void convertInto( std::string const& _source, bool& _dest ) {
             std::string sourceLC = _source;
-            std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower );
+            std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh );
             if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
                 _dest = true;
             else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
@@ -7578,7 +7582,7 @@ namespace Catch {
         return os;
     }
 
-    Version libraryVersion( 1, 5, 8, "", 0 );
+    Version libraryVersion( 1, 5, 9, "", 0 );
 
 }
 
@@ -9166,6 +9170,7 @@ namespace Catch {
     public:
         XmlReporter( ReporterConfig const& _config )
         :   StreamingReporterBase( _config ),
+            m_xml(_config.stream()),
             m_sectionDepth( 0 )
         {
             m_reporterPrefs.shouldRedirectStdOut = true;
@@ -9185,7 +9190,6 @@ namespace Catch {
 
         virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testRunStarting( testInfo );
-            m_xml.setStream( stream );
             m_xml.startElement( "Catch" );
             if( !m_config->name().empty() )
                 m_xml.writeAttribute( "name", m_config->name() );
diff --git a/test/t/area/test_area_id.cpp b/test/t/area/test_area_id.cpp
index fbd8d78..6918d69 100644
--- a/test/t/area/test_area_id.cpp
+++ b/test/t/area/test_area_id.cpp
@@ -2,24 +2,21 @@
 
 #include <osmium/osm/area.hpp>
 
-TEST_CASE("area_id") {
-
-    SECTION("object_id_to_area_id_conversion") {
-        REQUIRE( 46 == osmium::object_id_to_area_id( 23, osmium::item_type::way));
-        REQUIRE( 47 == osmium::object_id_to_area_id( 23, osmium::item_type::relation));
-        REQUIRE(  0 == osmium::object_id_to_area_id(  0, osmium::item_type::way));
-        REQUIRE(  1 == osmium::object_id_to_area_id(  0, osmium::item_type::relation));
-        REQUIRE(-24 == osmium::object_id_to_area_id(-12, osmium::item_type::way));
-        REQUIRE(-25 == osmium::object_id_to_area_id(-12, osmium::item_type::relation));
-    }
-
-    SECTION("area_id_to_object_id_conversion") {
-        REQUIRE( 23 == osmium::area_id_to_object_id( 46));
-        REQUIRE( 23 == osmium::area_id_to_object_id( 47));
-        REQUIRE(  0 == osmium::area_id_to_object_id(  0));
-        REQUIRE(  0 == osmium::area_id_to_object_id(  1));
-        REQUIRE(-12 == osmium::area_id_to_object_id(-24));
-        REQUIRE(-12 == osmium::area_id_to_object_id(-25));
-    }
+TEST_CASE("object_id to area_id conversion") {
+    REQUIRE( 46 == osmium::object_id_to_area_id( 23, osmium::item_type::way));
+    REQUIRE( 47 == osmium::object_id_to_area_id( 23, osmium::item_type::relation));
+    REQUIRE(  0 == osmium::object_id_to_area_id(  0, osmium::item_type::way));
+    REQUIRE(  1 == osmium::object_id_to_area_id(  0, osmium::item_type::relation));
+    REQUIRE(-24 == osmium::object_id_to_area_id(-12, osmium::item_type::way));
+    REQUIRE(-25 == osmium::object_id_to_area_id(-12, osmium::item_type::relation));
+}
 
+TEST_CASE("area_id to object_id conversion") {
+    REQUIRE( 23 == osmium::area_id_to_object_id( 46));
+    REQUIRE( 23 == osmium::area_id_to_object_id( 47));
+    REQUIRE(  0 == osmium::area_id_to_object_id(  0));
+    REQUIRE(  0 == osmium::area_id_to_object_id(  1));
+    REQUIRE(-12 == osmium::area_id_to_object_id(-24));
+    REQUIRE(-12 == osmium::area_id_to_object_id(-25));
 }
+
diff --git a/test/t/area/test_node_ref_segment.cpp b/test/t/area/test_node_ref_segment.cpp
index 5e33c2b..9eba380 100644
--- a/test/t/area/test_node_ref_segment.cpp
+++ b/test/t/area/test_node_ref_segment.cpp
@@ -4,164 +4,160 @@
 
 using osmium::area::detail::NodeRefSegment;
 
-TEST_CASE("NodeRefSegmentClass") {
+TEST_CASE("Default construction of NodeRefSegment") {
+    NodeRefSegment s;
+    REQUIRE(s.first().ref() == 0);
+    REQUIRE(s.first().location() == osmium::Location());
+    REQUIRE(s.second().ref() == 0);
+    REQUIRE(s.second().location() == osmium::Location());
+}
+
+TEST_CASE("Construction of NodeRefSegment with NodeRefs") {
+    osmium::NodeRef nr1{1, {1.2, 3.4}};
+    osmium::NodeRef nr2{2, {1.4, 3.1}};
+    osmium::NodeRef nr3{3, {1.2, 3.6}};
+    osmium::NodeRef nr4{4, {1.2, 3.7}};
+
+    NodeRefSegment s1{nr1, nr2};
+    REQUIRE(s1.first().ref() == 1);
+    REQUIRE(s1.second().ref() == 2);
+
+    NodeRefSegment s2{nr2, nr3};
+    REQUIRE(s2.first().ref() == 3);
+    REQUIRE(s2.second().ref() == 2);
+
+    NodeRefSegment s3{nr3, nr4};
+    REQUIRE(s3.first().ref() == 3);
+    REQUIRE(s3.second().ref() == 4);
+}
+
+TEST_CASE("Intersection of NodeRefSegments") {
+    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(s5, s1) == osmium::Location(2.0, 2.0));
+
+    REQUIRE(calculate_intersection(s1, s6) == osmium::Location(1.0, 1.0));
+    REQUIRE(calculate_intersection(s6, s1) == osmium::Location(1.0, 1.0));
 
-    SECTION("instantiation_with_default_parameters") {
-        NodeRefSegment s;
-        REQUIRE(s.first().ref() == 0);
-        REQUIRE(s.first().location() == osmium::Location());
-        REQUIRE(s.second().ref() == 0);
-        REQUIRE(s.second().location() == osmium::Location());
-    }
+    REQUIRE(calculate_intersection(s1, s7) == osmium::Location(1.0, 1.0));
+    REQUIRE(calculate_intersection(s7, s1) == osmium::Location(1.0, 1.0));
 
-    SECTION("instantiation") {
-        osmium::NodeRef nr1(1, { 1.2, 3.4 });
-        osmium::NodeRef nr2(2, { 1.4, 3.1 });
-        osmium::NodeRef nr3(3, { 1.2, 3.6 });
-        osmium::NodeRef nr4(4, { 1.2, 3.7 });
+    REQUIRE(calculate_intersection(s6, s7) == osmium::Location());
+    REQUIRE(calculate_intersection(s7, s6) == osmium::Location());
+}
+
+TEST_CASE("Intersection of collinear NodeRefSegments") {
+    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(calculate_intersection(s1, s2) == osmium::Location());
+    REQUIRE(calculate_intersection(s2, s1) == osmium::Location());
+
+    REQUIRE(calculate_intersection(s1, s3) == osmium::Location(1.0, 0.0));
+    REQUIRE(calculate_intersection(s3, s1) == osmium::Location(1.0, 0.0));
+
+    REQUIRE(calculate_intersection(s1, s4) == osmium::Location(1.0, 0.0));
+    REQUIRE(calculate_intersection(s4, s1) == osmium::Location(1.0, 0.0));
+
+    REQUIRE(calculate_intersection(s1, s5) == osmium::Location(1.0, 0.0));
+    REQUIRE(calculate_intersection(s5, s1) == osmium::Location(1.0, 0.0));
 
-        NodeRefSegment s1(nr1, nr2);
-        REQUIRE(s1.first().ref() == 1);
-        REQUIRE(s1.second().ref() == 2);
-
-        NodeRefSegment s2(nr2, nr3);
-        REQUIRE(s2.first().ref() == 3);
-        REQUIRE(s2.second().ref() == 2);
-
-        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}});
-        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(s5, s1) == osmium::Location(2.0, 2.0));
-
-        REQUIRE(calculate_intersection(s1, s6) == osmium::Location(1.0, 1.0));
-        REQUIRE(calculate_intersection(s6, s1) == osmium::Location(1.0, 1.0));
-
-        REQUIRE(calculate_intersection(s1, s7) == osmium::Location(1.0, 1.0));
-        REQUIRE(calculate_intersection(s7, s1) == osmium::Location(1.0, 1.0));
-
-        REQUIRE(calculate_intersection(s6, s7) == osmium::Location());
-        REQUIRE(calculate_intersection(s7, s6) == osmium::Location());
-    }
-
-    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(calculate_intersection(s1, s2) == osmium::Location());
-        REQUIRE(calculate_intersection(s2, s1) == osmium::Location());
-
-        REQUIRE(calculate_intersection(s1, s3) == osmium::Location(1.0, 0.0));
-        REQUIRE(calculate_intersection(s3, s1) == osmium::Location(1.0, 0.0));
-
-        REQUIRE(calculate_intersection(s1, s4) == osmium::Location(1.0, 0.0));
-        REQUIRE(calculate_intersection(s4, s1) == osmium::Location(1.0, 0.0));
-
-        REQUIRE(calculate_intersection(s1, s5) == osmium::Location(1.0, 0.0));
-        REQUIRE(calculate_intersection(s5, s1) == osmium::Location(1.0, 0.0));
-
-        REQUIRE(calculate_intersection(s1, s6) == osmium::Location(2.0, 0.0));
-        REQUIRE(calculate_intersection(s6, s1) == osmium::Location(2.0, 0.0));
-
-        REQUIRE(calculate_intersection(s1, s7) == osmium::Location(2.0, 0.0));
-        REQUIRE(calculate_intersection(s7, s1) == osmium::Location(2.0, 0.0));
-
-        REQUIRE(calculate_intersection(s1, s8) == osmium::Location(1.0, 0.0));
-        REQUIRE(calculate_intersection(s8, s1) == osmium::Location(1.0, 0.0));
-
-        REQUIRE(calculate_intersection(s1, s9) == osmium::Location());
-        REQUIRE(calculate_intersection(s9, s1) == osmium::Location());
-
-        REQUIRE(calculate_intersection(s5, s6) == osmium::Location(1.0, 0.0));
-        REQUIRE(calculate_intersection(s6, s5) == osmium::Location(1.0, 0.0));
-
-        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));
+    REQUIRE(calculate_intersection(s1, s6) == osmium::Location(2.0, 0.0));
+    REQUIRE(calculate_intersection(s6, s1) == osmium::Location(2.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));
-    }
+    REQUIRE(calculate_intersection(s1, s7) == osmium::Location(2.0, 0.0));
+    REQUIRE(calculate_intersection(s7, s1) == osmium::Location(2.0, 0.0));
 
-    SECTION("ordering") {
-        osmium::NodeRef node_ref1(1, { 1.0, 3.0 });
-        osmium::NodeRef node_ref2(2, { 1.4, 2.9 });
-        osmium::NodeRef node_ref3(3, { 1.2, 3.0 });
-        osmium::NodeRef node_ref4(4, { 1.2, 3.3 });
+    REQUIRE(calculate_intersection(s1, s8) == osmium::Location(1.0, 0.0));
+    REQUIRE(calculate_intersection(s8, s1) == osmium::Location(1.0, 0.0));
 
-        REQUIRE(node_ref1 < node_ref2);
-        REQUIRE(node_ref2 < node_ref3);
-        REQUIRE(node_ref1 < node_ref3);
-        REQUIRE(node_ref1 >= node_ref1);
+    REQUIRE(calculate_intersection(s1, s9) == osmium::Location());
+    REQUIRE(calculate_intersection(s9, s1) == osmium::Location());
 
-        REQUIRE( osmium::location_less()(node_ref1, node_ref2));
-        REQUIRE(!osmium::location_less()(node_ref2, node_ref3));
-        REQUIRE( osmium::location_less()(node_ref1, node_ref3));
-        REQUIRE( osmium::location_less()(node_ref3, node_ref4));
-        REQUIRE(!osmium::location_less()(node_ref1, node_ref1));
-    }
+    REQUIRE(calculate_intersection(s5, s6) == osmium::Location(1.0, 0.0));
+    REQUIRE(calculate_intersection(s6, s5) == osmium::Location(1.0, 0.0));
+
+    REQUIRE(calculate_intersection(s7, s8) == osmium::Location(1.0, 0.0));
+    REQUIRE(calculate_intersection(s8, s7) == osmium::Location(1.0, 0.0));
+}
+
+TEST_CASE("Intersection of very long NodeRefSegments") {
+    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));
+}
 
+TEST_CASE("Ordering of NodeRefSegements") {
+    osmium::NodeRef node_ref1{1, {1.0, 3.0}};
+    osmium::NodeRef node_ref2{2, {1.4, 2.9}};
+    osmium::NodeRef node_ref3{3, {1.2, 3.0}};
+    osmium::NodeRef node_ref4{4, {1.2, 3.3}};
+
+    REQUIRE(node_ref1 < node_ref2);
+    REQUIRE(node_ref2 < node_ref3);
+    REQUIRE(node_ref1 < node_ref3);
+    REQUIRE(node_ref1 >= node_ref1);
+
+    REQUIRE( osmium::location_less()(node_ref1, node_ref2));
+    REQUIRE(!osmium::location_less()(node_ref2, node_ref3));
+    REQUIRE( osmium::location_less()(node_ref1, node_ref3));
+    REQUIRE( osmium::location_less()(node_ref3, node_ref4));
+    REQUIRE(!osmium::location_less()(node_ref1, node_ref1));
 }
 
-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);
+TEST_CASE("More 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);
diff --git a/test/t/builder/test_attr.cpp b/test/t/builder/test_attr.cpp
index dedde77..8767817 100644
--- a/test/t/builder/test_attr.cpp
+++ b/test/t/builder/test_attr.cpp
@@ -17,7 +17,7 @@ TEST_CASE("create node using builders") {
 
     using namespace osmium::builder::attr;
 
-    osmium::memory::Buffer buffer(1024*10);
+    osmium::memory::Buffer buffer{1024 * 10};
 
     SECTION("add node with only id") {
         const auto pos = osmium::builder::add_node(buffer, _id(22));
@@ -35,11 +35,11 @@ TEST_CASE("create node using builders") {
     }
 
     SECTION("add node with complete info but no tags") {
-        const auto loc = osmium::Location(3.14, 1.59);
+        const osmium::Location loc{3.14, 1.59};
         const auto pos = osmium::builder::add_node(buffer,
             _id(1),
             _version(17),
-            _timestamp(osmium::Timestamp("2015-01-01T10:20:30Z")),
+            _timestamp(osmium::Timestamp{"2015-01-01T10:20:30Z"}),
             _cid(21),
             _uid(222),
             _location(loc),
@@ -78,7 +78,7 @@ TEST_CASE("create node using builders") {
     }
 
     SECTION("order of attributes doesn't matter") {
-        const auto loc = osmium::Location(3.14, 1.59);
+        const osmium::Location loc{3.14, 1.59};
         const auto pos = osmium::builder::add_node(buffer,
             _timestamp("2015-01-01T10:20:30Z"),
             _version(17),
@@ -224,7 +224,7 @@ TEST_CASE("create way using builders") {
 
     using namespace osmium::builder::attr;
 
-    osmium::memory::Buffer buffer(1024*10);
+    osmium::memory::Buffer buffer{1024 * 10};
 
     SECTION("add way without nodes") {
         const auto pos = osmium::builder::add_way(buffer,
@@ -259,7 +259,7 @@ TEST_CASE("create way with nodes") {
 
     using namespace osmium::builder::attr;
 
-    osmium::memory::Buffer wbuffer(1024*10);
+    osmium::memory::Buffer wbuffer{1024 * 10};
     osmium::builder::add_way(wbuffer,
         _id(1),
         _nodes({1, 2, 4, 8})
@@ -267,7 +267,7 @@ TEST_CASE("create way with nodes") {
 
     const osmium::NodeRefList& nodes = wbuffer.get<osmium::Way>(0).nodes();
 
-    osmium::memory::Buffer buffer(1024*10);
+    osmium::memory::Buffer buffer{1024 * 10};
 
     SECTION("add nodes using an OSM object id or NodeRef") {
         osmium::builder::add_way(buffer,
@@ -380,7 +380,7 @@ TEST_CASE("create relation using builders") {
 
     using namespace osmium::builder::attr;
 
-    osmium::memory::Buffer buffer(1024*10);
+    osmium::memory::Buffer buffer{1024 * 10};
 
     SECTION("create relation") {
         osmium::builder::attr::member_type m{osmium::item_type::way, 113, "inner"};
@@ -540,7 +540,7 @@ TEST_CASE("create area using builders") {
 
     using namespace osmium::builder::attr;
 
-    osmium::memory::Buffer buffer(1024*10);
+    osmium::memory::Buffer buffer{1024 * 10};
 
     SECTION("add area without rings") {
         const auto pos = osmium::builder::add_area(buffer,
diff --git a/test/t/builder/test_object_builder.cpp b/test/t/builder/test_object_builder.cpp
index 5b12564..3110a38 100644
--- a/test/t/builder/test_object_builder.cpp
+++ b/test/t/builder/test_object_builder.cpp
@@ -6,7 +6,7 @@
 #include <osmium/osm.hpp>
 
 TEST_CASE("create objects using builder") {
-    osmium::memory::Buffer buffer{1024*10};
+    osmium::memory::Buffer buffer{1024 * 10};
     std::string user;
 
     SECTION("complete node with tags") {
@@ -68,7 +68,7 @@ TEST_CASE("create objects using builder") {
             user = "123456789012345678";
         }
 
-        osmium::Location loc{1.2, 3.4};
+        const osmium::Location loc{1.2, 3.4};
 
         {
             osmium::builder::NodeBuilder builder{buffer};
@@ -272,8 +272,8 @@ TEST_CASE("create objects using builder") {
     }
 
     SECTION("complete changeset with tags") {
-        osmium::Location bl{-1.2, -3.4};
-        osmium::Location tr{1.2, 3.4};
+        const osmium::Location bl{-1.2, -3.4};
+        const osmium::Location tr{1.2, 3.4};
 
         SECTION("user length 0") {
             user = "";
@@ -365,7 +365,7 @@ TEST_CASE("create objects using builder") {
 }
 
 TEST_CASE("no call to set_user on node") {
-    osmium::memory::Buffer buffer{1024*10};
+    osmium::memory::Buffer buffer{1024 * 10};
 
     {
         osmium::builder::NodeBuilder builder{buffer};
@@ -377,7 +377,7 @@ TEST_CASE("no call to set_user on node") {
 }
 
 TEST_CASE("set_user with length on node") {
-    osmium::memory::Buffer buffer{1024*10};
+    osmium::memory::Buffer buffer{1024 * 10};
     std::string user = "userx";
 
     {
@@ -391,7 +391,7 @@ TEST_CASE("set_user with length on node") {
 }
 
 TEST_CASE("no call to set_user on way") {
-    osmium::memory::Buffer buffer{1024*10};
+    osmium::memory::Buffer buffer{1024 * 10};
 
     {
         osmium::builder::WayBuilder builder{buffer};
@@ -403,7 +403,7 @@ TEST_CASE("no call to set_user on way") {
 }
 
 TEST_CASE("set_user with length on way") {
-    osmium::memory::Buffer buffer{1024*10};
+    osmium::memory::Buffer buffer{1024 * 10};
     std::string user = "userx";
 
     {
@@ -417,7 +417,7 @@ TEST_CASE("set_user with length on way") {
 }
 
 TEST_CASE("no call to set_user on changeset") {
-    osmium::memory::Buffer buffer{1024*10};
+    osmium::memory::Buffer buffer{1024 * 10};
 
     {
         osmium::builder::ChangesetBuilder builder{buffer};
@@ -429,7 +429,7 @@ TEST_CASE("no call to set_user on changeset") {
 }
 
 TEST_CASE("set_user with length on changeset") {
-    osmium::memory::Buffer buffer{1024*10};
+    osmium::memory::Buffer buffer{1024 * 10};
     std::string user = "userx";
 
     {
diff --git a/test/t/geom/test_coordinates.cpp b/test/t/geom/test_coordinates.cpp
new file mode 100644
index 0000000..d876969
--- /dev/null
+++ b/test/t/geom/test_coordinates.cpp
@@ -0,0 +1,71 @@
+#include "catch.hpp"
+
+#include <osmium/geom/coordinates.hpp>
+
+TEST_CASE("Default constructed coordinates are invalid") {
+    const osmium::geom::Coordinates c;
+    REQUIRE_FALSE(c.valid());
+}
+
+TEST_CASE("Coordinates constructed from doubles are valid") {
+    const osmium::geom::Coordinates c{1.2, 3.4};
+    REQUIRE(c.valid());
+    REQUIRE(c.x == Approx(1.2));
+    REQUIRE(c.y == Approx(3.4));
+}
+
+TEST_CASE("Coordinates constructed from a location are valid") {
+    const osmium::Location loc{1.2, 3.4};
+    const osmium::geom::Coordinates c{loc};
+    REQUIRE(c.valid());
+    REQUIRE(c.x == Approx(1.2));
+    REQUIRE(c.y == Approx(3.4));
+}
+
+TEST_CASE("Comparing coordinates") {
+    const osmium::geom::Coordinates ci1;
+    const osmium::geom::Coordinates ci2;
+    const osmium::geom::Coordinates cv1{1.2, 3.4};
+    const osmium::geom::Coordinates cv2{1.2, 3.4};
+    const osmium::geom::Coordinates cv3{2.1, 4.3};
+    REQUIRE(ci1 == ci2);
+    REQUIRE_FALSE(ci1 == cv1);
+    REQUIRE(cv1 == cv2);
+    REQUIRE_FALSE(cv1 == cv3);
+}
+
+TEST_CASE("Write coordinates to string") {
+    const osmium::geom::Coordinates c{0.1234567, 1.89898989};
+    std::string out;
+
+    SECTION("precision 7") {
+        c.append_to_string(out, ',', 7);
+        REQUIRE(out == "0.1234567,1.8989899");
+    }
+
+    SECTION("precision 3") {
+        c.append_to_string(out, ',', 3);
+        REQUIRE(out == "0.123,1.899");
+    }
+
+    SECTION("with prefix and suffix") {
+        c.append_to_string(out, '(', ',', ')', 3);
+        REQUIRE(out == "(0.123,1.899)");
+    }
+}
+
+TEST_CASE("Write invalid coordinates to string") {
+    const osmium::geom::Coordinates c;
+    std::string out;
+
+    SECTION("with infix only") {
+        c.append_to_string(out, ',', 7);
+        REQUIRE(out == "invalid");
+    }
+
+    SECTION("with prefix and suffix") {
+        c.append_to_string(out, '(', ',', ')', 3);
+        REQUIRE(out == "(invalid)");
+    }
+}
+
diff --git a/test/t/geom/test_crs.cpp b/test/t/geom/test_crs.cpp
index d8f7c36..5bd7d4f 100644
--- a/test/t/geom/test_crs.cpp
+++ b/test/t/geom/test_crs.cpp
@@ -1,7 +1,5 @@
 #include "catch.hpp"
 
-#include <random>
-
 #include <osmium/geom/projection.hpp>
 
 TEST_CASE("CRS") {
diff --git a/test/t/geom/test_tile.cpp b/test/t/geom/test_tile.cpp
index 953fcc7..4218797 100644
--- a/test/t/geom/test_tile.cpp
+++ b/test/t/geom/test_tile.cpp
@@ -6,6 +6,14 @@
 
 #include "test_tile_data.hpp"
 
+TEST_CASE("Helper functions") {
+    REQUIRE(osmium::geom::num_tiles_in_zoom(0) == 1);
+    REQUIRE(osmium::geom::num_tiles_in_zoom(1) == 2);
+    REQUIRE(osmium::geom::num_tiles_in_zoom(12) == 4096);
+
+    REQUIRE(osmium::geom::tile_extent_in_zoom(1) == osmium::geom::detail::max_coordinate_epsg3857);
+}
+
 TEST_CASE("Tile from x0.0 y0.0 at zoom 0") {
     osmium::Location l{0.0, 0.0};
 
@@ -61,6 +69,14 @@ TEST_CASE("Tile from max values at zoom 30") {
     REQUIRE(t.valid());
 }
 
+TEST_CASE("Tile from coordinates") {
+    osmium::geom::Coordinates c{9.99312, 53.55078};
+    osmium::geom::Tile t{12, osmium::geom::lonlat_to_mercator(c)};
+    REQUIRE(t.valid());
+    REQUIRE(t.x == 2161);
+    REQUIRE(t.y == 1323);
+}
+
 TEST_CASE("Tile equality") {
     osmium::geom::Tile a{4, 3, 4};
     osmium::geom::Tile b{4, 3, 4};
diff --git a/test/t/geom/test_wkb.cpp b/test/t/geom/test_wkb.cpp
index e46c5de..29b6d6b 100644
--- a/test/t/geom/test_wkb.cpp
+++ b/test/t/geom/test_wkb.cpp
@@ -18,26 +18,28 @@ TEST_CASE("WKB geometry factory (byte-order-dependant), points") {
         REQUIRE(wkb == "01010000009A99999999990940CDCCCCCCCCCC1040");
     }
 
-    SECTION("point in web mercator") {
-        osmium::geom::WKBFactory<osmium::geom::MercatorProjection> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex};
+    SECTION("point in ewkb") {
+        osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex};
 
         const std::string wkb{factory.create_point(loc)};
-        REQUIRE(wkb == "010100000028706E7BF9BD1541B03E0D93E48F1C41");
+        REQUIRE(wkb == "0101000020E61000009A99999999990940CDCCCCCCCCCC1040");
     }
 
-    SECTION("point in ewkb") {
-        osmium::geom::WKBFactory<> factory{osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex};
+#ifndef OSMIUM_USE_SLOW_MERCATOR_PROJECTION
+    SECTION("point in web mercator") {
+        osmium::geom::WKBFactory<osmium::geom::MercatorProjection> factory{osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex};
 
         const std::string wkb{factory.create_point(loc)};
-        REQUIRE(wkb == "0101000020E61000009A99999999990940CDCCCCCCCCCC1040");
+        REQUIRE(wkb == "010100000028706E7BF9BD1541D6A90093E48F1C41");
     }
 
     SECTION("point in ewkb in web mercator") {
         osmium::geom::WKBFactory<osmium::geom::MercatorProjection> factory{osmium::geom::wkb_type::ewkb, osmium::geom::out_type::hex};
 
         const std::string wkb{factory.create_point(loc)};
-        REQUIRE(wkb == "0101000020110F000028706E7BF9BD1541B03E0D93E48F1C41");
+        REQUIRE(wkb == "0101000020110F000028706E7BF9BD1541D6A90093E48F1C41");
     }
+#endif
 
 }
 
diff --git a/test/t/index/test_id_to_location.cpp b/test/t/index/test_id_to_location.cpp
index 36fc074..9e38d12 100644
--- a/test/t/index/test_id_to_location.cpp
+++ b/test/t/index/test_id_to_location.cpp
@@ -37,6 +37,11 @@ void test_func_all(TIndex& index) {
     REQUIRE_THROWS_AS(index.get(100), osmium::not_found);
     REQUIRE_THROWS_WITH(index.get(0), "id 0 not found");
     REQUIRE_THROWS_WITH(index.get(1), "id 1 not found");
+
+    REQUIRE(index.get_noexcept(0) == osmium::Location{});
+    REQUIRE(index.get_noexcept(1) == osmium::Location{});
+    REQUIRE(index.get_noexcept(5) == osmium::Location{});
+    REQUIRE(index.get_noexcept(100) == osmium::Location{});
 }
 
 template <typename TIndex>
@@ -54,11 +59,19 @@ void test_func_real(TIndex& index) {
     REQUIRE(loc1 == index.get(id1));
     REQUIRE(loc2 == index.get(id2));
 
+    REQUIRE(loc1 == index.get_noexcept(id1));
+    REQUIRE(loc2 == index.get_noexcept(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);
 
+    REQUIRE(index.get_noexcept(0) == osmium::Location{});
+    REQUIRE(index.get_noexcept(1) == osmium::Location{});
+    REQUIRE(index.get_noexcept(5) == osmium::Location{});
+    REQUIRE(index.get_noexcept(100) == osmium::Location{});
+
     index.clear();
 
     REQUIRE_THROWS_AS(index.get(id1), osmium::not_found);
@@ -68,6 +81,13 @@ void test_func_real(TIndex& index) {
     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);
+
+    REQUIRE(index.get_noexcept(id1) == osmium::Location{});
+    REQUIRE(index.get_noexcept(id2) == osmium::Location{});
+    REQUIRE(index.get_noexcept(0) == osmium::Location{});
+    REQUIRE(index.get_noexcept(1) == osmium::Location{});
+    REQUIRE(index.get_noexcept(5) == osmium::Location{});
+    REQUIRE(index.get_noexcept(100) == osmium::Location{});
 }
 
 TEST_CASE("Map Id to location: Dummy") {
diff --git a/test/t/index/test_relations_map.cpp b/test/t/index/test_relations_map.cpp
new file mode 100644
index 0000000..8ef0755
--- /dev/null
+++ b/test/t/index/test_relations_map.cpp
@@ -0,0 +1,50 @@
+
+#include "catch.hpp"
+
+#include <type_traits>
+
+#include <osmium/index/relations_map.hpp>
+
+static_assert(std::is_default_constructible<osmium::index::RelationsMapIndex>::value == false, "RelationsMapIndex should not be default constructible");
+static_assert(std::is_copy_constructible<osmium::index::RelationsMapIndex>::value == false, "RelationsMapIndex should not be copy constructible");
+static_assert(std::is_copy_constructible<osmium::index::RelationsMapStash>::value == false, "RelationsMapStash should not be copy constructible");
+static_assert(std::is_copy_assignable<osmium::index::RelationsMapIndex>::value == false, "RelationsMapIndex should not be copy assignable");
+static_assert(std::is_copy_assignable<osmium::index::RelationsMapStash>::value == false, "RelationsMapStash should not be copy assignable");
+
+TEST_CASE("RelationsMapStash lvalue") {
+    osmium::index::RelationsMapStash stash;
+    REQUIRE(stash.empty());
+    REQUIRE(stash.size() == 0);
+
+    stash.add(1, 2);
+    stash.add(2, 3);
+    REQUIRE_FALSE(stash.empty());
+    REQUIRE(stash.size() == 2);
+
+    auto index= stash.build_index();
+
+    REQUIRE_FALSE(index.empty());
+    REQUIRE(index.size() == 2);
+
+    index.for_each_parent(1, [](osmium::unsigned_object_id_type id) {
+        REQUIRE(id == 2);
+    });
+}
+
+osmium::index::RelationsMapIndex func() {
+    osmium::index::RelationsMapStash stash;
+
+    stash.add(1, 2);
+    stash.add(2, 3);
+
+    return stash.build_index();
+}
+
+TEST_CASE("RelationsMapStash rvalue") {
+    const osmium::index::RelationsMapIndex index{func()};
+
+    index.for_each_parent(1, [](osmium::unsigned_object_id_type id) {
+        REQUIRE(id == 2);
+    });
+}
+
diff --git a/test/t/io/test_bzip2.cpp b/test/t/io/test_bzip2.cpp
index 5cc30b4..2d1c0f3 100644
--- a/test/t/io/test_bzip2.cpp
+++ b/test/t/io/test_bzip2.cpp
@@ -7,27 +7,23 @@
 
 #include <osmium/io/bzip2_compression.hpp>
 
-TEST_CASE("Bzip2") {
-
-    SECTION("read_compressed_file") {
-        std::string input_file = with_data_dir("t/io/data_bzip2.txt.bz2");
-
-        int fd = ::open(input_file.c_str(), O_RDONLY);
-        REQUIRE(fd > 0);
-
-        size_t size = 0;
-        std::string all;
-        {
-            osmium::io::Bzip2Decompressor decomp(fd);
-            for (std::string data = decomp.read(); !data.empty(); data = decomp.read()) {
-                size += data.size();
-                all += data;
-            }
+TEST_CASE("Read bzip2-compressed file") {
+    std::string input_file = with_data_dir("t/io/data_bzip2.txt.bz2");
+
+    const int fd = ::open(input_file.c_str(), O_RDONLY);
+    REQUIRE(fd > 0);
+
+    size_t size = 0;
+    std::string all;
+    {
+        osmium::io::Bzip2Decompressor decomp{fd};
+        for (std::string data = decomp.read(); !data.empty(); data = decomp.read()) {
+            size += data.size();
+            all += data;
         }
-
-        REQUIRE(9 == size);
-        REQUIRE("TESTDATA\n" == all);
     }
 
+    REQUIRE(9 == size);
+    REQUIRE("TESTDATA\n" == all);
 }
 
diff --git a/test/t/io/test_file_formats.cpp b/test/t/io/test_file_formats.cpp
index f0ba0c6..356e3d1 100644
--- a/test/t/io/test_file_formats.cpp
+++ b/test/t/io/test_file_formats.cpp
@@ -4,272 +4,268 @@
 
 #include <osmium/io/file.hpp>
 
-TEST_CASE("FileFormats") {
-
-    SECTION("default_file_format") {
-        osmium::io::File f;
-        REQUIRE(osmium::io::file_format::unknown == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        REQUIRE_THROWS_AS(f.check(), std::runtime_error);
-    }
-
-    SECTION("stdin_stdout_empty") {
-        osmium::io::File f {""};
-        REQUIRE(osmium::io::file_format::unknown == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        REQUIRE_THROWS_AS(f.check(), std::runtime_error);
-    }
-
-    SECTION("stdin_stdout_dash") {
-        osmium::io::File f {"-"};
-        REQUIRE(osmium::io::file_format::unknown == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        REQUIRE_THROWS_AS(f.check(), std::runtime_error);
-    }
-
-    SECTION("stdin_stdout_bz2") {
-        osmium::io::File f {"-", "osm.bz2"};
-        REQUIRE("" == f.filename());
-        REQUIRE(osmium::io::file_format::xml == f.format());
-        REQUIRE(osmium::io::file_compression::bzip2 == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("detect_file_format_by_suffix_osm") {
-        osmium::io::File f {"test.osm"};
-        REQUIRE(osmium::io::file_format::xml == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("detect_file_format_by_suffix_pbf") {
-        osmium::io::File f {"test.pbf"};
-        REQUIRE(osmium::io::file_format::pbf == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("detect_file_format_by_suffix_osm_pbf") {
-        osmium::io::File f {"test.osm.pbf"};
-        REQUIRE(osmium::io::file_format::pbf == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("detect_file_format_by_suffix_opl") {
-        osmium::io::File f {"test.opl"};
-        REQUIRE(osmium::io::file_format::opl == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("detect_file_format_by_suffix_osm_opl") {
-        osmium::io::File f {"test.osm.opl"};
-        REQUIRE(osmium::io::file_format::opl == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("detect_file_format_by_suffix_osm_gz") {
-        osmium::io::File f {"test.osm.gz"};
-        REQUIRE(osmium::io::file_format::xml == f.format());
-        REQUIRE(osmium::io::file_compression::gzip == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("detect_file_format_by_suffix_opl_bz2") {
-        osmium::io::File f {"test.osm.opl.bz2"};
-        REQUIRE(osmium::io::file_format::opl == f.format());
-        REQUIRE(osmium::io::file_compression::bzip2 == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("detect_file_format_by_suffix_osc_gz") {
-        osmium::io::File f {"test.osc.gz"};
-        REQUIRE(osmium::io::file_format::xml == f.format());
-        REQUIRE(osmium::io::file_compression::gzip == f.compression());
-        REQUIRE(true == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("detect_file_format_by_suffix_opl_gz") {
-        osmium::io::File f {"test.osh.opl.gz"};
-        REQUIRE(osmium::io::file_format::opl == f.format());
-        REQUIRE(osmium::io::file_compression::gzip == f.compression());
-        REQUIRE(true == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("detect_file_format_by_suffix_osh_pbf") {
-        osmium::io::File f {"test.osh.pbf"};
-        REQUIRE(osmium::io::file_format::pbf == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(true == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("override_file_format_by_suffix_osm") {
-        osmium::io::File f {"test", "osm"};
-        REQUIRE(osmium::io::file_format::xml == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("override_file_format_by_suffix_pbf") {
-        osmium::io::File f {"test", "pbf"};
-        REQUIRE(osmium::io::file_format::pbf == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("override_file_format_by_suffix_osm_pbf") {
-        osmium::io::File f {"test", "osm.pbf"};
-        REQUIRE(osmium::io::file_format::pbf == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("override_file_format_by_suffix_opl") {
-        osmium::io::File f {"test", "opl"};
-        REQUIRE(osmium::io::file_format::opl == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("override_file_format_by_suffix_osm_opl") {
-        osmium::io::File f {"test", "osm.opl"};
-        REQUIRE(osmium::io::file_format::opl == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("override_file_format_by_suffix_osm_gz") {
-        osmium::io::File f {"test", "osm.gz"};
-        REQUIRE(osmium::io::file_format::xml == f.format());
-        REQUIRE(osmium::io::file_compression::gzip == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("override_file_format_by_suffix_osm_opl_bz2") {
-        osmium::io::File f {"test", "osm.opl.bz2"};
-        REQUIRE(osmium::io::file_format::opl == f.format());
-        REQUIRE(osmium::io::file_compression::bzip2 == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("override_file_format_by_suffix_osc_gz") {
-        osmium::io::File f {"test", "osc.gz"};
-        REQUIRE(osmium::io::file_format::xml == f.format());
-        REQUIRE(osmium::io::file_compression::gzip == f.compression());
-        REQUIRE(true == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("override_file_format_by_suffix_osh_opl_gz") {
-        osmium::io::File f {"test", "osh.opl.gz"};
-        REQUIRE(osmium::io::file_format::opl == f.format());
-        REQUIRE(osmium::io::file_compression::gzip == f.compression());
-        REQUIRE(true == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("override_file_format_by_suffix_osh_pbf") {
-        osmium::io::File f {"test", "osh.pbf"};
-        REQUIRE(osmium::io::file_format::pbf == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(true == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("format_options_pbf_history") {
-        osmium::io::File f {"test", "pbf,history=true"};
-        REQUIRE(osmium::io::file_format::pbf == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(true == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("format_options_pbf_foo") {
-        osmium::io::File f {"test.osm", "pbf,foo=bar"};
-        REQUIRE(osmium::io::file_format::pbf == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE("bar" == f.get("foo"));
-        f.check();
-    }
-
-    SECTION("format_options_xml_abc_something") {
-        osmium::io::File f {"test.bla", "xml,abc,some=thing"};
-        REQUIRE(osmium::io::file_format::xml == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE("true" == f.get("abc"));
-        REQUIRE("thing" == f.get("some"));
-        REQUIRE(2 == std::distance(f.begin(), f.end()));
-        f.check();
-    }
-
-    SECTION("unknown_format_foo_bar") {
-        osmium::io::File f {"test.foo.bar"};
-        REQUIRE(osmium::io::file_format::unknown == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE_THROWS_AS(f.check(), std::runtime_error);
-    }
-
-    SECTION("unknown_format_foo") {
-        osmium::io::File f {"test", "foo"};
-        REQUIRE_THROWS_AS(f.check(), std::runtime_error);
-    }
-
-    SECTION("unknown_format_osm_foo") {
-        osmium::io::File f {"test", "osm.foo"};
-        REQUIRE_THROWS_AS(f.check(), std::runtime_error);
-    }
-
-    SECTION("unknown_format_bla_equals_foo") {
-        osmium::io::File f {"test", "bla=foo"};
-        REQUIRE_THROWS_AS(f.check(), std::runtime_error);
-    }
-
-    SECTION("url without format") {
-        osmium::io::File f {"http://www.example.com/api"};
-        REQUIRE(osmium::io::file_format::xml == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("url without format and filename") {
-        osmium::io::File f {"http://planet.osm.org/pbf/planet-latest.osm.pbf"};
-        REQUIRE(osmium::io::file_format::pbf == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(false == f.has_multiple_object_versions());
-        f.check();
-    }
-
-    SECTION("url with format") {
-        osmium::io::File f {"http://www.example.com/api", "osh"};
-        REQUIRE(osmium::io::file_format::xml == f.format());
-        REQUIRE(osmium::io::file_compression::none == f.compression());
-        REQUIRE(true == f.has_multiple_object_versions());
-        f.check();
-    }
+TEST_CASE("Default file format") {
+    osmium::io::File f;
+    REQUIRE(osmium::io::file_format::unknown == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+TEST_CASE("File format when empty (stdin/stdout)") {
+    osmium::io::File f{""};
+    REQUIRE(osmium::io::file_format::unknown == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+TEST_CASE("File format from dash (stdin/stdout)") {
+    osmium::io::File f{"-"};
+    REQUIRE(osmium::io::file_format::unknown == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+TEST_CASE("File format from dash with osm.bz2") {
+    osmium::io::File f{"-", "osm.bz2"};
+    REQUIRE("" == f.filename());
+    REQUIRE(osmium::io::file_format::xml == f.format());
+    REQUIRE(osmium::io::file_compression::bzip2 == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Detect file format by suffix 'osm'") {
+    osmium::io::File f{"test.osm"};
+    REQUIRE(osmium::io::file_format::xml == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Detect file format by suffix 'pbf'") {
+    osmium::io::File f{"test.pbf"};
+    REQUIRE(osmium::io::file_format::pbf == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Detect file format by suffix 'osm.pbf'") {
+    osmium::io::File f{"test.osm.pbf"};
+    REQUIRE(osmium::io::file_format::pbf == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Detect file format by suffix 'opl'") {
+    osmium::io::File f{"test.opl"};
+    REQUIRE(osmium::io::file_format::opl == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Detect file format by suffix 'osm.opl'") {
+    osmium::io::File f{"test.osm.opl"};
+    REQUIRE(osmium::io::file_format::opl == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Detect file format by suffix 'osm.gz'") {
+    osmium::io::File f{"test.osm.gz"};
+    REQUIRE(osmium::io::file_format::xml == f.format());
+    REQUIRE(osmium::io::file_compression::gzip == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Detect file format by suffix 'opl.bz2'") {
+    osmium::io::File f{"test.osm.opl.bz2"};
+    REQUIRE(osmium::io::file_format::opl == f.format());
+    REQUIRE(osmium::io::file_compression::bzip2 == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Detect file format by suffix 'osc.gz'") {
+    osmium::io::File f{"test.osc.gz"};
+    REQUIRE(osmium::io::file_format::xml == f.format());
+    REQUIRE(osmium::io::file_compression::gzip == f.compression());
+    REQUIRE(true == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Detect file format by suffix 'opl.gz'") {
+    osmium::io::File f{"test.osh.opl.gz"};
+    REQUIRE(osmium::io::file_format::opl == f.format());
+    REQUIRE(osmium::io::file_compression::gzip == f.compression());
+    REQUIRE(true == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Detect file format by suffix 'osh.pbf'") {
+    osmium::io::File f{"test.osh.pbf"};
+    REQUIRE(osmium::io::file_format::pbf == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(true == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'osm'") {
+    osmium::io::File f{"test", "osm"};
+    REQUIRE(osmium::io::file_format::xml == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'pbf'") {
+    osmium::io::File f{"test", "pbf"};
+    REQUIRE(osmium::io::file_format::pbf == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'osm.pbf'") {
+    osmium::io::File f{"test", "osm.pbf"};
+    REQUIRE(osmium::io::file_format::pbf == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'opl'") {
+    osmium::io::File f{"test", "opl"};
+    REQUIRE(osmium::io::file_format::opl == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'osm.opl'") {
+    osmium::io::File f{"test", "osm.opl"};
+    REQUIRE(osmium::io::file_format::opl == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'osm.gz'") {
+    osmium::io::File f{"test", "osm.gz"};
+    REQUIRE(osmium::io::file_format::xml == f.format());
+    REQUIRE(osmium::io::file_compression::gzip == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'osm.opl.bz2'") {
+    osmium::io::File f{"test", "osm.opl.bz2"};
+    REQUIRE(osmium::io::file_format::opl == f.format());
+    REQUIRE(osmium::io::file_compression::bzip2 == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'osc.gz'") {
+    osmium::io::File f{"test", "osc.gz"};
+    REQUIRE(osmium::io::file_format::xml == f.format());
+    REQUIRE(osmium::io::file_compression::gzip == f.compression());
+    REQUIRE(true == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'osh.opl.gz'") {
+    osmium::io::File f{"test", "osh.opl.gz"};
+    REQUIRE(osmium::io::file_format::opl == f.format());
+    REQUIRE(osmium::io::file_compression::gzip == f.compression());
+    REQUIRE(true == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Override file format by suffix 'osh.pbf'") {
+    osmium::io::File f{"test", "osh.pbf"};
+    REQUIRE(osmium::io::file_format::pbf == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(true == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Format option pbf history") {
+    osmium::io::File f{"test", "pbf,history=true"};
+    REQUIRE(osmium::io::file_format::pbf == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(true == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("Format option pbf foo") {
+    osmium::io::File f{"test.osm", "pbf,foo=bar"};
+    REQUIRE(osmium::io::file_format::pbf == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE("bar" == f.get("foo"));
+    f.check();
+}
+
+TEST_CASE("Format option xml abc something") {
+    osmium::io::File f{"test.bla", "xml,abc,some=thing"};
+    REQUIRE(osmium::io::file_format::xml == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE("true" == f.get("abc"));
+    REQUIRE("thing" == f.get("some"));
+    REQUIRE(2 == std::distance(f.begin(), f.end()));
+    f.check();
+}
+
+TEST_CASE("Unknown format 'foo.bar'") {
+    osmium::io::File f{"test.foo.bar"};
+    REQUIRE(osmium::io::file_format::unknown == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+TEST_CASE("Unknown format 'foo'") {
+    osmium::io::File f{"test", "foo"};
+    REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+TEST_CASE("Unknown format 'osm.foo'") {
+    osmium::io::File f{"test", "osm.foo"};
+    REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+TEST_CASE("Unknown format 'bla=foo'") {
+    osmium::io::File f{"test", "bla=foo"};
+    REQUIRE_THROWS_AS(f.check(), std::runtime_error);
+}
+
+TEST_CASE("URL without format") {
+    osmium::io::File f{"http://www.example.com/api"};
+    REQUIRE(osmium::io::file_format::xml == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
+
+TEST_CASE("URL without format and filename") {
+    osmium::io::File f{"http://planet.osm.org/pbf/planet-latest.osm.pbf"};
+    REQUIRE(osmium::io::file_format::pbf == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(false == f.has_multiple_object_versions());
+    f.check();
+}
 
+TEST_CASE("URL with format") {
+    osmium::io::File f{"http://www.example.com/api", "osh"};
+    REQUIRE(osmium::io::file_format::xml == f.format());
+    REQUIRE(osmium::io::file_compression::none == f.compression());
+    REQUIRE(true == f.has_multiple_object_versions());
+    f.check();
 }
 
diff --git a/test/t/io/test_output_iterator.cpp b/test/t/io/test_output_iterator.cpp
index 7a1f570..f554da2 100644
--- a/test/t/io/test_output_iterator.cpp
+++ b/test/t/io/test_output_iterator.cpp
@@ -6,29 +6,28 @@
 
 TEST_CASE("output iterator") {
 
+    osmium::io::Header header;
+
     SECTION("should be copy constructable") {
-        osmium::io::Header header;
-        osmium::io::Writer writer("test.osm", header, osmium::io::overwrite::allow);
-        osmium::io::OutputIterator<osmium::io::Writer> out1(writer);
+        osmium::io::Writer writer{"test.osm", header, osmium::io::overwrite::allow};
+        osmium::io::OutputIterator<osmium::io::Writer> out1{writer};
 
-        osmium::io::OutputIterator<osmium::io::Writer> out2(out1);
+        osmium::io::OutputIterator<osmium::io::Writer> out2{out1};
     }
 
     SECTION("should be copy assignable") {
-        osmium::io::Header header;
-        osmium::io::Writer writer1("test1.osm", header, osmium::io::overwrite::allow);
-        osmium::io::Writer writer2("test2.osm", header, osmium::io::overwrite::allow);
+        osmium::io::Writer writer1{"test1.osm", header, osmium::io::overwrite::allow};
+        osmium::io::Writer writer2{"test2.osm", header, osmium::io::overwrite::allow};
 
-        osmium::io::OutputIterator<osmium::io::Writer> out1(writer1);
-        osmium::io::OutputIterator<osmium::io::Writer> out2(writer2);
+        osmium::io::OutputIterator<osmium::io::Writer> out1{writer1};
+        osmium::io::OutputIterator<osmium::io::Writer> out2{writer2};
 
         out2 = out1;
     }
 
     SECTION("should be incrementable") {
-        osmium::io::Header header;
-        osmium::io::Writer writer("test.osm", header, osmium::io::overwrite::allow);
-        osmium::io::OutputIterator<osmium::io::Writer> out(writer);
+        osmium::io::Writer writer{"test.osm", header, osmium::io::overwrite::allow};
+        osmium::io::OutputIterator<osmium::io::Writer> out{writer};
 
         ++out;
     }
diff --git a/test/t/io/test_reader.cpp b/test/t/io/test_reader.cpp
index a83af52..c95c76b 100644
--- a/test/t/io/test_reader.cpp
+++ b/test/t/io/test_reader.cpp
@@ -12,7 +12,7 @@ struct CountHandler : public osmium::handler::Handler {
 
     int count = 0;
 
-    void node(osmium::Node&) {
+    void node(const osmium::Node&) {
         ++count;
     }
 
@@ -24,13 +24,13 @@ struct ZeroPositionNodeCountHandler : public osmium::handler::Handler {
     // location.
     int count = 0;
     int total_count = 0; // total number of nodes seen
-    const osmium::Location zero = osmium::Location(int32_t(0), int32_t(0));
+    const osmium::Location zero = osmium::Location{int32_t(0), int32_t(0)};
 
-    void node(osmium::Node &n) {
+    void node(const osmium::Node& node) {
         // no nodes in the history file have a zero location, and
         // no visible nodes should have an undefined location.
-        if ((n.location() == zero) ||
-            (n.visible() && !n.location())) {
+        if ((node.location() == zero) ||
+            (node.visible() && !node.location())) {
             ++count;
         }
         ++total_count;
@@ -39,174 +39,170 @@ struct ZeroPositionNodeCountHandler : public osmium::handler::Handler {
 }; // class ZeroPositionNodeCountHandler
 
 
-TEST_CASE("Reader") {
+TEST_CASE("Reader can be initialized with file") {
+    osmium::io::File file{with_data_dir("t/io/data.osm")};
+    osmium::io::Reader reader{file};
+    osmium::handler::Handler handler;
 
-    SECTION("reader can be initialized with file") {
-        osmium::io::File file(with_data_dir("t/io/data.osm"));
-        osmium::io::Reader reader(file);
-        osmium::handler::Handler handler;
-
-        osmium::apply(reader, handler);
-    }
+    osmium::apply(reader, handler);
+}
 
-    SECTION("reader can be initialized with string") {
-        osmium::io::Reader reader(with_data_dir("t/io/data.osm"));
-        osmium::handler::Handler handler;
+TEST_CASE("Reader can be initialized with string") {
+    osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
+    osmium::handler::Handler handler;
 
-        osmium::apply(reader, handler);
-    }
+    osmium::apply(reader, handler);
+}
 
-    SECTION("should throw after eof") {
-        osmium::io::File file(with_data_dir("t/io/data.osm"));
-        osmium::io::Reader reader(file);
+TEST_CASE("Reader should throw after eof") {
+    osmium::io::File file{with_data_dir("t/io/data.osm")};
+    osmium::io::Reader reader{file};
 
-        REQUIRE(!reader.eof());
+    REQUIRE(!reader.eof());
 
-        while (osmium::memory::Buffer buffer = reader.read()) {
-        }
+    while (osmium::memory::Buffer buffer = reader.read()) {
+    }
 
-        REQUIRE(reader.eof());
+    REQUIRE(reader.eof());
 
-        REQUIRE_THROWS_AS({
-            reader.read();
-        }, osmium::io_error);
-    }
+    REQUIRE_THROWS_AS({
+        reader.read();
+    }, osmium::io_error);
+}
 
-    SECTION("should not hang when apply() is called twice on reader") {
-        osmium::io::File file(with_data_dir("t/io/data.osm"));
-        osmium::io::Reader reader(file);
-        osmium::handler::Handler handler;
+TEST_CASE("Reader should not hang when apply() is called twice on reader") {
+    osmium::io::File file{with_data_dir("t/io/data.osm")};
+    osmium::io::Reader reader{file};
+    osmium::handler::Handler handler;
 
+    osmium::apply(reader, handler);
+    REQUIRE_THROWS_AS({
         osmium::apply(reader, handler);
-        REQUIRE_THROWS_AS({
-            osmium::apply(reader, handler);
-        }, osmium::io_error);
-    }
-
-    SECTION("should work with a buffer with uncompressed data") {
-        int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm"));
-        REQUIRE(fd >= 0);
+    }, osmium::io_error);
+}
 
-        const size_t buffer_size = 1000;
-        char buffer[buffer_size];
-        auto length = ::read(fd, buffer, buffer_size);
-        REQUIRE(length > 0);
+TEST_CASE("Reader should work with a buffer with uncompressed data") {
+    const int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm"));
+    REQUIRE(fd >= 0);
 
-        osmium::io::File file(buffer, static_cast<size_t>(length), "osm");
-        osmium::io::Reader reader(file);
-        CountHandler handler;
+    const size_t buffer_size = 1000;
+    char buffer[buffer_size];
+    const auto length = ::read(fd, buffer, buffer_size);
+    REQUIRE(length > 0);
 
-        REQUIRE(handler.count == 0);
-        osmium::apply(reader, handler);
-        REQUIRE(handler.count == 1);
-    }
+    osmium::io::File file{buffer, static_cast<size_t>(length), "osm"};
+    osmium::io::Reader reader{file};
+    CountHandler handler;
 
-    SECTION("should work with a buffer with gzip-compressed data") {
-        int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm.gz"));
-        REQUIRE(fd >= 0);
+    REQUIRE(handler.count == 0);
+    osmium::apply(reader, handler);
+    REQUIRE(handler.count == 1);
+}
 
-        const size_t buffer_size = 1000;
-        char buffer[buffer_size];
-        auto length = ::read(fd, buffer, buffer_size);
-        REQUIRE(length > 0);
+TEST_CASE("Reader should work with a buffer with gzip-compressed data") {
+    const int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm.gz"));
+    REQUIRE(fd >= 0);
 
-        osmium::io::File file(buffer, static_cast<size_t>(length), "osm.gz");
-        osmium::io::Reader reader(file);
-        CountHandler handler;
+    const size_t buffer_size = 1000;
+    char buffer[buffer_size];
+    const auto length = ::read(fd, buffer, buffer_size);
+    REQUIRE(length > 0);
 
-        REQUIRE(handler.count == 0);
-        osmium::apply(reader, handler);
-        REQUIRE(handler.count == 1);
-    }
+    osmium::io::File file{buffer, static_cast<size_t>(length), "osm.gz"};
+    osmium::io::Reader reader{file};
+    CountHandler handler;
 
-    SECTION("should work with a buffer with bzip2-compressed data") {
-        int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm.bz2"));
-        REQUIRE(fd >= 0);
+    REQUIRE(handler.count == 0);
+    osmium::apply(reader, handler);
+    REQUIRE(handler.count == 1);
+}
 
-        const size_t buffer_size = 1000;
-        char buffer[buffer_size];
-        auto length = ::read(fd, buffer, buffer_size);
-        REQUIRE(length > 0);
+TEST_CASE("Reader should work with a buffer with bzip2-compressed data") {
+    const int fd = osmium::io::detail::open_for_reading(with_data_dir("t/io/data.osm.bz2"));
+    REQUIRE(fd >= 0);
 
-        osmium::io::File file(buffer, static_cast<size_t>(length), "osm.bz2");
-        osmium::io::Reader reader(file);
-        CountHandler handler;
+    const size_t buffer_size = 1000;
+    char buffer[buffer_size];
+    const auto length = ::read(fd, buffer, buffer_size);
+    REQUIRE(length > 0);
 
-        REQUIRE(handler.count == 0);
-        osmium::apply(reader, handler);
-        REQUIRE(handler.count == 1);
-    }
+    osmium::io::File file{buffer, static_cast<size_t>(length), "osm.bz2"};
+    osmium::io::Reader reader{file};
+    CountHandler handler;
 
-    SECTION("should decode zero node positions in history (XML)") {
-        osmium::io::Reader reader(with_data_dir("t/io/deleted_nodes.osh"),
-                                  osmium::osm_entity_bits::node);
-        ZeroPositionNodeCountHandler handler;
+    REQUIRE(handler.count == 0);
+    osmium::apply(reader, handler);
+    REQUIRE(handler.count == 1);
+}
 
-        REQUIRE(handler.count == 0);
-        REQUIRE(handler.total_count == 0);
+TEST_CASE("Reader should decode zero node positions in history (XML)") {
+    osmium::io::Reader reader{with_data_dir("t/io/deleted_nodes.osh"),
+                                osmium::osm_entity_bits::node};
+    ZeroPositionNodeCountHandler handler;
 
-        osmium::apply(reader, handler);
+    REQUIRE(handler.count == 0);
+    REQUIRE(handler.total_count == 0);
 
-        REQUIRE(handler.count == 0);
-        REQUIRE(handler.total_count == 2);
-    }
+    osmium::apply(reader, handler);
 
-    SECTION("should decode zero node positions in history (PBF)") {
-        osmium::io::Reader reader(with_data_dir("t/io/deleted_nodes.osh.pbf"),
-                                  osmium::osm_entity_bits::node);
-        ZeroPositionNodeCountHandler handler;
+    REQUIRE(handler.count == 0);
+    REQUIRE(handler.total_count == 2);
+}
 
-        REQUIRE(handler.count == 0);
-        REQUIRE(handler.total_count == 0);
+TEST_CASE("Reader should decode zero node positions in history (PBF)") {
+    osmium::io::Reader reader{with_data_dir("t/io/deleted_nodes.osh.pbf"),
+                                osmium::osm_entity_bits::node};
+    ZeroPositionNodeCountHandler handler;
 
-        osmium::apply(reader, handler);
+    REQUIRE(handler.count == 0);
+    REQUIRE(handler.total_count == 0);
 
-        REQUIRE(handler.count == 0);
-        REQUIRE(handler.total_count == 2);
-    }
+    osmium::apply(reader, handler);
 
+    REQUIRE(handler.count == 0);
+    REQUIRE(handler.total_count == 2);
 }
 
-TEST_CASE("Reader failure modes") {
-
-    SECTION("should fail with nonexistent file") {
-        REQUIRE_THROWS({
-            osmium::io::Reader reader(with_data_dir("t/io/nonexistent-file.osm"));
-        });
-    }
-
-    SECTION("should fail with nonexistent file (gz)") {
-        REQUIRE_THROWS({
-            osmium::io::Reader reader(with_data_dir("t/io/nonexistent-file.osm.gz"));
-        });
-    }
+TEST_CASE("Reader should fail with nonexistent file") {
+    REQUIRE_THROWS({
+        osmium::io::Reader reader{with_data_dir("t/io/nonexistent-file.osm")};
+    });
+}
 
-    SECTION("should fail with nonexistent file (pbf)") {
-        REQUIRE_THROWS({
-            osmium::io::Reader reader(with_data_dir("t/io/nonexistent-file.osm.pbf"));
-        });
-    }
+TEST_CASE("Reader should fail with nonexistent file (gz)") {
+    REQUIRE_THROWS({
+        osmium::io::Reader reader{with_data_dir("t/io/nonexistent-file.osm.gz")};
+    });
+}
 
-    SECTION("should work when there is an exception in main thread before getting header") {
-        try {
-            osmium::io::Reader reader(with_data_dir("t/io/data.osm"));
-            REQUIRE(!reader.eof());
-            throw std::runtime_error("foo");
-        } catch (...) {
-        }
+TEST_CASE("Reader should fail with nonexistent file (pbf)") {
+    REQUIRE_THROWS({
+        osmium::io::Reader reader{with_data_dir("t/io/nonexistent-file.osm.pbf")};
+    });
+}
 
+TEST_CASE("Reader should work when there is an exception in main thread before getting header") {
+    try {
+        osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
+        REQUIRE(!reader.eof());
+        throw std::runtime_error{"foo"};
+    } catch (...) {
     }
+}
 
-    SECTION("should work when there is an exception in main thread while reading") {
-        try {
-            osmium::io::Reader reader(with_data_dir("t/io/data.osm"));
-            REQUIRE(!reader.eof());
-            auto header = reader.header();
-            throw std::runtime_error("foo");
-        } catch (...) {
-        }
-
+TEST_CASE("Reader should work when there is an exception in main thread while reading") {
+    try {
+        osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
+        REQUIRE(!reader.eof());
+        auto header = reader.header();
+        throw std::runtime_error{"foo"};
+    } catch (...) {
     }
+}
 
+TEST_CASE("Applying rvalue handler on reader") {
+    osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
+    struct NullHandler : public osmium::handler::Handler { };
+    osmium::apply(reader, NullHandler{});
 }
 
diff --git a/test/t/io/test_reader_with_mock_decompression.cpp b/test/t/io/test_reader_with_mock_decompression.cpp
index 63b8bd2..5833329 100644
--- a/test/t/io/test_reader_with_mock_decompression.cpp
+++ b/test/t/io/test_reader_with_mock_decompression.cpp
@@ -23,7 +23,7 @@ public:
         Decompressor(),
         m_fail_in(fail_in) {
         if (m_fail_in == "constructor") {
-            throw std::runtime_error("error constructor");
+            throw std::runtime_error{"error constructor"};
         }
     }
 
@@ -41,7 +41,7 @@ public:
 
         if (m_read_count == 1) {
             if (m_fail_in == "first read") {
-                throw std::runtime_error("error first read");
+                throw std::runtime_error{"error first read"};
             } else {
                 buffer += "<?xml version='1.0' encoding='UTF-8'?>\n<osm version='0.6' generator='testdata'>\n";
                 for (int i = 0; i < 1000; ++i) {
@@ -50,7 +50,7 @@ public:
             }
         } else if (m_read_count == 2) {
             if (m_fail_in == "second read") {
-                throw std::runtime_error("error second read");
+                throw std::runtime_error{"error second read"};
             } else {
                 for (int i = 1000; i < 2000; ++i) {
                     add_node(buffer, i);
@@ -65,7 +65,7 @@ public:
 
     void close() final {
         if (m_fail_in == "close") {
-            throw std::runtime_error("error close");
+            throw std::runtime_error{"error close"};
         }
     }
 
@@ -85,7 +85,7 @@ TEST_CASE("Test Reader using MockDecompressor") {
         fail_in = "constructor";
 
         try {
-            osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz"));
+            osmium::io::Reader reader{with_data_dir("t/io/data.osm.gz")};
             REQUIRE(false);
         } catch (const std::runtime_error& e) {
             REQUIRE(std::string{e.what()} == "error constructor");
@@ -96,7 +96,7 @@ TEST_CASE("Test Reader using MockDecompressor") {
         fail_in = "first read";
 
         try {
-            osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz"));
+            osmium::io::Reader reader{with_data_dir("t/io/data.osm.gz")};
             reader.read();
             REQUIRE(false);
         } catch (const std::runtime_error& e) {
@@ -108,7 +108,7 @@ TEST_CASE("Test Reader using MockDecompressor") {
         fail_in = "second read";
 
         try {
-            osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz"));
+            osmium::io::Reader reader{with_data_dir("t/io/data.osm.gz")};
             reader.read();
             reader.read();
             REQUIRE(false);
@@ -121,7 +121,7 @@ TEST_CASE("Test Reader using MockDecompressor") {
         fail_in = "close";
 
         try {
-            osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz"));
+            osmium::io::Reader reader{with_data_dir("t/io/data.osm.gz")};
             reader.read();
             reader.read();
             reader.read();
@@ -135,7 +135,7 @@ TEST_CASE("Test Reader using MockDecompressor") {
     SECTION("not failing") {
         fail_in = "not";
 
-        osmium::io::Reader reader(with_data_dir("t/io/data.osm.gz"));
+        osmium::io::Reader reader{with_data_dir("t/io/data.osm.gz")};
         reader.read();
         reader.close();
         REQUIRE(true);
diff --git a/test/t/io/test_reader_with_mock_parser.cpp b/test/t/io/test_reader_with_mock_parser.cpp
index b1076cc..9ae1b7f 100644
--- a/test/t/io/test_reader_with_mock_parser.cpp
+++ b/test/t/io/test_reader_with_mock_parser.cpp
@@ -34,7 +34,7 @@ public:
         osmium::thread::set_thread_name("_osmium_mock_in");
 
         if (m_fail_in == "header") {
-            throw std::runtime_error("error in header");
+            throw std::runtime_error{"error in header"};
         }
 
         set_header_value(osmium::io::Header{});
@@ -44,7 +44,7 @@ public:
         send_to_output_queue(std::move(buffer));
 
         if (m_fail_in == "read") {
-            throw std::runtime_error("error in read");
+            throw std::runtime_error{"error in read"};
         }
     }
 
@@ -65,7 +65,7 @@ TEST_CASE("Test Reader using MockParser") {
 
     SECTION("no failure") {
         fail_in = "";
-        osmium::io::Reader reader(with_data_dir("t/io/data.osm"));
+        osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
         auto header = reader.header();
         REQUIRE(reader.read());
         REQUIRE(!reader.read());
@@ -76,7 +76,7 @@ TEST_CASE("Test Reader using MockParser") {
     SECTION("throw in header") {
         fail_in = "header";
         try {
-            osmium::io::Reader reader(with_data_dir("t/io/data.osm"));
+            osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
             reader.header();
         } catch (const std::runtime_error& e) {
             REQUIRE(std::string{e.what()} == "error in header");
@@ -85,7 +85,7 @@ TEST_CASE("Test Reader using MockParser") {
 
     SECTION("throw in read") {
         fail_in = "read";
-        osmium::io::Reader reader(with_data_dir("t/io/data.osm"));
+        osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
         reader.header();
         try {
             reader.read();
@@ -97,10 +97,10 @@ TEST_CASE("Test Reader using MockParser") {
 
     SECTION("throw in user code") {
         fail_in = "";
-        osmium::io::Reader reader(with_data_dir("t/io/data.osm"));
+        osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
         reader.header();
         try {
-            throw std::runtime_error("error in user code");
+            throw std::runtime_error{"error in user code"};
         } catch (const std::runtime_error& e) {
             REQUIRE(std::string{e.what()} == "error in user code");
         }
diff --git a/test/t/io/test_string_table.cpp b/test/t/io/test_string_table.cpp
index 1e76245..38418a0 100644
--- a/test/t/io/test_string_table.cpp
+++ b/test/t/io/test_string_table.cpp
@@ -3,7 +3,7 @@
 #include <osmium/io/detail/string_table.hpp>
 
 TEST_CASE("String store") {
-    osmium::io::detail::StringStore ss(100);
+    osmium::io::detail::StringStore ss{100};
 
     SECTION("empty") {
         REQUIRE(ss.begin() == ss.end());
diff --git a/test/t/io/test_writer.cpp b/test/t/io/test_writer.cpp
index d3c2836..3d61759 100644
--- a/test/t/io/test_writer.cpp
+++ b/test/t/io/test_writer.cpp
@@ -10,15 +10,14 @@
 #include <osmium/memory/buffer.hpp>
 
 TEST_CASE("Writer") {
-
     osmium::io::Header header;
     header.set("generator", "test_writer.cpp");
 
-    osmium::io::Reader reader(with_data_dir("t/io/data.osm"));
+    osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
     osmium::memory::Buffer buffer = reader.read();
     REQUIRE(buffer);
     REQUIRE(buffer.committed() > 0);
-    auto num = std::distance(buffer.select<osmium::OSMObject>().cbegin(), buffer.select<osmium::OSMObject>().cend());
+    const auto num = std::distance(buffer.select<osmium::OSMObject>().cbegin(), buffer.select<osmium::OSMObject>().cend());
     REQUIRE(num > 0);
     REQUIRE(buffer.select<osmium::OSMObject>().cbegin()->id() == 1);
 
@@ -28,7 +27,7 @@ TEST_CASE("Writer") {
 
         SECTION("Empty buffer") {
             filename = "test-writer-out-empty-buffer.osm";
-            osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow);
+            osmium::io::Writer writer{filename, header, osmium::io::overwrite::allow};
             osmium::memory::Buffer empty_buffer(1024);
             writer(std::move(empty_buffer));
             writer.close();
@@ -36,13 +35,13 @@ TEST_CASE("Writer") {
 
         SECTION("Invalid buffer") {
             filename = "test-writer-out-invalid-buffer.osm";
-            osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow);
+            osmium::io::Writer writer{filename, header, osmium::io::overwrite::allow};
             osmium::memory::Buffer invalid_buffer;
             writer(std::move(invalid_buffer));
             writer.close();
         }
 
-        osmium::io::Reader reader_check(filename);
+        osmium::io::Reader reader_check{filename};
         osmium::memory::Buffer buffer_check = reader_check.read();
         REQUIRE(!buffer_check);
     }
@@ -51,7 +50,7 @@ TEST_CASE("Writer") {
 
         SECTION("Writer buffer") {
             filename = "test-writer-out-buffer.osm";
-            osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow);
+            osmium::io::Writer writer{filename, header, osmium::io::overwrite::allow};
             writer(std::move(buffer));
             writer.close();
 
@@ -62,7 +61,7 @@ TEST_CASE("Writer") {
 
         SECTION("Writer item") {
             filename = "test-writer-out-item.osm";
-            osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow);
+            osmium::io::Writer writer{filename, header, osmium::io::overwrite::allow};
             for (const auto& item : buffer) {
                 writer(item);
             }
@@ -71,13 +70,13 @@ TEST_CASE("Writer") {
 
         SECTION("Writer output iterator") {
             filename = "test-writer-out-iterator.osm";
-            osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow);
+            osmium::io::Writer writer{filename, header, osmium::io::overwrite::allow};
             auto it = osmium::io::make_output_iterator(writer);
             std::copy(buffer.cbegin(), buffer.cend(), it);
             writer.close();
         }
 
-        osmium::io::Reader reader_check(filename);
+        osmium::io::Reader reader_check{filename};
         osmium::memory::Buffer buffer_check = reader_check.read();
         REQUIRE(buffer_check);
         REQUIRE(buffer_check.committed() > 0);
@@ -89,7 +88,7 @@ TEST_CASE("Writer") {
         int error = 0;
         try {
             filename = "test-writer-out-fail1.osm";
-            osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow);
+            osmium::io::Writer writer{filename, header, osmium::io::overwrite::allow};
             throw 1;
         } catch (int e) {
             error = e;
@@ -102,7 +101,7 @@ TEST_CASE("Writer") {
         int error = 0;
         try {
             filename = "test-writer-out-fail2.osm";
-            osmium::io::Writer writer(filename, header, osmium::io::overwrite::allow);
+            osmium::io::Writer writer{filename, header, osmium::io::overwrite::allow};
             writer(std::move(buffer));
             throw 2;
         } catch (int e) {
@@ -114,4 +113,3 @@ TEST_CASE("Writer") {
 
 }
 
-
diff --git a/test/t/io/test_writer_with_mock_compression.cpp b/test/t/io/test_writer_with_mock_compression.cpp
index a28d537..186ce25 100644
--- a/test/t/io/test_writer_with_mock_compression.cpp
+++ b/test/t/io/test_writer_with_mock_compression.cpp
@@ -19,7 +19,7 @@ public:
         Compressor(osmium::io::fsync::no),
         m_fail_in(fail_in) {
         if (m_fail_in == "constructor") {
-            throw std::logic_error("constructor");
+            throw std::logic_error{"constructor"};
         }
     }
 
@@ -27,13 +27,13 @@ public:
 
     void write(const std::string&) final {
         if (m_fail_in == "write") {
-            throw std::logic_error("write");
+            throw std::logic_error{"write"};
         }
     }
 
     void close() final {
         if (m_fail_in == "close") {
-            throw std::logic_error("close");
+            throw std::logic_error{"close"};
         }
     }
 
@@ -52,7 +52,7 @@ TEST_CASE("Write with mock compressor") {
     osmium::io::Header header;
     header.set("generator", "test_writer_with_mock_compression.cpp");
 
-    osmium::io::Reader reader(with_data_dir("t/io/data.osm"));
+    osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
     osmium::memory::Buffer buffer = reader.read();
     REQUIRE(buffer);
     REQUIRE(buffer.committed() > 0);
diff --git a/test/t/io/test_writer_with_mock_encoder.cpp b/test/t/io/test_writer_with_mock_encoder.cpp
index d059f6b..b00b096 100644
--- a/test/t/io/test_writer_with_mock_encoder.cpp
+++ b/test/t/io/test_writer_with_mock_encoder.cpp
@@ -24,21 +24,21 @@ public:
 
     void write_header(const osmium::io::Header&) final {
         if (m_fail_in == "header") {
-            throw std::logic_error("header");
+            throw std::logic_error{"header"};
         }
         send_to_output_queue(std::string{"header"});
     }
 
     void write_buffer(osmium::memory::Buffer&&) final {
         if (m_fail_in == "write") {
-            throw std::logic_error("write");
+            throw std::logic_error{"write"};
         }
         send_to_output_queue(std::string{"write"});
     }
 
     void write_end() final {
         if (m_fail_in == "write_end") {
-            throw std::logic_error("write_end");
+            throw std::logic_error{"write_end"};
         }
         send_to_output_queue(std::string{"end"});
     }
@@ -58,7 +58,7 @@ TEST_CASE("Test Writer with MockOutputFormat") {
     osmium::io::Header header;
     header.set("generator", "test_writer_with_mock_encoder.cpp");
 
-    osmium::io::Reader reader(with_data_dir("t/io/data.osm"));
+    osmium::io::Reader reader{with_data_dir("t/io/data.osm")};
     osmium::memory::Buffer buffer = reader.read();
     REQUIRE(buffer);
     REQUIRE(buffer.committed() > 0);
diff --git a/test/t/memory/test_type_is_compatible.cpp b/test/t/memory/test_type_is_compatible.cpp
new file mode 100644
index 0000000..e188111
--- /dev/null
+++ b/test/t/memory/test_type_is_compatible.cpp
@@ -0,0 +1,72 @@
+
+#include "catch.hpp"
+
+#include <osmium/memory/item_iterator.hpp>
+#include <osmium/osm.hpp>
+
+static_assert(osmium::memory::detail::type_is_compatible<osmium::Node>(osmium::item_type::node), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Way>(osmium::item_type::node), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Relation>(osmium::item_type::node), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Area>(osmium::item_type::node), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::OSMObject>(osmium::item_type::node), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Changeset>(osmium::item_type::node), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::OSMEntity>(osmium::item_type::node), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::memory::Item>(osmium::item_type::node), "");
+
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Node>(osmium::item_type::way), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::Way>(osmium::item_type::way), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Relation>(osmium::item_type::way), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Area>(osmium::item_type::way), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::OSMObject>(osmium::item_type::way), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Changeset>(osmium::item_type::way), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::OSMEntity>(osmium::item_type::way), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::memory::Item>(osmium::item_type::way), "");
+
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Node>(osmium::item_type::relation), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Way>(osmium::item_type::relation), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::Relation>(osmium::item_type::relation), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Area>(osmium::item_type::relation), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::OSMObject>(osmium::item_type::relation), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Changeset>(osmium::item_type::relation), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::OSMEntity>(osmium::item_type::relation), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::memory::Item>(osmium::item_type::relation), "");
+
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Node>(osmium::item_type::area), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Way>(osmium::item_type::area), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Relation>(osmium::item_type::area), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::Area>(osmium::item_type::area), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::OSMObject>(osmium::item_type::area), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Changeset>(osmium::item_type::area), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::OSMEntity>(osmium::item_type::area), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::memory::Item>(osmium::item_type::area), "");
+
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Node>(osmium::item_type::changeset), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Way>(osmium::item_type::changeset), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Relation>(osmium::item_type::changeset), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::Area>(osmium::item_type::changeset), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::OSMObject>(osmium::item_type::changeset), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::Changeset>(osmium::item_type::changeset), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::OSMEntity>(osmium::item_type::changeset), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::memory::Item>(osmium::item_type::changeset), "");
+
+static_assert(osmium::memory::detail::type_is_compatible<osmium::OuterRing>(osmium::item_type::outer_ring), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::InnerRing>(osmium::item_type::inner_ring), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::WayNodeList>(osmium::item_type::way_node_list), "");
+
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::OuterRing>(osmium::item_type::inner_ring), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::InnerRing>(osmium::item_type::outer_ring), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::WayNodeList>(osmium::item_type::outer_ring), "");
+
+static_assert(osmium::memory::detail::type_is_compatible<osmium::TagList>(osmium::item_type::tag_list), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::InnerRing>(osmium::item_type::inner_ring), "");
+
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::TagList>(osmium::item_type::inner_ring), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::InnerRing>(osmium::item_type::tag_list), "");
+
+static_assert(osmium::memory::detail::type_is_compatible<osmium::RelationMemberList>(osmium::item_type::relation_member_list), "");
+static_assert(osmium::memory::detail::type_is_compatible<osmium::RelationMemberList>(osmium::item_type::relation_member_list_with_full_members), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::RelationMemberList>(osmium::item_type::tag_list), "");
+
+static_assert(osmium::memory::detail::type_is_compatible<osmium::ChangesetDiscussion>(osmium::item_type::changeset_discussion), "");
+static_assert(!osmium::memory::detail::type_is_compatible<osmium::ChangesetDiscussion>(osmium::item_type::relation_member_list), "");
+
diff --git a/test/t/osm/test_area.cpp b/test/t/osm/test_area.cpp
index f2847dc..c964dbb 100644
--- a/test/t/osm/test_area.cpp
+++ b/test/t/osm/test_area.cpp
@@ -74,5 +74,11 @@ TEST_CASE("Build area") {
     osmium::CRC<boost::crc_32_type> crc32;
     crc32.update(area);
     REQUIRE(crc32().checksum() == 0x2b2b7fa0);
+
+    osmium::Box envelope = area.envelope();
+    REQUIRE(envelope.bottom_left().lon() == Approx(3.2));
+    REQUIRE(envelope.bottom_left().lat() == Approx(4.2));
+    REQUIRE(envelope.top_right().lon() == Approx(3.6));
+    REQUIRE(envelope.top_right().lat() == Approx(4.9));
 }
 
diff --git a/test/t/osm/test_node_ref.cpp b/test/t/osm/test_node_ref.cpp
index 9932ff5..28a28a5 100644
--- a/test/t/osm/test_node_ref.cpp
+++ b/test/t/osm/test_node_ref.cpp
@@ -5,81 +5,92 @@
 #include <osmium/osm/node_ref.hpp>
 #include <osmium/osm/node_ref_list.hpp>
 
-TEST_CASE("NodeRef") {
-
-    SECTION("instantiation_with_default_parameters") {
-        osmium::NodeRef node_ref;
-        REQUIRE(node_ref.ref() == 0);
-//    REQUIRE(!node_ref.has_location());
-    }
-
-    SECTION("instantiation_with_id") {
-        osmium::NodeRef node_ref(7);
-        REQUIRE(node_ref.ref() == 7);
-    }
+TEST_CASE("Default construct a NodeRef") {
+    osmium::NodeRef node_ref;
+    REQUIRE(node_ref.ref() == 0);
+    REQUIRE(node_ref.location() == osmium::Location{});
+}
 
-    SECTION("equality") {
-        osmium::NodeRef node_ref1(7, { 1.2, 3.4 });
-        osmium::NodeRef node_ref2(7, { 1.4, 3.1 });
-        osmium::NodeRef node_ref3(9, { 1.2, 3.4 });
-        REQUIRE(node_ref1 == node_ref2);
-        REQUIRE(node_ref1 != node_ref3);
-        REQUIRE(!osmium::location_equal()(node_ref1, node_ref2));
-        REQUIRE(!osmium::location_equal()(node_ref2, node_ref3));
-        REQUIRE(osmium::location_equal()(node_ref1, node_ref3));
-    }
+TEST_CASE("Construct a NodeRef with an id") {
+    osmium::NodeRef node_ref{7};
+    REQUIRE(node_ref.ref() == 7);
+}
 
-    SECTION("set_location") {
-        osmium::NodeRef node_ref(7);
-        REQUIRE(!node_ref.location().valid());
-        REQUIRE(node_ref.location() == osmium::Location());
-        node_ref.set_location(osmium::Location(13.5, -7.2));
-        REQUIRE(node_ref.location().lon() == 13.5);
-        REQUIRE(node_ref.location().valid());
-    }
+TEST_CASE("Equality comparison fo NodeRefs") {
+    osmium::NodeRef node_ref1{7, {1.2, 3.4}};
+    osmium::NodeRef node_ref2{7, {1.4, 3.1}};
+    osmium::NodeRef node_ref3{9, {1.2, 3.4}};
+    REQUIRE(node_ref1 == node_ref2);
+    REQUIRE(node_ref1 != node_ref3);
+    REQUIRE(!osmium::location_equal()(node_ref1, node_ref2));
+    REQUIRE(!osmium::location_equal()(node_ref2, node_ref3));
+    REQUIRE(osmium::location_equal()(node_ref1, node_ref3));
+}
 
-    SECTION("ordering") {
-        osmium::NodeRef node_ref1(1, { 1.0, 3.0 });
-        osmium::NodeRef node_ref2(2, { 1.4, 2.9 });
-        osmium::NodeRef node_ref3(3, { 1.2, 3.0 });
-        osmium::NodeRef node_ref4(4, { 1.2, 3.3 });
-
-        REQUIRE(node_ref1 < node_ref2);
-        REQUIRE(node_ref2 < node_ref3);
-        REQUIRE(node_ref1 < node_ref3);
-        REQUIRE(node_ref1 >= node_ref1);
-
-        REQUIRE(osmium::location_less()(node_ref1, node_ref2));
-        REQUIRE(!osmium::location_less()(node_ref2, node_ref3));
-        REQUIRE(osmium::location_less()(node_ref1, node_ref3));
-        REQUIRE(osmium::location_less()(node_ref3, node_ref4));
-        REQUIRE(!osmium::location_less()(node_ref1, node_ref1));
-    }
+TEST_CASE("Set location on a NodeRef") {
+    osmium::NodeRef node_ref{7};
+    REQUIRE(!node_ref.location().valid());
+    REQUIRE(node_ref.location() == osmium::Location());
+    node_ref.set_location(osmium::Location(13.5, -7.2));
+    REQUIRE(node_ref.location().lon() == 13.5);
+    REQUIRE(node_ref.location().valid());
+}
 
+TEST_CASE("Ordering of NodeRefs") {
+    osmium::NodeRef node_ref1{1, {1.0, 3.0}};
+    osmium::NodeRef node_ref2{2, {1.4, 2.9}};
+    osmium::NodeRef node_ref3{3, {1.2, 3.0}};
+    osmium::NodeRef node_ref4{4, {1.2, 3.3}};
+
+    REQUIRE(node_ref1 < node_ref2);
+    REQUIRE(node_ref2 < node_ref3);
+    REQUIRE(node_ref1 < node_ref3);
+    REQUIRE(node_ref1 >= node_ref1);
+
+    REQUIRE(osmium::location_less()(node_ref1, node_ref2));
+    REQUIRE(!osmium::location_less()(node_ref2, node_ref3));
+    REQUIRE(osmium::location_less()(node_ref1, node_ref3));
+    REQUIRE(osmium::location_less()(node_ref3, node_ref4));
+    REQUIRE(!osmium::location_less()(node_ref1, node_ref1));
 }
 
 TEST_CASE("WayNodeList") {
-    osmium::memory::Buffer buffer(1024);
+    osmium::memory::Buffer buffer{1024};
 
     SECTION("Empty list") {
         {
-            osmium::builder::WayNodeListBuilder builder(buffer);
+            osmium::builder::WayNodeListBuilder builder{buffer};
         }
         REQUIRE(buffer.commit() == 0);
-        REQUIRE(buffer.committed( )> 0);
+        REQUIRE(buffer.committed() > 0);
 
         const osmium::WayNodeList& nrl = buffer.get<osmium::WayNodeList>(0);
         REQUIRE(nrl.empty());
         REQUIRE(nrl.size() == 0);
     }
 
+    SECTION("Change a WayNodeList") {
+        osmium::builder::add_way_node_list(buffer, osmium::builder::attr::_nodes({
+            {1, {0.0, 0.0}},
+            {2, {0.0, 1.0}},
+            {3, {1.0, 1.0}}
+        }));
+
+        osmium::WayNodeList& nrl = buffer.get<osmium::WayNodeList>(0);
+        REQUIRE(nrl.size() == 3);
+
+        REQUIRE(nrl[1].location() == osmium::Location(0.0, 1.0));
+        nrl[1].set_location(osmium::Location(13.5, -7.2));
+        REQUIRE(nrl[1].location() == osmium::Location(13.5, -7.2));
+    }
+
     SECTION("Small area") {
         osmium::builder::add_way_node_list(buffer, osmium::builder::attr::_nodes({
-            { 1, {0, 0}},
-            { 2, {0, 1}},
-            { 3, {1, 1}},
-            { 4, {1, 0}},
-            { 1, {0, 0}},
+            {1, {0.0, 0.0}},
+            {2, {0.0, 1.0}},
+            {3, {1.0, 1.0}},
+            {4, {1.0, 0.0}},
+            {1, {0.0, 0.0}},
         }));
 
         const osmium::WayNodeList& nrl = buffer.get<osmium::WayNodeList>(0);
@@ -88,13 +99,19 @@ TEST_CASE("WayNodeList") {
         REQUIRE(nrl.is_closed());
         REQUIRE(nrl.ends_have_same_id());
         REQUIRE(nrl.ends_have_same_location());
+
+        osmium::Box envelope = nrl.envelope();
+        REQUIRE(envelope.bottom_left().lon() == Approx(0));
+        REQUIRE(envelope.bottom_left().lat() == Approx(0));
+        REQUIRE(envelope.top_right().lon() == Approx(1));
+        REQUIRE(envelope.top_right().lat() == Approx(1));
     }
 
     SECTION("Not an area") {
         osmium::builder::add_way_node_list(buffer, osmium::builder::attr::_nodes({
-            { 1, {0, 0}},
-            { 2, {1, 0}},
-            { 1, {0, 0}},
+            {1, {0.0, 0.0}},
+            {2, {1.0, 0.0}},
+            {1, {0.0, 0.0}},
         }));
 
         const osmium::WayNodeList& nrl = buffer.get<osmium::WayNodeList>(0);
diff --git a/test/t/osm/test_way.cpp b/test/t/osm/test_way.cpp
index 21258a4..699abe1 100644
--- a/test/t/osm/test_way.cpp
+++ b/test/t/osm/test_way.cpp
@@ -10,7 +10,7 @@
 using namespace osmium::builder::attr;
 
 TEST_CASE("Build way") {
-    osmium::memory::Buffer buffer(10000);
+    osmium::memory::Buffer buffer{10000};
 
     osmium::builder::add_way(buffer,
         _id(17),
@@ -50,7 +50,7 @@ TEST_CASE("Build way") {
 }
 
 TEST_CASE("build closed way") {
-    osmium::memory::Buffer buffer(10000);
+    osmium::memory::Buffer buffer{10000};
 
     osmium::builder::add_way(buffer,
         _tag("highway", "residential"),
@@ -64,7 +64,7 @@ TEST_CASE("build closed way") {
 }
 
 TEST_CASE("build way with helpers") {
-    osmium::memory::Buffer buffer(10000);
+    osmium::memory::Buffer buffer{10000};
 
     {
         osmium::builder::WayBuilder builder(buffer);
@@ -90,6 +90,12 @@ TEST_CASE("build way with helpers") {
 
     REQUIRE(2 == way.nodes().size());
     REQUIRE(22 == way.nodes()[0].ref());
-    REQUIRE(4.1 == way.nodes()[1].location().lon());
+    REQUIRE(4.1 == Approx(way.nodes()[1].location().lon()));
+
+    osmium::Box envelope = way.envelope();
+    REQUIRE(envelope.bottom_left().lon() == Approx(3.5));
+    REQUIRE(envelope.bottom_left().lat() == Approx(2.2));
+    REQUIRE(envelope.top_right().lon() == Approx(4.1));
+    REQUIRE(envelope.top_right().lat() == Approx(4.7));
 }
 
diff --git a/test/t/tags/test_filter.cpp b/test/t/tags/test_filter.cpp
index 260a4ba..eab8844 100644
--- a/test/t/tags/test_filter.cpp
+++ b/test/t/tags/test_filter.cpp
@@ -10,56 +10,56 @@
 #include <osmium/tags/regex_filter.hpp>
 
 template <class TFilter>
-void check_filter(const osmium::TagList& tag_list, const TFilter filter, const std::vector<bool>& reference) {
+void check_filter(const osmium::TagList& tag_list,
+                  const TFilter filter,
+                  const std::vector<bool>& reference) {
     REQUIRE(tag_list.size() == reference.size());
     auto t_it = tag_list.begin();
     for (auto it = reference.begin(); it != reference.end(); ++t_it, ++it) {
         REQUIRE(filter(*t_it) == *it);
     }
 
-    typename TFilter::iterator fi_begin(filter, tag_list.begin(), tag_list.end());
-    typename TFilter::iterator fi_end(filter, tag_list.end(), tag_list.end());
+    typename TFilter::iterator fi_begin{filter, tag_list.begin(), tag_list.end()};
+    typename TFilter::iterator fi_end{filter, tag_list.end(), tag_list.end()};
 
     REQUIRE(std::distance(fi_begin, fi_end) == std::count(reference.begin(), reference.end(), true));
 }
 
-const osmium::TagList& make_tag_list(osmium::memory::Buffer& buffer, std::initializer_list<std::pair<const char*, const char*>> tags) {
+const osmium::TagList& make_tag_list(osmium::memory::Buffer& buffer,
+                                     std::initializer_list<std::pair<const char*, const char*>> tags) {
     const auto pos = osmium::builder::add_tag_list(buffer, osmium::builder::attr::_tags(tags));
     return buffer.get<osmium::TagList>(pos);
 }
 
 
-TEST_CASE("Filter") {
+TEST_CASE("KeyFilter") {
+    osmium::memory::Buffer buffer{10240};
+    osmium::tags::KeyFilter filter{false};
 
-    SECTION("KeyFilter_matches_some_tags") {
-        osmium::tags::KeyFilter filter(false);
-        filter.add(true, "highway").add(true, "railway");
+    const osmium::TagList& tag_list = make_tag_list(buffer, {
+        { "highway", "primary" },
+        { "name", "Main Street" },
+        { "source", "GPS" }
+    });
 
-        osmium::memory::Buffer buffer(10240);
-        const osmium::TagList& tag_list = make_tag_list(buffer, {
-            { "highway", "primary" },  // match
-            { "name", "Main Street" }, // no match
-            { "source", "GPS" }        // no match
-        });
+    SECTION("KeyFilter matches some tags") {
+        filter.add(true, "highway")
+              .add(true, "railway");
 
         const std::vector<bool> results = { true, false, false };
 
         check_filter(tag_list, filter, results);
     }
 
-    SECTION("KeyFilter_iterator_filters_tags") {
-        osmium::tags::KeyFilter filter(false);
-        filter.add(true, "highway").add(true, "source");
+    SECTION("KeyFilter iterator filters tags") {
+        filter.add(true, "highway")
+              .add(true, "source");
 
-        osmium::memory::Buffer buffer(10240);
-        const osmium::TagList& tl = make_tag_list(buffer, {
-            { "highway", "primary" },  // match
-            { "name", "Main Street" }, // no match
-            { "source", "GPS" }        // no match
-        });
+        osmium::tags::KeyFilter::iterator it{filter, tag_list.begin(),
+                                                     tag_list.end()};
 
-        osmium::tags::KeyFilter::iterator it(filter, tl.begin(), tl.end());
-        const osmium::tags::KeyFilter::iterator end(filter, tl.end(), tl.end());
+        const osmium::tags::KeyFilter::iterator end{filter, tag_list.end(),
+                                                            tag_list.end()};
 
         REQUIRE(2 == std::distance(it, end));
 
@@ -72,12 +72,18 @@ TEST_CASE("Filter") {
         REQUIRE(++it == end);
     }
 
-    SECTION("KeyValueFilter_matches_some_tags") {
-        osmium::tags::KeyValueFilter filter(false);
+}
+
+TEST_CASE("KeyValueFilter") {
+    osmium::memory::Buffer buffer{10240};
+
+    SECTION("KeyValueFilter matches some tags") {
+        osmium::tags::KeyValueFilter filter{false};
 
-        filter.add(true, "highway", "residential").add(true, "highway", "primary").add(true, "railway");
+        filter.add(true, "highway", "residential")
+              .add(true, "highway", "primary")
+              .add(true, "railway");
 
-        osmium::memory::Buffer buffer(10240);
         const osmium::TagList& tag_list = make_tag_list(buffer, {
             { "highway", "primary" },
             { "railway", "tram" },
@@ -89,14 +95,14 @@ TEST_CASE("Filter") {
         check_filter(tag_list, filter, results);
     }
 
-    SECTION("KeyValueFilter_ordering_matters") {
+    SECTION("KeyValueFilter ordering matters") {
         osmium::tags::KeyValueFilter filter1(false);
-        filter1.add(true, "highway").add(false, "highway", "road");
+        filter1.add(true, "highway")
+               .add(false, "highway", "road");
 
         osmium::tags::KeyValueFilter filter2(false);
-        filter2.add(false, "highway", "road").add(true, "highway");
-
-        osmium::memory::Buffer buffer(10240);
+        filter2.add(false, "highway", "road")
+               .add(true, "highway");
 
         const osmium::TagList& tag_list1 = make_tag_list(buffer, {
             { "highway", "road" },
@@ -113,12 +119,12 @@ TEST_CASE("Filter") {
         check_filter(tag_list2, filter2, {true, false});
     }
 
-    SECTION("KeyValueFilter_matches_against_taglist_with_any") {
-        osmium::tags::KeyValueFilter filter(false);
+    SECTION("KeyValueFilter matches against taglist with any") {
+        osmium::tags::KeyValueFilter filter{false};
 
-        filter.add(true, "highway", "primary").add(true, "name");
+        filter.add(true, "highway", "primary")
+              .add(true, "name");
 
-        osmium::memory::Buffer buffer(10240);
         const osmium::TagList& tag_list = make_tag_list(buffer, {
             { "highway", "primary" },
             { "railway", "tram" },
@@ -130,12 +136,12 @@ TEST_CASE("Filter") {
         REQUIRE(!osmium::tags::match_none_of(tag_list, filter));
     }
 
-    SECTION("KeyValueFilter_matches_against_taglist_with_all") {
-        osmium::tags::KeyValueFilter filter(false);
+    SECTION("KeyValueFilter matches against taglist with_all") {
+        osmium::tags::KeyValueFilter filter{false};
 
-        filter.add(true, "highway", "primary").add(true, "name");
+        filter.add(true, "highway", "primary")
+              .add(true, "name");
 
-        osmium::memory::Buffer buffer(10240);
         const osmium::TagList& tag_list = make_tag_list(buffer, {
             { "highway", "primary" },
             { "name", "Main Street" }
@@ -146,12 +152,12 @@ TEST_CASE("Filter") {
         REQUIRE(!osmium::tags::match_none_of(tag_list, filter));
     }
 
-    SECTION("KeyValueFilter_matches_against_taglist_with_none") {
-        osmium::tags::KeyValueFilter filter(false);
+    SECTION("KeyValueFilter matches against taglist with none") {
+        osmium::tags::KeyValueFilter filter{false};
 
-        filter.add(true, "highway", "road").add(true, "source");
+        filter.add(true, "highway", "road")
+              .add(true, "source");
 
-        osmium::memory::Buffer buffer(10240);
         const osmium::TagList& tag_list = make_tag_list(buffer, {
             { "highway", "primary" },
             { "name", "Main Street" }
@@ -162,8 +168,7 @@ TEST_CASE("Filter") {
         REQUIRE( osmium::tags::match_none_of(tag_list, filter));
     }
 
-    SECTION("KeyValueFilter_matches_against_taglist_with_any_called_with_rvalue") {
-        osmium::memory::Buffer buffer(10240);
+    SECTION("KeyValueFilter matches against taglist with any called with rvalue") {
         const osmium::TagList& tag_list = make_tag_list(buffer, {
             { "highway", "primary" },
             { "railway", "tram" },
@@ -171,14 +176,20 @@ TEST_CASE("Filter") {
         });
 
         REQUIRE(osmium::tags::match_any_of(tag_list,
-                                           osmium::tags::KeyValueFilter().add(true, "highway", "primary").add(true, "name")));
+                                           osmium::tags::KeyValueFilter()
+                                               .add(true, "highway", "primary")
+                                               .add(true, "name")));
     }
 
-    SECTION("RegexFilter_matches_some_tags") {
-        osmium::tags::RegexFilter filter(false);
-        filter.add(true, "highway", std::regex(".*_link"));
+}
+
+TEST_CASE("RegexFilter") {
+    osmium::memory::Buffer buffer{10240};
+
+    SECTION("RegexFilter matches some tags") {
+        osmium::tags::RegexFilter filter{false};
+        filter.add(true, "highway", std::regex{".*_link"});
 
-        osmium::memory::Buffer buffer(10240);
         const osmium::TagList& tag_list1 = make_tag_list(buffer, {
             { "highway", "primary_link" },
             { "source", "GPS" }
@@ -192,12 +203,11 @@ TEST_CASE("Filter") {
         check_filter(tag_list2, filter, {false, false});
     }
 
-    SECTION("RegexFilter_matches_some_tags_with_lvalue_regex") {
-        osmium::tags::RegexFilter filter(false);
-        std::regex r(".*straße");
+    SECTION("RegexFilter matches some tags with lvalue regex") {
+        osmium::tags::RegexFilter filter{false};
+        std::regex r{".*straße"};
         filter.add(true, "name", r);
 
-        osmium::memory::Buffer buffer(10240);
         const osmium::TagList& tag_list = make_tag_list(buffer, {
             { "highway", "primary" },
             { "name", "Hauptstraße" }
@@ -206,17 +216,36 @@ TEST_CASE("Filter") {
         check_filter(tag_list, filter, {false, true});
     }
 
-    SECTION("KeyPrefixFilter_matches_some_tags") {
-        osmium::tags::KeyPrefixFilter filter(false);
-        filter.add(true, "name:");
+}
 
-        osmium::memory::Buffer buffer(10240);
-        const osmium::TagList& tag_list = make_tag_list(buffer, {
-            { "highway", "primary" },
-            { "name:de", "Hauptstraße" }
-        });
+TEST_CASE("KeyPrefixFilter matches some keys") {
+    osmium::memory::Buffer buffer{10240};
 
-        check_filter(tag_list, filter, {false, true});
-    }
+    osmium::tags::KeyPrefixFilter filter{false};
+    filter.add(true, "name:");
+
+    const osmium::TagList& tag_list = make_tag_list(buffer, {
+        { "highway", "primary" },
+        { "name:de", "Hauptstraße" }
+    });
+
+    check_filter(tag_list, filter, {false, true});
+
+}
+
+TEST_CASE("Generic Filterw with regex matches some keys") {
+    osmium::memory::Buffer buffer{10240};
+
+    osmium::tags::Filter<std::regex> filter{false};
+    filter.add(true, std::regex{"restriction.+conditional"});
+
+    const osmium::TagList& tag_list = make_tag_list(buffer, {
+        { "highway", "primary" },
+        { "restrictionconditional", "only_right_turn @ (Mo-Fr 07:00-14:00)" },
+        { "restriction:conditional", "only_right_turn @ (Mo-Fr 07:00-14:00)" },
+        { "restriction:psv:conditional", "only_right_turn @ (Mo-Fr 07:00-14:00)" }
+    });
+
+    check_filter(tag_list, filter, {false, false, true, true});
 
 }
diff --git a/test/t/util/test_memory.cpp b/test/t/util/test_memory.cpp
index fa905c8..d1abc59 100644
--- a/test/t/util/test_memory.cpp
+++ b/test/t/util/test_memory.cpp
@@ -11,6 +11,8 @@ TEST_CASE("Check memory usage") {
     REQUIRE(m1.current() > 1);
     REQUIRE(m1.peak() > 1);
 
+// Memory reporting on M68k architecture doesn't work properly.
+# ifndef __m68k__
     {
         std::vector<int> v;
         v.reserve(size_in_mbytes * 1024 * 1024);
@@ -25,6 +27,7 @@ TEST_CASE("Check memory usage") {
     REQUIRE(m3.current() > 1);
     REQUIRE(m3.current() < m3.peak());
     REQUIRE(m3.peak() >= m1.peak() + size_in_mbytes);
+# endif
 #else
     osmium::MemoryUsage m;
     REQUIRE(m.current() == 0);

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