[libosmium] 01/05: Imported Upstream version 2.2.0

Sebastiaan Couwenberg sebastic at moszumanska.debian.org
Sat Jul 4 13:04:42 UTC 2015


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

sebastic pushed a commit to branch master
in repository libosmium.

commit c64b95a55d1335fadfbc70d9f829bfa31fbc8d01
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Sat Jul 4 14:19:12 2015 +0200

    Imported Upstream version 2.2.0
---
 .travis.yml                                        |  79 +--
 CHANGELOG.md                                       |  44 +-
 CMakeLists.txt                                     |  65 +-
 appveyor.yml                                       |  70 +-
 benchmarks/osmium_benchmark_count.cpp              |   7 +-
 benchmarks/osmium_benchmark_count_tag.cpp          |   5 +-
 cmake/FindOsmium.cmake                             |   2 +-
 doc/CMakeLists.txt                                 |   2 -
 doc/Doxyfile.in                                    |   4 +-
 doc/README.md                                      |   2 +-
 examples/osmium_count.cpp                          |   7 +-
 examples/osmium_create_node_cache.cpp              |   3 +-
 examples/osmium_serdump.cpp                        |   3 +-
 examples/osmium_use_node_cache.cpp                 |   3 +-
 include/mmap_for_windows.hpp                       | 103 ---
 include/osmium/area/assembler.hpp                  |  24 +-
 include/osmium/area/detail/segment_list.hpp        |   2 +-
 include/osmium/area/multipolygon_collector.hpp     |  24 +-
 include/osmium/builder/builder.hpp                 |   4 +-
 include/osmium/builder/osm_object_builder.hpp      |  14 +-
 include/osmium/geom/factory.hpp                    | 164 +++--
 include/osmium/geom/geos.hpp                       |  49 +-
 include/osmium/geom/haversine.hpp                  |   2 +-
 include/osmium/geom/mercator_projection.hpp        |   1 +
 include/osmium/geom/rapid_geojson.hpp              | 190 ++++++
 include/osmium/geom/tile.hpp                       | 101 +++
 include/osmium/geom/wkb.hpp                        |   2 +-
 include/osmium/handler/dump.hpp                    |   2 +-
 .../mmap_vector_anon.hpp => bool_vector.hpp}       |  59 +-
 include/osmium/index/detail/mmap_vector_anon.hpp   |  21 +-
 include/osmium/index/detail/mmap_vector_base.hpp   |  82 ++-
 include/osmium/index/detail/mmap_vector_file.hpp   |  26 +-
 include/osmium/index/detail/typed_mmap.hpp         | 229 -------
 include/osmium/index/detail/vector_map.hpp         |  10 +-
 include/osmium/index/detail/vector_multimap.hpp    |  34 +
 include/osmium/index/index.hpp                     |   2 +-
 include/osmium/index/map.hpp                       |  16 +-
 include/osmium/index/map/sparse_mem_table.hpp      |   4 +-
 include/osmium/io/bzip2_compression.hpp            |   2 +-
 include/osmium/io/detail/pbf.hpp                   |  31 -
 include/osmium/io/detail/pbf_input_format.hpp      |   3 +-
 include/osmium/io/detail/pbf_output_format.hpp     |  64 +-
 include/osmium/io/detail/pbf_parser.hpp            |  25 +-
 include/osmium/io/detail/pbf_stringtable.hpp       |   2 +-
 .../io/detail/{pbf.hpp => pbf_type_conv.hpp}       |  31 +-
 include/osmium/io/detail/read_write.hpp            |   2 +-
 include/osmium/io/detail/xml_input_format.hpp      |   1 +
 include/osmium/io/detail/xml_output_format.hpp     |   2 +-
 include/osmium/io/gzip_compression.hpp             |   2 +-
 include/osmium/io/reader.hpp                       |   2 +-
 include/osmium/memory/buffer.hpp                   |  18 +
 include/osmium/memory/item.hpp                     |   2 +-
 include/osmium/osm/changeset.hpp                   |   1 +
 include/osmium/osm/diff_object.hpp                 |  29 +-
 include/osmium/osm/item_type.hpp                   |  20 +
 include/osmium/osm/node_ref.hpp                    |   2 +-
 include/osmium/osm/object.hpp                      |   1 +
 include/osmium/osm/relation.hpp                    |   7 +-
 include/osmium/osm/timestamp.hpp                   |  15 +
 include/osmium/osm/types.hpp                       |  21 -
 include/osmium/osm/types_from_string.hpp           | 116 ++++
 include/osmium/relations/collector.hpp             |   4 +-
 include/osmium/thread/pool.hpp                     |   3 +-
 include/osmium/thread/queue.hpp                    |  31 +-
 include/osmium/thread/sorted_queue.hpp             |   4 +-
 include/osmium/util/cast.hpp                       |   3 +-
 include/osmium/util/data_file.hpp                  | 192 ++++++
 .../detail/mmap_vector_anon.hpp => util/delta.hpp} |  71 +-
 include/osmium/util/file.hpp                       | 118 ++++
 include/osmium/util/memory_mapping.hpp             | 723 +++++++++++++++++++++
 include/osmium/util/minmax.hpp                     | 123 ++++
 include/osmium/util/string.hpp                     |  42 +-
 include/osmium/util/verbose_output.hpp             |   2 +-
 scripts/travis_install.sh                          |  26 +
 scripts/travis_script.sh                           |  31 +
 test/CMakeLists.txt                                |  96 ++-
 test/data-tests/testdata-xml.cpp                   |   8 +-
 test/t/area/test_area_id.cpp                       |  32 +-
 test/t/area/test_node_ref_segment.cpp              | 208 +++---
 test/t/basic/test_box.cpp                          | 158 ++---
 test/t/basic/test_entity_bits.cpp                  |  36 +-
 test/t/basic/test_location.cpp                     | 278 ++++----
 test/t/basic/test_node_ref.cpp                     |  94 +--
 test/t/basic/test_object_comparisons.cpp           | 268 ++++----
 test/t/basic/test_timestamp.cpp                    |  87 ++-
 test/t/basic/test_types_from_string.cpp            |  90 +++
 test/t/buffer/test_buffer_node.cpp                 | 122 ++--
 test/t/buffer/test_buffer_purge.cpp                |  14 +-
 test/t/geom/test_exception.cpp                     |  16 +
 test/t/geom/test_factory_with_projection.cpp       |  42 +-
 test/t/geom/test_mercator.cpp                      |  58 +-
 test/t/geom/test_projection.cpp                    |  18 +
 test/t/geom/test_tile.cpp                          |  93 +++
 test/t/geom/test_tile_data.hpp                     | 475 ++++++++++++++
 test/t/geom/test_wkt.cpp                           |   8 +
 test/t/index/test_id_to_location.cpp               | 138 ++--
 test/t/index/test_typed_mmap.cpp                   |  99 ---
 test/t/io/test_bzip2.cpp                           |  34 +-
 test/t/io/test_file_formats.cpp                    | 480 +++++++-------
 test/t/tags/test_filter.cpp                        | 302 ++++-----
 test/t/tags/test_operators.cpp                     |  92 +--
 test/t/tags/test_tag_list.cpp                      | 114 ++--
 test/t/thread/test_pool.cpp                        |   7 +-
 test/t/util/test_data_file.cpp                     |  81 +++
 test/t/util/test_delta.cpp                         |  44 ++
 test/t/util/test_double.cpp                        |  50 +-
 test/t/util/test_file.cpp                          |  69 ++
 test/t/util/test_memory_mapping.cpp                | 393 +++++++++++
 test/t/util/test_minmax.cpp                        |  68 ++
 test/t/util/test_options.cpp                       |  74 +--
 test/t/util/test_string.cpp                        | 107 +--
 111 files changed, 5265 insertions(+), 2321 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 73dff72..5499433 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,46 +6,49 @@
 
 language: cpp
 
-compiler:
- - gcc
- - clang
-
-env:
- - CONFIGURATION=Dev
- - CONFIGURATION=Release
-
-before_install:
- # we need at least g++-4.8 for c++11 features
- - sudo add-apt-repository --yes ppa:ubuntu-toolchain-r/test
- - sudo apt-get update --yes --quiet
+sudo: required
+
+matrix:
+    include:
+        - os: linux
+          compiler: clang
+          env: BUILD_TYPE=Dev
+        - os: linux
+          compiler: clang
+          env: BUILD_TYPE=Release
+        - os: linux
+          compiler: gcc
+          env: BUILD_TYPE=Dev
+        - os: linux
+          compiler: gcc
+          env: BUILD_TYPE=Release
+        - os: osx
+          compiler: clang
+          env: BUILD_TYPE=Dev
+        - os: osx
+          compiler: clang
+          env: BUILD_TYPE=Release
+
+# http://docs.travis-ci.com/user/apt/
+addons:
+    apt:
+        sources:
+            - ubuntu-toolchain-r-test
+        packages:
+            - g++-4.8
+            - gcc-4.8
+            - libboost-dev
+            - libboost-program-options-dev
+            - libgeos++-dev
+            - libproj-dev
+            - libprotobuf-dev
+            - libsparsehash-dev
+            - protobuf-compiler
+            - spatialite-bin
 
 install:
- - cd ..
- # upgrade compilers
- - sudo apt-get install --yes gcc-4.8 g++-4.8
- # make sure 'cpp' is the just installed current one
- - sudo rm /usr/bin/cpp
- - sudo ln -s /usr/bin/cpp-4.8 /usr/bin/cpp
- # upgrade libosmium dependencies
- - sudo apt-get install --yes make libboost-dev libboost-program-options-dev libsparsehash-dev libprotobuf-dev protobuf-compiler libgeos++-dev libproj-dev libgdal1h libgdal-dev
- - git clone https://github.com/osmcode/osm-testdata.git
- # OSMPBF is too old, install from git
- #- sudo apt-get install --yes libosmpbf-dev
- - git clone https://github.com/scrosby/OSM-binary.git
- - cd OSM-binary/src
- - make
- - sudo make install
- - cd ../..
- - cd libosmium
-
-before_script:
- - true
+    - scripts/travis_install.sh
 
 script:
- - if [ "${CXX}" = 'g++' ]; then export CXX=g++-4.8; fi;
- - mkdir build
- - cd build
- - cmake -LA -DCMAKE_BUILD_TYPE=${CONFIGURATION} ..
- - make VERBOSE=1
- - ctest --output-on-failure
+    - scripts/travis_script.sh
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c345a4..3e7bea6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,47 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 
 ## [unreleased] -
 
+### Added
+
+### Changed
+
+### Fixed
+
+## [2.2.0] - 2015-07-04
+
+### Added
+
+- Conversion functions for some low-level types.
+- BoolVector index class.
+- `min_op`/`max_op` utility functions.
+- More tests here and there.
+- Helper methods `is_between()` and `is_visible_at()` to DiffObject.
+- GeoJSON factory using the RapidJSON library.
+- Support for tile calculations.
+- Create simple polygons from ways in geom factories.
+- `MemoryMapping` and `TypedMemoryMapping` helper classes.
+- `close()` function to `mmap_vector_base` class.
+- Function on `Buffer` class to get iterator to specific offset.
+- Explicit cast operator from `osmium::Timestamp` to `uint32_t`.
+
+### Changed
+
+- Throw exception on illegal values in functions parsing strings to get ids,
+  versions, etc.
+- Improved error message for geometry exceptions.
+
+### Fixed
+
+- Throw exception from `dump_as_array()` and `dump_as_list()` functions if not
+  implemented in an index.
+- After writing OSM files, program could stall up to a second.
+- Dense location store was written out only partially.
+- Use `uint64_t` as counter in benchmarks, so there can be no overflows.
+- Example programs now read packed XML files, too.
+- Refactoring of memory mapping code. Removes leak on Windows.
+- Better check for invalid locations.
+- Mark `cbegin()` and `cend()` of `mmap_vector_base` as const functions.
+
 ## [2.1.0] - 2015-03-31
 
 ### Added
@@ -26,6 +67,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
   Doxygen (up to version 1.8.8). This version contains a workaround to fix
   this.
 
-[unreleased]: https://github.com/osmcode/libosmium/compare/v2.1.0...HEAD
+[unreleased]: https://github.com/osmcode/libosmium/compare/v2.2.0...HEAD
+[2.2.0]: https://github.com/osmcode/libosmium/compare/v2.1.0...v2.2.0
 [2.1.0]: https://github.com/osmcode/libosmium/compare/v2.0.0...v2.1.0
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a277765..f01a20b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,6 +9,8 @@
 cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
 list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
 
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
 
 #-----------------------------------------------------------------------------
 #
@@ -16,7 +18,7 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
 #
 #-----------------------------------------------------------------------------
 
-set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev"
+set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev;Coverage"
     CACHE STRING
     "List of available configuration types"
     FORCE)
@@ -24,11 +26,13 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev"
 project(libosmium)
 
 set(LIBOSMIUM_VERSION_MAJOR 2)
-set(LIBOSMIUM_VERSION_MINOR 1)
+set(LIBOSMIUM_VERSION_MINOR 2)
 set(LIBOSMIUM_VERSION_PATCH 0)
 
 set(LIBOSMIUM_VERSION
-    ${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH})
+    "${LIBOSMIUM_VERSION_MAJOR}.${LIBOSMIUM_VERSION_MINOR}.${LIBOSMIUM_VERSION_PATCH}"
+    CACHE STRING
+    "Libosmium version")
 
 
 #-----------------------------------------------------------------------------
@@ -55,6 +59,57 @@ option(BUILD_DATA_TESTS "compile data tests, please run them with ctest" ${dev_b
 
 #-----------------------------------------------------------------------------
 #
+#  Coverage support
+#
+#-----------------------------------------------------------------------------
+
+include(CheckCXXCompilerFlag)
+check_cxx_compiler_flag("-fkeep-inline-functions" HAS_KEEP_INLINE_FUNCTIONS)
+if(HAS_KEEP_INLINE_FUNCTIONS)
+    set(extra_coverage_flags_ "-fkeep-inline-functions")
+endif()
+
+set(CMAKE_CXX_FLAGS_COVERAGE
+    "-g -O0 -fno-inline-functions -fno-inline --coverage ${extra_coverage_flags_}"
+    CACHE STRING "Flags used by the compiler during coverage builds.")
+
+set(CMAKE_EXE_LINKER_FLAGS_COVERAGE
+    "--coverage"
+    CACHE STRING "Flags used by the linker during coverage builds.")
+
+if(CMAKE_BUILD_TYPE STREQUAL "Coverage")
+    if(BUILD_EXAMPLES OR BUILD_HEADERS OR BUILD_BENCHMARKS OR BUILD_DATA_TESTS)
+        message(WARNING "Coverage builds don't work for anything but the unit tests")
+    endif()
+
+    if(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
+        string(REGEX REPLACE "^([0-9]+)\\.([0-9]+).*$" "llvm-cov-\\1.\\2"
+               gcov_ ${CMAKE_CXX_COMPILER_VERSION})
+    else()
+        set(gcov_ "gcov")
+    endif()
+
+    find_program(GCOV ${gcov_} DOC "Coverage tool")
+    find_program(GCOVR "gcovr" DOC "Coverage report tool")
+
+    set(coverage_report_dir "${CMAKE_BINARY_DIR}/coverage")
+    file(MAKE_DIRECTORY ${coverage_report_dir})
+    add_custom_target(coverage
+        ${GCOVR}
+        ${CMAKE_BINARY_DIR}
+        --root=${CMAKE_SOURCE_DIR}
+        --html --html-details
+        #--verbose
+        #--keep
+        '--filter=.*include/osmium.*'
+        --sort-percentage
+        --gcov-executable=${GCOV}
+        --output=${coverage_report_dir}/index.html)
+endif()
+
+
+#-----------------------------------------------------------------------------
+#
 #  Find external dependencies
 #
 #-----------------------------------------------------------------------------
@@ -111,8 +166,10 @@ endif()
 #-----------------------------------------------------------------------------
 if(MSVC)
     set(USUAL_COMPILE_OPTIONS "/Ox")
+    set(USUAL_LINK_OPTIONS "/debug")
 else()
     set(USUAL_COMPILE_OPTIONS "-O3 -g")
+    set(USUAL_LINK_OPTIONS "")
 endif()
 
 if(WIN32)
@@ -124,7 +181,7 @@ set(CMAKE_CXX_FLAGS_DEV "${USUAL_COMPILE_OPTIONS}"
     CACHE STRING "Flags used by the compiler during developer builds."
     FORCE)
 
-set(CMAKE_EXE_LINKER_FLAGS_DEV ""
+set(CMAKE_EXE_LINKER_FLAGS_DEV "${USUAL_LINK_OPTIONS}"
     CACHE STRING "Flags used by the linker during developer builds."
     FORCE)
 mark_as_advanced(
diff --git a/appveyor.yml b/appveyor.yml
index b3b3f51..965603b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -15,6 +15,8 @@ branches:
   only:
     - master
 
+shallow_clone: true
+
 # Operating system (build VM template)
 os: Visual Studio 2014 CTP4
 
@@ -50,8 +52,8 @@ install:
   #cmake cannot find it otherwise
   - set LIBBZIP2=%LODEPSDIR%\bzip2\lib\libbz2.lib
   - set LIBBZIP2=%LIBBZIP2:\=/%
-  - ps: Start-FileDownload https://mapnik.s3.amazonaws.com/deps/cmake-3.1.0-win32-x86.7z -FileName cm.7z
-  - ps: Start-FileDownload https://mapnik.s3.amazonaws.com/dist/dev/libosmium-deps-win-14.0-x64.7z -FileName lodeps.7z
+  - ps: Start-FileDownload https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/cmake-3.1.0-win32-x86.7z -FileName cm.7z
+  - ps: Start-FileDownload https://mapbox.s3.amazonaws.com/windows-builds/windows-build-deps/libosmium-deps-win-14.0-x64.7z -FileName lodeps.7z
   - 7z x cm.7z | %windir%\system32\find "ing archive"
   - 7z x lodeps.7z | %windir%\system32\find "ing archive"
   - echo %LODEPSDIR%
@@ -59,18 +61,74 @@ install:
   - echo our own cmake
   - cmake -version
   - cd c:\projects
-  - git clone https://github.com/osmcode/osm-testdata.git
+  - git clone --depth 1 https://github.com/osmcode/osm-testdata.git
 
 build_script:
   - cd c:\projects\libosmium
   - mkdir build
   - cd build
   - echo %config%
-  - cmake .. -LA -G "Visual Studio 14 Win64" -DOsmium_DEBUG=TRUE -DCMAKE_BUILD_TYPE=%config% -DBOOST_ROOT=%LODEPSDIR%\boost -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_57.lib -DOSMPBF_LIBRARY=%LODEPSDIR%\osmpbf\lib\osmpbf.lib -DOSMPBF_INCLUDE_DIR=%LODEPSDIR%\osmpbf\include -DPROTOBUF_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf.lib -DPROTOBUF_LITE_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf-lite.lib -DPROTOBUF_INCLUDE_DIR=%LODEPSDIR%\protobu [...]
+  # This will produce lots of LNK4099 warnings which can be ignored.
+  # Unfortunately they can't be disabled, see
+  # http://stackoverflow.com/questions/661606/visual-c-how-to-disable-specific-linker-warnings
+  - cmake .. -LA -G "Visual Studio 14 Win64"
+    -DOsmium_DEBUG=TRUE
+    -DCMAKE_BUILD_TYPE=%config%
+    -DBUILD_HEADERS=OFF
+    -DBOOST_ROOT=%LODEPSDIR%\boost
+    -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_57.lib
+    -DOSMPBF_LIBRARY=%LODEPSDIR%\osmpbf\lib\osmpbf.lib
+    -DOSMPBF_INCLUDE_DIR=%LODEPSDIR%\osmpbf\include
+    -DPROTOBUF_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf.lib
+    -DPROTOBUF_LITE_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf-lite.lib
+    -DPROTOBUF_INCLUDE_DIR=%LODEPSDIR%\protobuf\include
+    -DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib
+    -DZLIB_INCLUDE_DIR=%LODEPSDIR%\zlib\include
+    -DEXPAT_LIBRARY=%LODEPSDIR%\expat\lib\libexpat.lib
+    -DEXPAT_INCLUDE_DIR=%LODEPSDIR%\expat\include
+    -DBZIP2_LIBRARIES=%LIBBZIP2%
+    -DBZIP2_INCLUDE_DIR=%LODEPSDIR%\bzip2\include
+    -DGDAL_LIBRARY=%LODEPSDIR%\gdal\lib\gdal_i.lib
+    -DGDAL_INCLUDE_DIR=%LODEPSDIR%\gdal\include
+    -DGEOS_LIBRARY=%LODEPSDIR%\geos\lib\geos.lib
+    -DGEOS_INCLUDE_DIR=%LODEPSDIR%\geos\include
+    -DPROJ_LIBRARY=%LODEPSDIR%\proj\lib\proj.lib
+    -DPROJ_INCLUDE_DIR=%LODEPSDIR%\proj\include
+    -DSPARSEHASH_INCLUDE_DIR=%LODEPSDIR%\sparsehash\include
+    -DGETOPT_LIBRARY=%LODEPSDIR%\wingetopt\lib\wingetopt.lib
+    -DGETOPT_INCLUDE_DIR=%LODEPSDIR%\wingetopt\include
   - msbuild libosmium.sln /p:Configuration=%config% /toolsversion:14.0 /p:Platform=x64 /p:PlatformToolset=v140
-  #- cmake .. -LA -G "NMake Makefiles" -DOsmium_DEBUG=TRUE -DCMAKE_BUILD_TYPE=%config% -DBOOST_ROOT=%LODEPSDIR%\boost -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_57.lib -DOSMPBF_LIBRARY=%LODEPSDIR%\osmpbf\lib\osmpbf.lib -DOSMPBF_INCLUDE_DIR=%LODEPSDIR%\osmpbf\include -DPROTOBUF_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf.lib -DPROTOBUF_LITE_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf-lite.lib -DPROTOBUF_INCLUDE_DIR=%LODEPSDIR%\protobuf\incl [...]
+  #- cmake .. -LA -G "NMake Makefiles"
+  #  -DOsmium_DEBUG=TRUE
+  #  -DCMAKE_BUILD_TYPE=%config%
+  #  -DBOOST_ROOT=%LODEPSDIR%\boost
+  #  -DBoost_PROGRAM_OPTIONS_LIBRARY=%LODEPSDIR%\boost\lib\libboost_program_options-vc140-mt-1_57.lib
+  #  -DOSMPBF_LIBRARY=%LODEPSDIR%\osmpbf\lib\osmpbf.lib
+  #  -DOSMPBF_INCLUDE_DIR=%LODEPSDIR%\osmpbf\include
+  #  -DPROTOBUF_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf.lib
+  #  -DPROTOBUF_LITE_LIBRARY=%LODEPSDIR%\protobuf\lib\libprotobuf-lite.lib
+  #  -DPROTOBUF_INCLUDE_DIR=%LODEPSDIR%\protobuf\include
+  #  -DZLIB_LIBRARY=%LODEPSDIR%\zlib\lib\zlibwapi.lib
+  #  -DZLIB_INCLUDE_DIR=%LODEPSDIR%\zlib\include
+  #  -DEXPAT_LIBRARY=%LODEPSDIR%\expat\lib\libexpat.lib
+  #  -DEXPAT_INCLUDE_DIR=%LODEPSDIR%\expat\include
+  #  -DBZIP2_LIBRARIES=%LIBBZIP2%
+  #  -DBZIP2_INCLUDE_DIR=%LODEPSDIR%\bzip2\include
+  #  -DGDAL_LIBRARY=%LODEPSDIR%\gdal\lib\gdal_i.lib
+  #  -DGDAL_INCLUDE_DIR=%LODEPSDIR%\gdal\include
+  #  -DGEOS_LIBRARY=%LODEPSDIR%\geos\lib\geos.lib
+  #  -DGEOS_INCLUDE_DIR=%LODEPSDIR%\geos\include
+  #  -DPROJ_LIBRARY=%LODEPSDIR%\proj\lib\proj.lib
+  #  -DPROJ_INCLUDE_DIR=%LODEPSDIR%\proj\include
+  #  -DSPARSEHASH_INCLUDE_DIR=%LODEPSDIR%\sparsehash\include
+  #  -DGETOPT_LIBRARY=%LODEPSDIR%\wingetopt\lib\wingetopt.lib
+  #  -DGETOPT_INCLUDE_DIR=%LODEPSDIR%\wingetopt\include
   #- nmake
 
 test_script:
-  - ctest --output-on-failure -C %config%
+  # "-E testdata-overview" exempts one test we know fails on Appveyor
+  #    because we currently don't have spatialite support.
+  - ctest --output-on-failure
+    -C %config%
+    -E testdata-overview
 
diff --git a/benchmarks/osmium_benchmark_count.cpp b/benchmarks/osmium_benchmark_count.cpp
index 701d6fa..4409448 100644
--- a/benchmarks/osmium_benchmark_count.cpp
+++ b/benchmarks/osmium_benchmark_count.cpp
@@ -4,6 +4,7 @@
 
 */
 
+#include <cstdint>
 #include <iostream>
 
 #include <osmium/io/any_input.hpp>
@@ -12,9 +13,9 @@
 
 struct CountHandler : public osmium::handler::Handler {
 
-    int nodes = 0;
-    int ways = 0;
-    int relations = 0;
+    uint64_t nodes = 0;
+    uint64_t ways = 0;
+    uint64_t relations = 0;
 
     void node(osmium::Node&) {
         ++nodes;
diff --git a/benchmarks/osmium_benchmark_count_tag.cpp b/benchmarks/osmium_benchmark_count_tag.cpp
index 4a77c34..ca6771c 100644
--- a/benchmarks/osmium_benchmark_count_tag.cpp
+++ b/benchmarks/osmium_benchmark_count_tag.cpp
@@ -4,6 +4,7 @@
 
 */
 
+#include <cstdint>
 #include <iostream>
 
 #include <osmium/io/any_input.hpp>
@@ -12,8 +13,8 @@
 
 struct CountHandler : public osmium::handler::Handler {
 
-    int counter = 0;
-    int all = 0;
+    uint64_t counter = 0;
+    uint64_t all = 0;
 
     void node(osmium::Node& node) {
         ++all;
diff --git a/cmake/FindOsmium.cmake b/cmake/FindOsmium.cmake
index 1de41a0..56a5a4d 100644
--- a/cmake/FindOsmium.cmake
+++ b/cmake/FindOsmium.cmake
@@ -325,7 +325,7 @@ endif()
 if(MSVC)
     set(OSMIUM_WARNING_OPTIONS "/W3 /wd4514" CACHE STRING "Recommended warning options for libosmium")
 else()
-    set(OSMIUM_WARNING_OPTIONS "-Wall -Wextra -pedantic -Wredundant-decls -Wdisabled-optimization -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo -Wold-style-cast -Wno-return-type" CACHE STRING "Recommended warning options for libosmium")
+    set(OSMIUM_WARNING_OPTIONS "-Wall -Wextra -pedantic -Wredundant-decls -Wdisabled-optimization -Wctor-dtor-privacy -Wnon-virtual-dtor -Woverloaded-virtual -Wsign-promo -Wold-style-cast" CACHE STRING "Recommended warning options for libosmium")
 endif()
 
 set(OSMIUM_DRACONIC_CLANG_OPTIONS "-Wdocumentation -Wunused-exception-parameter -Wmissing-declarations -Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-unused-macros -Wno-exit-time-destructors -Wno-global-constructors -Wno-padded -Wno-switch-enum -Wno-missing-prototypes -Wno-weak-vtables -Wno-cast-align -Wno-float-equal")
diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt
index 9d69a16..5ea819b 100644
--- a/doc/CMakeLists.txt
+++ b/doc/CMakeLists.txt
@@ -21,8 +21,6 @@ if(DOXYGEN_FOUND)
         WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
         COMMENT "Generating API documentation with Doxygen" VERBATIM
     )
-#    install(DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/html"
-#            DESTINATION "share/doc/libosmium-dev")
 else()
     message(STATUS "Looking for doxygen - not found")
     message(STATUS "  Disabled making of documentation.")
diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in
index 6bc0185..d5ed13d 100644
--- a/doc/Doxyfile.in
+++ b/doc/Doxyfile.in
@@ -1080,7 +1080,7 @@ HTML_STYLESHEET        =
 # see the documentation.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
-HTML_EXTRA_STYLESHEET  = doc/osmium.css
+HTML_EXTRA_STYLESHEET  = "@PROJECT_SOURCE_DIR@/doc/osmium.css"
 
 # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
 # other source files which should be copied to the HTML output directory. Note
@@ -1128,7 +1128,7 @@ HTML_COLORSTYLE_GAMMA  = 80
 # The default value is: YES.
 # This tag requires that the tag GENERATE_HTML is set to YES.
 
-HTML_TIMESTAMP         = YES
+HTML_TIMESTAMP         = NO
 
 # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
 # documentation will contain sections that can be hidden and shown after the
diff --git a/doc/README.md b/doc/README.md
index 7ca8e7c..5e1cf4b 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -3,6 +3,6 @@ The `header.html` is created with:
 
 `doxygen -w html header.html footer.html stylesheet.css`
 
-This might have to be rn again for newer Doxygen versions. After that add
+This might have to be run again for newer Doxygen versions. After that add
 changes back in.
 
diff --git a/examples/osmium_count.cpp b/examples/osmium_count.cpp
index dca18bf..e249687 100644
--- a/examples/osmium_count.cpp
+++ b/examples/osmium_count.cpp
@@ -7,6 +7,7 @@
 
 */
 
+#include <cstdint>
 #include <iostream>
 
 #include <osmium/io/any_input.hpp>
@@ -15,9 +16,9 @@
 
 struct CountHandler : public osmium::handler::Handler {
 
-    int nodes = 0;
-    int ways = 0;
-    int relations = 0;
+    uint64_t nodes = 0;
+    uint64_t ways = 0;
+    uint64_t relations = 0;
 
     void node(osmium::Node&) {
         ++nodes;
diff --git a/examples/osmium_create_node_cache.cpp b/examples/osmium_create_node_cache.cpp
index 74f7596..b52bd17 100644
--- a/examples/osmium_create_node_cache.cpp
+++ b/examples/osmium_create_node_cache.cpp
@@ -12,8 +12,7 @@
 #include <fcntl.h>
 #include <iostream>
 
-#include <osmium/io/pbf_input.hpp>
-#include <osmium/io/xml_input.hpp>
+#include <osmium/io/any_input.hpp>
 
 #include <osmium/index/map/dummy.hpp>
 #include <osmium/index/map/dense_mmap_array.hpp>
diff --git a/examples/osmium_serdump.cpp b/examples/osmium_serdump.cpp
index a774a8d..15dce3c 100644
--- a/examples/osmium_serdump.cpp
+++ b/examples/osmium_serdump.cpp
@@ -19,8 +19,7 @@
 # include <direct.h>
 #endif
 
-#include <osmium/io/pbf_input.hpp>
-#include <osmium/io/xml_input.hpp>
+#include <osmium/io/any_input.hpp>
 #include <osmium/handler/disk_store.hpp>
 #include <osmium/handler/object_relations.hpp>
 
diff --git a/examples/osmium_use_node_cache.cpp b/examples/osmium_use_node_cache.cpp
index 6b8f964..d800e8d 100644
--- a/examples/osmium_use_node_cache.cpp
+++ b/examples/osmium_use_node_cache.cpp
@@ -12,8 +12,7 @@
 #include <fcntl.h>
 #include <iostream>
 
-#include <osmium/io/pbf_input.hpp>
-#include <osmium/io/xml_input.hpp>
+#include <osmium/io/any_input.hpp>
 
 #include <osmium/index/map/dummy.hpp>
 #include <osmium/index/map/dense_file_array.hpp>
diff --git a/include/mmap_for_windows.hpp b/include/mmap_for_windows.hpp
deleted file mode 100644
index abe62d6..0000000
--- a/include/mmap_for_windows.hpp
+++ /dev/null
@@ -1,103 +0,0 @@
-#ifndef MMAP_FOR_WINDOWS_HPP
-#define MMAP_FOR_WINDOWS_HPP
-
-/* mmap() replacement for Windows
- *
- * Author: Mike Frysinger <vapier at gentoo.org>
- * Placed into the public domain
- */
-
-/* References:
- * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
- * CloseHandle:       http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
- * MapViewOfFile:     http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
- * UnmapViewOfFile:   http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
- */
-
-#include <io.h>
-#include <windows.h>
-#include <sys/types.h>
-
-#define PROT_READ     0x1
-#define PROT_WRITE    0x2
-/* This flag is only available in WinXP+ */
-#ifdef FILE_MAP_EXECUTE
-#define PROT_EXEC     0x4
-#else
-#define PROT_EXEC        0x0
-#define FILE_MAP_EXECUTE 0
-#endif
-
-#define MAP_SHARED    0x01
-#define MAP_PRIVATE   0x02
-#define MAP_ANONYMOUS 0x20
-#define MAP_ANON      MAP_ANONYMOUS
-#define MAP_FAILED    ((void *) -1)
-
-static DWORD dword_hi(uint64_t x) {
-    return static_cast<DWORD>(x >> 32);
-}
-
-static DWORD dword_lo(uint64_t x) {
-    return static_cast<DWORD>(x & 0xffffffff);
-}
-
-static void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset)
-{
-    if (prot & ~(PROT_READ | PROT_WRITE | PROT_EXEC))
-        return MAP_FAILED;
-    if (fd == -1) {
-        if (!(flags & MAP_ANON) || offset)
-            return MAP_FAILED;
-    } else if (flags & MAP_ANON)
-        return MAP_FAILED;
-
-    DWORD flProtect;
-    if (prot & PROT_WRITE) {
-        if (prot & PROT_EXEC)
-            flProtect = PAGE_EXECUTE_READWRITE;
-        else
-            flProtect = PAGE_READWRITE;
-    } else if (prot & PROT_EXEC) {
-        if (prot & PROT_READ)
-            flProtect = PAGE_EXECUTE_READ;
-        else if (prot & PROT_EXEC)
-            flProtect = PAGE_EXECUTE;
-    } else
-        flProtect = PAGE_READONLY;
-
-    uint64_t end = static_cast<uint64_t>(length) + offset;
-    HANDLE mmap_fd;
-    if (fd == -1)
-        mmap_fd = INVALID_HANDLE_VALUE;
-    else
-        mmap_fd = (HANDLE)_get_osfhandle(fd);
-
-    HANDLE h = CreateFileMapping(mmap_fd, NULL, flProtect, dword_hi(end), dword_lo(end), NULL);
-    if (h == NULL)
-        return MAP_FAILED;
-
-    DWORD dwDesiredAccess;
-    if (prot & PROT_WRITE)
-        dwDesiredAccess = FILE_MAP_WRITE;
-    else
-        dwDesiredAccess = FILE_MAP_READ;
-    if (prot & PROT_EXEC)
-        dwDesiredAccess |= FILE_MAP_EXECUTE;
-    if (flags & MAP_PRIVATE)
-        dwDesiredAccess |= FILE_MAP_COPY;
-    void *ret = MapViewOfFile(h, dwDesiredAccess, dword_hi(offset), dword_lo(offset), length);
-    if (ret == NULL) {
-        CloseHandle(h);
-        ret = MAP_FAILED;
-    }
-    return ret;
-}
-
-static int munmap(void *addr, size_t length)
-{
-    return UnmapViewOfFile(addr) ? 0 : -1;
-    /* ruh-ro, we leaked handle from CreateFileMapping() ... */
-}
-
-#endif
diff --git a/include/osmium/area/assembler.hpp b/include/osmium/area/assembler.hpp
index db769dd..f172991 100644
--- a/include/osmium/area/assembler.hpp
+++ b/include/osmium/area/assembler.hpp
@@ -65,7 +65,7 @@ namespace osmium {
             // Enables debug output to stderr
             bool debug;
 
-            explicit AssemblerConfig(osmium::area::ProblemReporter* pr = nullptr, bool d=false) :
+            explicit AssemblerConfig(osmium::area::ProblemReporter* pr = nullptr, bool d = false) :
                 problem_reporter(pr),
                 debug(d) {
             }
@@ -74,7 +74,7 @@ namespace osmium {
              * Enable or disable debug output to stderr. This is for Osmium
              * developers only.
              */
-            void enable_debug_output(bool d=true) {
+            void enable_debug_output(bool d = true) {
                 debug = d;
             }
 
@@ -171,7 +171,7 @@ namespace osmium {
             }
 
             void add_tags_to_area(osmium::builder::AreaBuilder& builder, const osmium::Relation& relation) const {
-                auto count = std::count_if(relation.tags().begin(), relation.tags().end(), filter());
+                const auto count = std::count_if(relation.tags().begin(), relation.tags().end(), filter());
 
                 if (debug()) {
                     std::cerr << "  found " << count << " tags on relation (without ignored ones)\n";
@@ -331,7 +331,7 @@ namespace osmium {
                 if (debug()) {
                     std::cerr << "      has_closed_subring_back()\n";
                 }
-                auto end = ring.segments().end();
+                const auto end = ring.segments().end();
                 for (auto it = ring.segments().begin() + 1; it != end - 1; ++it) {
                     if (has_same_location(nr, it->first())) {
                         split_off_subring(ring, it, it, end);
@@ -348,7 +348,7 @@ namespace osmium {
                 if (debug()) {
                     std::cerr << "      has_closed_subring_front()\n";
                 }
-                auto end = ring.segments().end();
+                const auto end = ring.segments().end();
                 for (auto it = ring.segments().begin() + 1; it != end - 1; ++it) {
                     if (has_same_location(nr, it->second())) {
                         split_off_subring(ring, it, ring.segments().begin(), it+1);
@@ -366,22 +366,22 @@ namespace osmium {
                 osmium::area::detail::ProtoRing::segments_type segments(ring.segments().size());
                 std::copy(ring.segments().begin(), ring.segments().end(), segments.begin());
                 std::sort(segments.begin(), segments.end());
-                auto it = std::adjacent_find(segments.begin(), segments.end(), [this](const osmium::area::detail::NodeRefSegment& s1, const osmium::area::detail::NodeRefSegment& s2) {
+                const auto it = std::adjacent_find(segments.begin(), segments.end(), [this](const osmium::area::detail::NodeRefSegment& s1, const osmium::area::detail::NodeRefSegment& s2) {
                     return has_same_location(s1.first(), s2.first());
                 });
                 if (it == segments.end()) {
                     return false;
                 }
-                auto r1 = std::find_first_of(ring.segments().begin(), ring.segments().end(), it, it+1);
+                const auto r1 = std::find_first_of(ring.segments().begin(), ring.segments().end(), it, it+1);
                 assert(r1 != ring.segments().end());
-                auto r2 = std::find_first_of(ring.segments().begin(), ring.segments().end(), it+1, it+2);
+                const auto r2 = std::find_first_of(ring.segments().begin(), ring.segments().end(), it+1, it+2);
                 assert(r2 != ring.segments().end());
 
                 if (debug()) {
                     std::cerr << "      found subring in ring " << ring << " at " << it->first() << "\n";
                 }
 
-                auto m = std::minmax(r1, r2);
+                const auto m = std::minmax(r1, r2);
 
                 ProtoRing new_ring(m.first, m.second);
                 ring.remove_segments(m.first, m.second);
@@ -445,7 +445,7 @@ namespace osmium {
             }
 
             bool add_to_existing_ring(osmium::area::detail::NodeRefSegment segment) {
-                int n=0;
+                int n = 0;
                 for (auto& ring : m_rings) {
                     if (debug()) {
                         std::cerr << "    check against ring " << n << " " << ring;
@@ -537,7 +537,7 @@ namespace osmium {
                 }
 
                 for (const auto ringptr : m_outer_rings) {
-                    for (const auto segment : ringptr->segments()) {
+                    for (const auto& segment : ringptr->segments()) {
                         if (!segment.role_outer()) {
                             ++m_inner_outer_mismatches;
                             if (debug()) {
@@ -550,7 +550,7 @@ namespace osmium {
                     }
                 }
                 for (const auto ringptr : m_inner_rings) {
-                    for (const auto segment : ringptr->segments()) {
+                    for (const auto& segment : ringptr->segments()) {
                         if (!segment.role_inner()) {
                             ++m_inner_outer_mismatches;
                             if (debug()) {
diff --git a/include/osmium/area/detail/segment_list.hpp b/include/osmium/area/detail/segment_list.hpp
index 12ec97f..ca6071e 100644
--- a/include/osmium/area/detail/segment_list.hpp
+++ b/include/osmium/area/detail/segment_list.hpp
@@ -99,7 +99,7 @@ namespace osmium {
                  * Enable or disable debug output to stderr. This is for Osmium
                  * developers only.
                  */
-                void enable_debug_output(bool debug=true) noexcept {
+                void enable_debug_output(bool debug = true) noexcept {
                     m_debug = debug;
                 }
 
diff --git a/include/osmium/area/multipolygon_collector.hpp b/include/osmium/area/multipolygon_collector.hpp
index 84a5262..2cb8fe1 100644
--- a/include/osmium/area/multipolygon_collector.hpp
+++ b/include/osmium/area/multipolygon_collector.hpp
@@ -41,6 +41,7 @@ DEALINGS IN THE SOFTWARE.
 
 #include <osmium/memory/buffer.hpp>
 #include <osmium/osm/item_type.hpp>
+#include <osmium/osm/location.hpp>
 #include <osmium/osm/relation.hpp>
 #include <osmium/osm/tag.hpp>
 #include <osmium/osm/way.hpp>
@@ -49,8 +50,6 @@ DEALINGS IN THE SOFTWARE.
 
 namespace osmium {
 
-    struct invalid_location;
-
     namespace relations {
         class RelationMeta;
     }
@@ -107,8 +106,8 @@ namespace osmium {
             }
 
             /**
-             * We are interested in all relations tagged with type=multipolygon or
-             * type=boundary.
+             * We are interested in all relations tagged with type=multipolygon
+             * or type=boundary.
              *
              * Overwritten from the base class.
              */
@@ -142,15 +141,22 @@ namespace osmium {
              * Overwritten from the base class.
              */
             void way_not_in_any_relation(const osmium::Way& way) {
-                if (way.nodes().size() > 3 && way.ends_have_same_location()) {
-                    // way is closed and has enough nodes, build simple multipolygon
-                    try {
+                // you need at least 4 nodes to make up a polygon
+                if (way.nodes().size() <= 3) {
+                    return;
+                }
+                try {
+                    if (!way.nodes().front().location() || !way.nodes().back().location()) {
+                        throw osmium::invalid_location("invalid location");
+                    }
+                    if (way.ends_have_same_location()) {
+                        // way is closed and has enough nodes, build simple multipolygon
                         TAssembler assembler(m_assembler_config);
                         assembler(way, m_output_buffer);
                         possibly_flush_output_buffer();
-                    } catch (osmium::invalid_location&) {
-                        // XXX ignore
                     }
+                } catch (osmium::invalid_location&) {
+                    // XXX ignore
                 }
             }
 
diff --git a/include/osmium/builder/builder.hpp b/include/osmium/builder/builder.hpp
index 6606025..dcb95e2 100644
--- a/include/osmium/builder/builder.hpp
+++ b/include/osmium/builder/builder.hpp
@@ -100,7 +100,7 @@ namespace osmium {
              *             parent item (if any).
              *
              */
-            void add_padding(bool self=false) {
+            void add_padding(bool self = false) {
                 auto padding = osmium::memory::align_bytes - (size() % osmium::memory::align_bytes);
                 if (padding != osmium::memory::align_bytes) {
                     std::fill_n(m_buffer.reserve_space(padding), padding, 0);
@@ -175,7 +175,7 @@ namespace osmium {
 
         public:
 
-            explicit ObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+            explicit ObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
                 Builder(buffer, parent, sizeof(TItem)) {
                 new (&item()) TItem();
             }
diff --git a/include/osmium/builder/osm_object_builder.hpp b/include/osmium/builder/osm_object_builder.hpp
index b88765e..058f89e 100644
--- a/include/osmium/builder/osm_object_builder.hpp
+++ b/include/osmium/builder/osm_object_builder.hpp
@@ -61,7 +61,7 @@ namespace osmium {
 
         public:
 
-            explicit TagListBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+            explicit TagListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
                 ObjectBuilder<TagList>(buffer, parent) {
             }
 
@@ -97,7 +97,7 @@ namespace osmium {
 
         public:
 
-            explicit NodeRefListBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+            explicit NodeRefListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
                 ObjectBuilder<T>(buffer, parent) {
             }
 
@@ -110,7 +110,7 @@ namespace osmium {
                 static_cast<Builder*>(this)->add_size(sizeof(osmium::NodeRef));
             }
 
-            void add_node_ref(const object_id_type ref, const osmium::Location location=Location()) {
+            void add_node_ref(const object_id_type ref, const osmium::Location location = Location()) {
                 add_node_ref(NodeRef(ref, location));
             }
 
@@ -160,7 +160,7 @@ namespace osmium {
 
         public:
 
-            explicit RelationMemberListBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+            explicit RelationMemberListBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
                 ObjectBuilder<RelationMemberList>(buffer, parent) {
             }
 
@@ -215,7 +215,7 @@ namespace osmium {
 
         public:
 
-            explicit OSMObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+            explicit OSMObjectBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
                 ObjectBuilder<T>(buffer, parent) {
                 static_cast<Builder*>(this)->reserve_space_for<string_size_type>();
                 static_cast<Builder*>(this)->add_size(sizeof(string_size_type));
@@ -237,7 +237,7 @@ namespace osmium {
 
         public:
 
-            explicit WayBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+            explicit WayBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
                 OSMObjectBuilder<osmium::Way>(buffer, parent) {
             }
 
@@ -254,7 +254,7 @@ namespace osmium {
 
         public:
 
-            explicit AreaBuilder(osmium::memory::Buffer& buffer, Builder* parent=nullptr) :
+            explicit AreaBuilder(osmium::memory::Buffer& buffer, Builder* parent = nullptr) :
                 OSMObjectBuilder<osmium::Area>(buffer, parent) {
             }
 
diff --git a/include/osmium/geom/factory.hpp b/include/osmium/geom/factory.hpp
index a0a2080..9be050d 100644
--- a/include/osmium/geom/factory.hpp
+++ b/include/osmium/geom/factory.hpp
@@ -54,14 +54,43 @@ namespace osmium {
      * Exception thrown when an invalid geometry is encountered. An example
      * would be a linestring with less than two points.
      */
-    struct geometry_error : public std::runtime_error {
+    class geometry_error : public std::runtime_error {
+
+        std::string m_message;
+        osmium::object_id_type m_id;
+
+    public:
+
+        geometry_error(const std::string& message, const char* object_type = "", osmium::object_id_type id = 0) :
+            std::runtime_error(message),
+            m_message(message),
+            m_id(id) {
+            if (m_id != 0) {
+                m_message += " (";
+                m_message += object_type;
+                m_message += "_id=";
+                m_message += std::to_string(m_id);
+                m_message += ")";
+            }
+        }
+
+        void set_id(const char* object_type, osmium::object_id_type id) {
+            if (m_id == 0 && id != 0) {
+                m_message += " (";
+                m_message += object_type;
+                m_message += "_id=";
+                m_message += std::to_string(id);
+                m_message += ")";
+            }
+            m_id = id;
+        }
 
-        geometry_error(const std::string& what) :
-            std::runtime_error(what) {
+        osmium::object_id_type id() const noexcept {
+            return m_id;
         }
 
-        geometry_error(const char* what) :
-            std::runtime_error(what) {
+        virtual const char* what() const noexcept override {
+            return m_message.c_str();
         }
 
     }; // struct geometry_error
@@ -174,11 +203,21 @@ namespace osmium {
             }
 
             point_type create_point(const osmium::Node& node) {
-                return create_point(node.location());
+                try {
+                    return create_point(node.location());
+                } catch (osmium::geometry_error& e) {
+                    e.set_id("node", node.id());
+                    throw;
+                }
             }
 
             point_type create_point(const osmium::NodeRef& node_ref) {
-                return create_point(node_ref.location());
+                try {
+                    return create_point(node_ref.location());
+                } catch (osmium::geometry_error& e) {
+                    e.set_id("node", node_ref.ref());
+                    throw;
+                }
             }
 
             /* LineString */
@@ -214,7 +253,7 @@ namespace osmium {
                 return m_impl.linestring_finish(num_points);
             }
 
-            linestring_type create_linestring(const osmium::WayNodeList& wnl, use_nodes un=use_nodes::unique, direction dir=direction::forward) {
+            linestring_type create_linestring(const osmium::WayNodeList& wnl, use_nodes un = use_nodes::unique, direction dir = direction::forward) {
                 linestring_start();
                 size_t num_points = 0;
 
@@ -240,14 +279,19 @@ namespace osmium {
                 }
 
                 if (num_points < 2) {
-                    throw osmium::geometry_error("not enough points for linestring");
+                    throw osmium::geometry_error("need at least two points for linestring");
                 }
 
                 return linestring_finish(num_points);
             }
 
             linestring_type create_linestring(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir=direction::forward) {
-                return create_linestring(way.nodes(), un, dir);
+                try {
+                    return create_linestring(way.nodes(), un, dir);
+                } catch (osmium::geometry_error& e) {
+                    e.set_id("way", way.id());
+                    throw;
+                }
             }
 
             /* Polygon */
@@ -283,40 +327,86 @@ namespace osmium {
                 return m_impl.polygon_finish(num_points);
             }
 
+            polygon_type create_polygon(const osmium::WayNodeList& wnl, use_nodes un = use_nodes::unique, direction dir = direction::forward) {
+                polygon_start();
+                size_t num_points = 0;
+
+                if (un == use_nodes::unique) {
+                    osmium::Location last_location;
+                    switch (dir) {
+                        case direction::forward:
+                            num_points = fill_polygon_unique(wnl.cbegin(), wnl.cend());
+                            break;
+                        case direction::backward:
+                            num_points = fill_polygon_unique(wnl.crbegin(), wnl.crend());
+                            break;
+                    }
+                } else {
+                    switch (dir) {
+                        case direction::forward:
+                            num_points = fill_polygon(wnl.cbegin(), wnl.cend());
+                            break;
+                        case direction::backward:
+                            num_points = fill_polygon(wnl.crbegin(), wnl.crend());
+                            break;
+                    }
+                }
+
+                if (num_points < 4) {
+                    throw osmium::geometry_error("need at least four points for polygon");
+                }
+
+                return polygon_finish(num_points);
+            }
+
+            polygon_type create_polygon(const osmium::Way& way, use_nodes un=use_nodes::unique, direction dir=direction::forward) {
+                try {
+                    return create_polygon(way.nodes(), un, dir);
+                } catch (osmium::geometry_error& e) {
+                    e.set_id("way", way.id());
+                    throw;
+                }
+            }
+
             /* MultiPolygon */
 
             multipolygon_type create_multipolygon(const osmium::Area& area) {
-                size_t num_polygons = 0;
-                size_t num_rings = 0;
-                m_impl.multipolygon_start();
-
-                for (auto it = area.cbegin(); it != area.cend(); ++it) {
-                    const osmium::OuterRing& ring = static_cast<const osmium::OuterRing&>(*it);
-                    if (it->type() == osmium::item_type::outer_ring) {
-                        if (num_polygons > 0) {
-                            m_impl.multipolygon_polygon_finish();
+                try {
+                    size_t num_polygons = 0;
+                    size_t num_rings = 0;
+                    m_impl.multipolygon_start();
+
+                    for (auto it = area.cbegin(); it != area.cend(); ++it) {
+                        const osmium::OuterRing& ring = static_cast<const osmium::OuterRing&>(*it);
+                        if (it->type() == osmium::item_type::outer_ring) {
+                            if (num_polygons > 0) {
+                                m_impl.multipolygon_polygon_finish();
+                            }
+                            m_impl.multipolygon_polygon_start();
+                            m_impl.multipolygon_outer_ring_start();
+                            add_points(ring);
+                            m_impl.multipolygon_outer_ring_finish();
+                            ++num_rings;
+                            ++num_polygons;
+                        } else if (it->type() == osmium::item_type::inner_ring) {
+                            m_impl.multipolygon_inner_ring_start();
+                            add_points(ring);
+                            m_impl.multipolygon_inner_ring_finish();
+                            ++num_rings;
                         }
-                        m_impl.multipolygon_polygon_start();
-                        m_impl.multipolygon_outer_ring_start();
-                        add_points(ring);
-                        m_impl.multipolygon_outer_ring_finish();
-                        ++num_rings;
-                        ++num_polygons;
-                    } else if (it->type() == osmium::item_type::inner_ring) {
-                        m_impl.multipolygon_inner_ring_start();
-                        add_points(ring);
-                        m_impl.multipolygon_inner_ring_finish();
-                        ++num_rings;
                     }
-                }
 
-                // if there are no rings, this area is invalid
-                if (num_rings == 0) {
-                    throw osmium::geometry_error("invalid area");
-                }
+                    // if there are no rings, this area is invalid
+                    if (num_rings == 0) {
+                        throw osmium::geometry_error("area contains no rings");
+                    }
 
-                m_impl.multipolygon_polygon_finish();
-                return m_impl.multipolygon_finish();
+                    m_impl.multipolygon_polygon_finish();
+                    return m_impl.multipolygon_finish();
+                } catch (osmium::geometry_error& e) {
+                    e.set_id("area", area.id());
+                    throw;
+                }
             }
 
         }; // class GeometryFactory
diff --git a/include/osmium/geom/geos.hpp b/include/osmium/geom/geos.hpp
index 3c73637..d4105b0 100644
--- a/include/osmium/geom/geos.hpp
+++ b/include/osmium/geom/geos.hpp
@@ -42,6 +42,7 @@ DEALINGS IN THE SOFTWARE.
  * @attention If you include this file, you'll need to link with `libgeos`.
  */
 
+#include <string>
 #include <utility>
 
 #include <geos/geom/Coordinate.h>
@@ -69,8 +70,8 @@ namespace osmium {
 
     struct geos_geometry_error : public geometry_error {
 
-        geos_geometry_error() :
-            geometry_error("geometry creation failed in GEOS library, see nested exception for details") {
+        geos_geometry_error(const char* message) :
+            geometry_error(std::string("geometry creation failed in GEOS library: ") + message) {
         }
 
     }; // struct geos_geometry_error
@@ -106,8 +107,8 @@ namespace osmium {
                 point_type make_point(const osmium::geom::Coordinates& xy) const {
                     try {
                         return point_type(m_geos_factory.createPoint(geos::geom::Coordinate(xy.x, xy.y)));
-                    } catch (geos::util::GEOSException&) {
-                        THROW(osmium::geos_geometry_error());
+                    } catch (geos::util::GEOSException& e) {
+                        THROW(osmium::geos_geometry_error(e.what()));
                     }
                 }
 
@@ -116,24 +117,24 @@ namespace osmium {
                 void linestring_start() {
                     try {
                         m_coordinate_sequence.reset(m_geos_factory.getCoordinateSequenceFactory()->create(static_cast<size_t>(0), 2));
-                    } catch (geos::util::GEOSException&) {
-                        THROW(osmium::geos_geometry_error());
+                    } catch (geos::util::GEOSException& e) {
+                        THROW(osmium::geos_geometry_error(e.what()));
                     }
                 }
 
                 void linestring_add_location(const osmium::geom::Coordinates& xy) {
                     try {
                         m_coordinate_sequence->add(geos::geom::Coordinate(xy.x, xy.y));
-                    } catch (geos::util::GEOSException&) {
-                        THROW(osmium::geos_geometry_error());
+                    } catch (geos::util::GEOSException& e) {
+                        THROW(osmium::geos_geometry_error(e.what()));
                     }
                 }
 
                 linestring_type linestring_finish(size_t /* num_points */) {
                     try {
                         return linestring_type(m_geos_factory.createLineString(m_coordinate_sequence.release()));
-                    } catch (geos::util::GEOSException&) {
-                        THROW(osmium::geos_geometry_error());
+                    } catch (geos::util::GEOSException& e) {
+                        THROW(osmium::geos_geometry_error(e.what()));
                     }
                 }
 
@@ -156,48 +157,48 @@ namespace osmium {
                         });
                         m_polygons.emplace_back(m_geos_factory.createPolygon(m_rings[0].release(), inner_rings));
                         m_rings.clear();
-                    } catch (geos::util::GEOSException&) {
-                        THROW(osmium::geos_geometry_error());
+                    } catch (geos::util::GEOSException& e) {
+                        THROW(osmium::geos_geometry_error(e.what()));
                     }
                 }
 
                 void multipolygon_outer_ring_start() {
                     try {
                         m_coordinate_sequence.reset(m_geos_factory.getCoordinateSequenceFactory()->create(static_cast<size_t>(0), 2));
-                    } catch (geos::util::GEOSException&) {
-                        THROW(osmium::geos_geometry_error());
+                    } catch (geos::util::GEOSException& e) {
+                        THROW(osmium::geos_geometry_error(e.what()));
                     }
                 }
 
                 void multipolygon_outer_ring_finish() {
                     try {
                         m_rings.emplace_back(m_geos_factory.createLinearRing(m_coordinate_sequence.release()));
-                    } catch (geos::util::GEOSException&) {
-                        THROW(osmium::geos_geometry_error());
+                    } catch (geos::util::GEOSException& e) {
+                        THROW(osmium::geos_geometry_error(e.what()));
                     }
                 }
 
                 void multipolygon_inner_ring_start() {
                     try {
                         m_coordinate_sequence.reset(m_geos_factory.getCoordinateSequenceFactory()->create(static_cast<size_t>(0), 2));
-                    } catch (geos::util::GEOSException&) {
-                        THROW(osmium::geos_geometry_error());
+                    } catch (geos::util::GEOSException& e) {
+                        THROW(osmium::geos_geometry_error(e.what()));
                     }
                 }
 
                 void multipolygon_inner_ring_finish() {
                     try {
                         m_rings.emplace_back(m_geos_factory.createLinearRing(m_coordinate_sequence.release()));
-                    } catch (geos::util::GEOSException&) {
-                        THROW(osmium::geos_geometry_error());
+                    } catch (geos::util::GEOSException& e) {
+                        THROW(osmium::geos_geometry_error(e.what()));
                     }
                 }
 
                 void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
                     try {
                         m_coordinate_sequence->add(geos::geom::Coordinate(xy.x, xy.y));
-                    } catch (geos::util::GEOSException&) {
-                        THROW(osmium::geos_geometry_error());
+                    } catch (geos::util::GEOSException& e) {
+                        THROW(osmium::geos_geometry_error(e.what()));
                     }
                 }
 
@@ -209,8 +210,8 @@ namespace osmium {
                         });
                         m_polygons.clear();
                         return multipolygon_type(m_geos_factory.createMultiPolygon(polygons));
-                    } catch (geos::util::GEOSException&) {
-                        THROW(osmium::geos_geometry_error());
+                    } catch (geos::util::GEOSException& e) {
+                        THROW(osmium::geos_geometry_error(e.what()));
                     }
                 }
 
diff --git a/include/osmium/geom/haversine.hpp b/include/osmium/geom/haversine.hpp
index 14e18c5..e62a31b 100644
--- a/include/osmium/geom/haversine.hpp
+++ b/include/osmium/geom/haversine.hpp
@@ -74,7 +74,7 @@ namespace osmium {
              * Calculate length of way.
              */
             inline double distance(const osmium::WayNodeList& wnl) {
-                double sum_length=0;
+                double sum_length = 0;
 
                 for (auto it = wnl.begin(); it != wnl.end(); ++it) {
                     if (std::next(it) != wnl.end()) {
diff --git a/include/osmium/geom/mercator_projection.hpp b/include/osmium/geom/mercator_projection.hpp
index a6d1d57..22a0d64 100644
--- a/include/osmium/geom/mercator_projection.hpp
+++ b/include/osmium/geom/mercator_projection.hpp
@@ -47,6 +47,7 @@ namespace osmium {
         namespace detail {
 
             constexpr double earth_radius_for_epsg3857 = 6378137.0;
+            constexpr double max_coordinate_epsg3857 = 20037508.34;
 
             constexpr inline double lon_to_x(double lon) {
                 return earth_radius_for_epsg3857 * deg_to_rad(lon);
diff --git a/include/osmium/geom/rapid_geojson.hpp b/include/osmium/geom/rapid_geojson.hpp
new file mode 100644
index 0000000..a3d4687
--- /dev/null
+++ b/include/osmium/geom/rapid_geojson.hpp
@@ -0,0 +1,190 @@
+#ifndef OSMIUM_GEOM_RAPID_GEOJSON_HPP
+#define OSMIUM_GEOM_RAPID_GEOJSON_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 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 <osmium/geom/coordinates.hpp>
+#include <osmium/geom/factory.hpp>
+
+namespace osmium {
+
+    namespace geom {
+
+        namespace detail {
+
+            /**
+             * A geometry factory implementation that can be used with the
+             * RapidJSON (https://github.com/miloyip/rapidjson) JSON writer.
+             */
+            template <class TWriter>
+            class RapidGeoJSONFactoryImpl {
+
+                TWriter* m_writer;
+
+            public:
+
+                typedef void point_type;
+                typedef void linestring_type;
+                typedef void polygon_type;
+                typedef void multipolygon_type;
+                typedef void ring_type;
+
+                RapidGeoJSONFactoryImpl(TWriter& writer) :
+                    m_writer(&writer) {
+                }
+
+                /* Point */
+
+                // { "type": "Point", "coordinates": [100.0, 0.0] }
+                point_type make_point(const osmium::geom::Coordinates& xy) const {
+                    m_writer->String("geometry");
+                    m_writer->StartObject();
+                    m_writer->String("type");
+                    m_writer->String("Point");
+                    m_writer->String("coordinates");
+                    m_writer->StartArray();
+                    m_writer->Double(xy.x);
+                    m_writer->Double(xy.y);
+                    m_writer->EndArray();
+                    m_writer->EndObject();
+                }
+
+                /* LineString */
+
+                // { "type": "LineString", "coordinates": [ [100.0, 0.0], [101.0, 1.0] ] }
+                void linestring_start() {
+                    m_writer->String("geometry");
+                    m_writer->StartObject();
+                    m_writer->String("type");
+                    m_writer->String("LineString");
+                    m_writer->String("coordinates");
+                    m_writer->StartArray();
+                }
+
+                void linestring_add_location(const osmium::geom::Coordinates& xy) {
+                    m_writer->StartArray();
+                    m_writer->Double(xy.x);
+                    m_writer->Double(xy.y);
+                    m_writer->EndArray();
+                }
+
+                linestring_type linestring_finish(size_t /* num_points */) {
+                    m_writer->EndArray();
+                    m_writer->EndObject();
+                }
+
+                /* Polygon */
+
+                // { "type": "Polygon", "coordinates": [[[100.0, 0.0], [101.0, 1.0]]] }
+                void polygon_start() {
+                    m_writer->String("geometry");
+                    m_writer->StartObject();
+                    m_writer->String("type");
+                    m_writer->String("Polygon");
+                    m_writer->String("coordinates");
+                    m_writer->StartArray();
+                    m_writer->StartArray();
+                }
+
+                void polygon_add_location(const osmium::geom::Coordinates& xy) {
+                    m_writer->StartArray();
+                    m_writer->Double(xy.x);
+                    m_writer->Double(xy.y);
+                    m_writer->EndArray();
+                }
+
+                polygon_type polygon_finish(size_t /* num_points */) {
+                    m_writer->EndArray();
+                    m_writer->EndArray();
+                    m_writer->EndObject();
+                }
+
+                /* MultiPolygon */
+
+                void multipolygon_start() {
+                    m_writer->String("geometry");
+                    m_writer->StartObject();
+                    m_writer->String("type");
+                    m_writer->String("MultiPolygon");
+                    m_writer->String("coordinates");
+                    m_writer->StartArray();
+                }
+
+                void multipolygon_polygon_start() {
+                    m_writer->StartArray();
+                }
+
+                void multipolygon_polygon_finish() {
+                    m_writer->EndArray();
+                }
+
+                void multipolygon_outer_ring_start() {
+                    m_writer->StartArray();
+                }
+
+                void multipolygon_outer_ring_finish() {
+                    m_writer->EndArray();
+                }
+
+                void multipolygon_inner_ring_start() {
+                    m_writer->StartArray();
+                }
+
+                void multipolygon_inner_ring_finish() {
+                    m_writer->EndArray();
+                }
+
+                void multipolygon_add_location(const osmium::geom::Coordinates& xy) {
+                    m_writer->StartArray();
+                    m_writer->Double(xy.x);
+                    m_writer->Double(xy.y);
+                    m_writer->EndArray();
+                }
+
+                multipolygon_type multipolygon_finish() {
+                    m_writer->EndArray();
+                    m_writer->EndObject();
+                }
+
+            }; // class RapidGeoJSONFactoryImpl
+
+        } // namespace detail
+
+        template <class TWriter, class TProjection = IdentityProjection>
+        using RapidGeoJSONFactory = GeometryFactory<detail::RapidGeoJSONFactoryImpl<TWriter>, TProjection>;
+
+    } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_RAPID_GEOJSON_HPP
diff --git a/include/osmium/geom/tile.hpp b/include/osmium/geom/tile.hpp
new file mode 100644
index 0000000..9cd0b28
--- /dev/null
+++ b/include/osmium/geom/tile.hpp
@@ -0,0 +1,101 @@
+#ifndef OSMIUM_GEOM_TILE_HPP
+#define OSMIUM_GEOM_TILE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 Jochen Topf <jochen at topf.org> and others (see README).
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+
+*/
+
+#include <cstdint>
+
+#include <osmium/geom/mercator_projection.hpp>
+
+namespace osmium {
+
+    namespace geom {
+
+        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;
+            }
+
+        } // namespace detail
+
+        /**
+         * A tile in the usual Mercator projection.
+         */
+        struct Tile {
+
+            uint32_t x;
+            uint32_t y;
+            uint32_t z;
+
+            explicit Tile(uint32_t zoom, uint32_t tx, uint32_t ty) noexcept : x(tx), y(ty), z(zoom) {
+            }
+
+            explicit Tile(uint32_t zoom, const osmium::Location& location) :
+                z(zoom) {
+                osmium::geom::Coordinates c = lonlat_to_mercator(location);
+                const int32_t n = 1LL << zoom;
+                const double scale = detail::max_coordinate_epsg3857 * 2 / n;
+                x = detail::restrict_to_range<int32_t>((c.x + detail::max_coordinate_epsg3857) / scale, 0, n-1);
+                y = detail::restrict_to_range<int32_t>((detail::max_coordinate_epsg3857 - c.y) / scale, 0, n-1);
+            }
+
+        }; // struct Tile
+
+        inline bool operator==(const Tile& a, const Tile& b) {
+            return a.z == b.z && a.x == b.x && a.y == b.y;
+        }
+
+        inline bool operator!=(const Tile& a, const Tile& b) {
+            return ! (a == b);
+        }
+
+        /**
+         * This defines an arbitrary order on tiles for use in std::map etc.
+         */
+        inline bool operator<(const Tile& a, const Tile& b) {
+            if (a.z < b.z) return true;
+            if (a.z > b.z) return false;
+            if (a.x < b.x) return true;
+            if (a.x > b.x) return false;
+            return a.y < b.y;
+        }
+
+    } // namespace geom
+
+} // namespace osmium
+
+#endif // OSMIUM_GEOM_TILE_HPP
diff --git a/include/osmium/geom/wkb.hpp b/include/osmium/geom/wkb.hpp
index 31fce71..2f32fe3 100644
--- a/include/osmium/geom/wkb.hpp
+++ b/include/osmium/geom/wkb.hpp
@@ -161,7 +161,7 @@ namespace osmium {
                 typedef std::string multipolygon_type;
                 typedef std::string ring_type;
 
-                explicit WKBFactoryImpl(wkb_type wtype=wkb_type::wkb, out_type otype=out_type::binary) :
+                explicit WKBFactoryImpl(wkb_type wtype = wkb_type::wkb, out_type otype = out_type::binary) :
                     m_wkb_type(wtype),
                     m_out_type(otype) {
                 }
diff --git a/include/osmium/handler/dump.hpp b/include/osmium/handler/dump.hpp
index 564035c..a23236e 100644
--- a/include/osmium/handler/dump.hpp
+++ b/include/osmium/handler/dump.hpp
@@ -136,7 +136,7 @@ namespace osmium {
 
         public:
 
-            explicit Dump(std::ostream& out, bool with_size=true, const std::string& prefix="") :
+            explicit Dump(std::ostream& out, bool with_size = true, const std::string& prefix = "") :
                 m_out(&out),
                 m_with_size(with_size),
                 m_prefix(prefix) {
diff --git a/include/osmium/index/detail/mmap_vector_anon.hpp b/include/osmium/index/bool_vector.hpp
similarity index 58%
copy from include/osmium/index/detail/mmap_vector_anon.hpp
copy to include/osmium/index/bool_vector.hpp
index 0ea4f9d..94e1f72 100644
--- a/include/osmium/index/detail/mmap_vector_anon.hpp
+++ b/include/osmium/index/bool_vector.hpp
@@ -1,5 +1,5 @@
-#ifndef OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
-#define OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
+#ifndef OSMIUM_INDEX_BOOL_VECTOR_HPP
+#define OSMIUM_INDEX_BOOL_VECTOR_HPP
 
 /*
 
@@ -33,46 +33,51 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
-#ifdef __linux__
-
-#include <cstddef>
-
-#include <osmium/index/detail/typed_mmap.hpp>
-#include <osmium/index/detail/mmap_vector_base.hpp>
+#include <type_traits>
+#include <vector>
 
 namespace osmium {
 
-    namespace detail {
+    namespace index {
 
         /**
-         * This class looks and behaves like STL vector, but uses mmap internally.
+         * Index storing one bit for each Id. The index automatically scales
+         * with the Ids stored. Default value is 'false'. Storage uses
+         * std::vector<bool> and needs a minimum of memory if the Ids are
+         * dense.
          */
         template <typename T>
-        class mmap_vector_anon : public mmap_vector_base<T, mmap_vector_anon> {
+        class BoolVector {
+
+            static_assert(std::is_unsigned<T>::value, "Needs unsigned type");
+
+            std::vector<bool> m_bits;
 
         public:
 
-            mmap_vector_anon() :
-                mmap_vector_base<T, osmium::detail::mmap_vector_anon>(
-                    -1,
-                    osmium::detail::mmap_vector_size_increment,
-                    0,
-                    osmium::detail::typed_mmap<T>::map(osmium::detail::mmap_vector_size_increment)) {
-            }
+            BoolVector() = default;
+            BoolVector(const BoolVector&) = default;
+            BoolVector(BoolVector&&) = default;
+            BoolVector& operator=(const BoolVector&) = default;
+            BoolVector& operator=(BoolVector&&) = default;
+            ~BoolVector() = default;
 
-            void reserve(size_t new_capacity) {
-                if (new_capacity > this->capacity()) {
-                    this->data(osmium::detail::typed_mmap<T>::remap(this->data(), this->capacity(), new_capacity));
-                    this->m_capacity = new_capacity;
+            void set(T id, bool value = true) {
+                if (m_bits.size() <= id) {
+                    m_bits.resize(id + 1024 * 1024);
                 }
+
+                m_bits[id] = value;
+            }
+
+            bool get(T id) const {
+                return id < m_bits.size() && m_bits[id];
             }
 
-        }; // class mmap_vector_anon
+        }; // class BoolVector
 
-    } // namespace detail
+    } // namespace index
 
 } // namespace osmium
 
-#endif // __linux__
-
-#endif // OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
+#endif // OSMIUM_INDEX_BOOL_VECTOR_HPP
diff --git a/include/osmium/index/detail/mmap_vector_anon.hpp b/include/osmium/index/detail/mmap_vector_anon.hpp
index 0ea4f9d..fc01626 100644
--- a/include/osmium/index/detail/mmap_vector_anon.hpp
+++ b/include/osmium/index/detail/mmap_vector_anon.hpp
@@ -35,9 +35,6 @@ DEALINGS IN THE SOFTWARE.
 
 #ifdef __linux__
 
-#include <cstddef>
-
-#include <osmium/index/detail/typed_mmap.hpp>
 #include <osmium/index/detail/mmap_vector_base.hpp>
 
 namespace osmium {
@@ -45,26 +42,16 @@ namespace osmium {
     namespace detail {
 
         /**
-         * This class looks and behaves like STL vector, but uses mmap internally.
+         * This class looks and behaves like STL vector, but uses mmap
+         * internally.
          */
         template <typename T>
-        class mmap_vector_anon : public mmap_vector_base<T, mmap_vector_anon> {
+        class mmap_vector_anon : public mmap_vector_base<T> {
 
         public:
 
             mmap_vector_anon() :
-                mmap_vector_base<T, osmium::detail::mmap_vector_anon>(
-                    -1,
-                    osmium::detail::mmap_vector_size_increment,
-                    0,
-                    osmium::detail::typed_mmap<T>::map(osmium::detail::mmap_vector_size_increment)) {
-            }
-
-            void reserve(size_t new_capacity) {
-                if (new_capacity > this->capacity()) {
-                    this->data(osmium::detail::typed_mmap<T>::remap(this->data(), this->capacity(), new_capacity));
-                    this->m_capacity = new_capacity;
-                }
+                mmap_vector_base<T>() {
             }
 
         }; // class mmap_vector_anon
diff --git a/include/osmium/index/detail/mmap_vector_base.hpp b/include/osmium/index/detail/mmap_vector_base.hpp
index 3aff26d..2400f89 100644
--- a/include/osmium/index/detail/mmap_vector_base.hpp
+++ b/include/osmium/index/detail/mmap_vector_base.hpp
@@ -37,8 +37,7 @@ DEALINGS IN THE SOFTWARE.
 #include <new>
 #include <stdexcept>
 
-#include <osmium/index/detail/typed_mmap.hpp>
-#include <osmium/util/compatibility.hpp>
+#include <osmium/util/memory_mapping.hpp>
 
 namespace osmium {
 
@@ -48,40 +47,29 @@ namespace osmium {
 
         /**
          * This is a base class for implementing classes that look like
-         * STL vector but use mmap internally. This class can not be used
-         * on it's own. Use the derived classes mmap_vector_anon or
-         * mmap_vector_file.
+         * STL vector but use mmap internally. Do not use this class itself,
+         * use the derived classes mmap_vector_anon or mmap_vector_file.
          */
-        template <typename T, template <typename> class TDerived>
+        template <typename T>
         class mmap_vector_base {
 
         protected:
 
-            int m_fd;
-            size_t m_capacity;
             size_t m_size;
-            T* m_data;
+            osmium::util::TypedMemoryMapping<T> m_mapping;
 
-            explicit mmap_vector_base(int fd, size_t capacity, size_t size, T* data) noexcept :
-                m_fd(fd),
-                m_capacity(capacity),
-                m_size(size),
-                m_data(data) {
-            }
+        public:
 
-            explicit mmap_vector_base(int fd, size_t capacity, size_t size) :
-                m_fd(fd),
-                m_capacity(capacity),
+            explicit mmap_vector_base(int fd, size_t capacity, size_t size = 0) :
                 m_size(size),
-                m_data(osmium::detail::typed_mmap<T>::grow_and_map(capacity, m_fd)) {
+                m_mapping(capacity, true, fd) {
             }
 
-            void data(T* data) {
-                m_data = data;
+            explicit mmap_vector_base(size_t capacity = mmap_vector_size_increment) :
+                m_size(0),
+                m_mapping(capacity) {
             }
 
-        public:
-
             typedef T value_type;
             typedef T& reference;
             typedef const T& const_reference;
@@ -90,12 +78,14 @@ namespace osmium {
             typedef T* iterator;
             typedef const T* const_iterator;
 
-            ~mmap_vector_base() {
-                osmium::detail::typed_mmap<T>::unmap(m_data, m_capacity);
+            ~mmap_vector_base() = default;
+
+            void close() {
+                m_mapping.unmap();
             }
 
             size_t capacity() const noexcept {
-                return m_capacity;
+                return m_mapping.size();
             }
 
             size_t size() const noexcept {
@@ -106,23 +96,23 @@ namespace osmium {
                 return m_size == 0;
             }
 
-            const T* data() const noexcept {
-                return m_data;
+            const T* data() const {
+                return m_mapping.begin();
             }
 
-            T* data() noexcept {
-                return m_data;
+            T* data() {
+                return m_mapping.begin();
             }
 
             T& operator[](size_t n) {
-                return m_data[n];
+                return data()[n];
             }
 
             T at(size_t n) const {
                 if (n >= m_size) {
                     throw std::out_of_range("out of range");
                 }
-                return m_data[n];
+                return data()[n];
             }
 
             void clear() noexcept {
@@ -134,16 +124,22 @@ namespace osmium {
             }
 
             void push_back(const T& value) {
-                if (m_size >= m_capacity) {
+                if (m_size >= capacity()) {
                     resize(m_size+1);
                 }
-                m_data[m_size] = value;
+                data()[m_size] = value;
                 ++m_size;
             }
 
+            void reserve(size_t new_capacity) {
+                if (new_capacity > capacity()) {
+                    m_mapping.resize(new_capacity);
+                }
+            }
+
             void resize(size_t new_size) {
                 if (new_size > capacity()) {
-                    static_cast<TDerived<T>*>(this)->reserve(new_size + osmium::detail::mmap_vector_size_increment);
+                    reserve(new_size + osmium::detail::mmap_vector_size_increment);
                 }
                 if (new_size > size()) {
                     new (data() + size()) T[new_size - size()];
@@ -152,27 +148,27 @@ namespace osmium {
             }
 
             iterator begin() noexcept {
-                return m_data;
+                return data();
             }
 
             iterator end() noexcept {
-                return m_data + m_size;
+                return data() + m_size;
             }
 
             const_iterator begin() const noexcept {
-                return m_data;
+                return data();
             }
 
             const_iterator end() const noexcept {
-                return m_data + m_size;
+                return data() + m_size;
             }
 
-            const_iterator cbegin() noexcept {
-                return m_data;
+            const_iterator cbegin() const noexcept {
+                return data();
             }
 
-            const_iterator cend() noexcept {
-                return m_data + m_size;
+            const_iterator cend() const noexcept {
+                return data() + m_size;
             }
 
         }; // class mmap_vector_base
diff --git a/include/osmium/index/detail/mmap_vector_file.hpp b/include/osmium/index/detail/mmap_vector_file.hpp
index 55077d1..1336003 100644
--- a/include/osmium/index/detail/mmap_vector_file.hpp
+++ b/include/osmium/index/detail/mmap_vector_file.hpp
@@ -35,7 +35,6 @@ DEALINGS IN THE SOFTWARE.
 
 #include <cstddef>
 
-#include <osmium/index/detail/typed_mmap.hpp>
 #include <osmium/index/detail/mmap_vector_base.hpp>
 #include <osmium/index/detail/tmpfile.hpp>
 
@@ -48,32 +47,19 @@ namespace osmium {
          * internally.
          */
         template <typename T>
-        class mmap_vector_file : public mmap_vector_base<T, mmap_vector_file> {
+        class mmap_vector_file : public mmap_vector_base<T> {
 
         public:
 
-            explicit mmap_vector_file() :
-                mmap_vector_base<T, osmium::detail::mmap_vector_file>(
+            explicit mmap_vector_file() : mmap_vector_base<T>(
                     osmium::detail::create_tmp_file(),
-                    osmium::detail::mmap_vector_size_increment,
-                    0) {
+                    osmium::detail::mmap_vector_size_increment) {
             }
 
-            explicit mmap_vector_file(int fd) :
-                mmap_vector_base<T, osmium::detail::mmap_vector_file>(
+            explicit mmap_vector_file(int fd) : mmap_vector_base<T>(
                     fd,
-                    osmium::detail::typed_mmap<T>::file_size(fd) == 0 ?
-                        osmium::detail::mmap_vector_size_increment :
-                        osmium::detail::typed_mmap<T>::file_size(fd),
-                    osmium::detail::typed_mmap<T>::file_size(fd)) {
-            }
-
-            void reserve(size_t new_capacity) {
-                if (new_capacity > this->capacity()) {
-                    typed_mmap<T>::unmap(this->data(), this->capacity());
-                    this->data(typed_mmap<T>::grow_and_map(new_capacity, this->m_fd));
-                    this->m_capacity = new_capacity;
-                }
+                    osmium::util::file_size(fd) / sizeof(T),
+                    osmium::util::file_size(fd) / sizeof(T)) {
             }
 
         }; // class mmap_vector_file
diff --git a/include/osmium/index/detail/typed_mmap.hpp b/include/osmium/index/detail/typed_mmap.hpp
deleted file mode 100644
index 77b065e..0000000
--- a/include/osmium/index/detail/typed_mmap.hpp
+++ /dev/null
@@ -1,229 +0,0 @@
-#ifndef OSMIUM_INDEX_DETAIL_TYPED_MMAP_HPP
-#define OSMIUM_INDEX_DETAIL_TYPED_MMAP_HPP
-
-/*
-
-This file is part of Osmium (http://osmcode.org/libosmium).
-
-Copyright 2013-2015 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 <cerrno>
-#include <cstddef>
-#include <stdexcept>
-#include <system_error>
-
-#include <sys/stat.h>
-
-#ifndef _WIN32
-# include <sys/mman.h>
-#else
-# include <mmap_for_windows.hpp>
-#endif
-
-#ifndef _MSC_VER
-# include <unistd.h>
-#else
-# define ftruncate _chsize
-#endif
-
-// for bsd systems
-#ifndef MAP_ANONYMOUS
-# define MAP_ANONYMOUS MAP_ANON
-#endif
-
-#include <osmium/util/cast.hpp>
-
-namespace osmium {
-
-    /**
-     * @brief Namespace for Osmium internal use
-     */
-    namespace detail {
-
-        /**
-         * This is a helper class for working with memory mapped files and
-         * anonymous shared memory. It wraps the necessary system calls
-         * adding:
-         * - error checking: all functions throw exceptions where needed
-         * - internal casts and size calculations allow use with user defined
-         *   type T instead of void*
-         *
-         * This class only contains static functions. It should never be
-         * instantiated.
-         *
-         * @tparam T Type of objects we want to store.
-         */
-        template <typename T>
-        class typed_mmap {
-
-        public:
-
-            /**
-             * Create anonymous private memory mapping with enough space for size
-             * objects of type T.
-             *
-             * Note that no constructor is called for any of the objects in this memory!
-             *
-             * @param size Number of objects of type T that should fit into this memory
-             * @returns Pointer to mapped memory
-             * @throws std::system_error If mmap(2) failed
-             */
-            static T* map(size_t size) {
-                void* addr = ::mmap(nullptr, sizeof(T) * size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-                if (addr == MAP_FAILED) {
-                    throw std::system_error(errno, std::system_category(), "mmap failed");
-                }
-#pragma GCC diagnostic pop
-                return reinterpret_cast<T*>(addr);
-            }
-
-            /**
-             * Create shared memory mapping of a file with enough space for size
-             * objects of type T. The file must already have at least the
-             * required size.
-             *
-             * Note that no constructor is called for any of the objects in this memory!
-             *
-             * @param size Number of objects of type T that should fit into this memory
-             * @param fd File descriptor
-             * @param write True if data should be writable
-             * @returns Pointer to mapped memory
-             * @throws std::system_error If mmap(2) failed
-             */
-            static T* map(size_t size, int fd, bool write = false) {
-                int prot = PROT_READ;
-                if (write) {
-                    prot |= PROT_WRITE;
-                }
-                void* addr = ::mmap(nullptr, sizeof(T) * size, prot, MAP_SHARED, fd, 0);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-                if (addr == MAP_FAILED) {
-                    throw std::system_error(errno, std::system_category(), "mmap failed");
-                }
-#pragma GCC diagnostic pop
-                return reinterpret_cast<T*>(addr);
-            }
-
-// mremap(2) is only available on linux systems
-#ifdef __linux__
-            /**
-             * Grow memory mapping created with map().
-             *
-             * Note that no constructor is called for any of the objects in this memory!
-             *
-             * @param data Pointer to current mapping (as returned by typed_mmap())
-             * @param old_size Number of objects currently stored in this memory
-             * @param new_size Number of objects we want to have space for
-             * @throws std::system_error If mremap(2) call failed
-             */
-            static T* remap(T* data, size_t old_size, size_t new_size) {
-                void* addr = ::mremap(reinterpret_cast<void*>(data), sizeof(T) * old_size, sizeof(T) * new_size, MREMAP_MAYMOVE);
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wold-style-cast"
-                if (addr == MAP_FAILED) {
-                    throw std::system_error(errno, std::system_category(), "mremap failed");
-                }
-#pragma GCC diagnostic pop
-                return reinterpret_cast<T*>(addr);
-            }
-#endif
-
-            /**
-             * Release memory from map() call.
-             *
-             * Note that no destructor is called for the objects in this memory!
-             *
-             * @param data Pointer to the data
-             * @param size Number of objects of type T stored
-             * @throws std::system_error If munmap(2) call failed
-             */
-            static void unmap(T* data, size_t size) {
-                if (::munmap(reinterpret_cast<void*>(data), sizeof(T) * size) != 0) {
-                    throw std::system_error(errno, std::system_category(), "munmap failed");
-                }
-            }
-
-            /**
-             * Get number of objects of type T that would fit into a file.
-             *
-             * @param fd File descriptor
-             * @returns Number of objects of type T in this file
-             * @throws std::system_error If fstat(2) call failed
-             * @throws std::length_error If size of the file isn't a multiple of sizeof(T)
-             */
-            static size_t file_size(int fd) {
-                struct stat s;
-                if (fstat(fd, &s) < 0) {
-                    throw std::system_error(errno, std::system_category(), "fstat failed");
-                }
-                if (static_cast<size_t>(s.st_size) % sizeof(T) != 0) {
-                    throw std::length_error("file size has to be multiple of object size");
-                }
-                return static_cast<size_t>(s.st_size) / sizeof(T);
-            }
-
-            /**
-             * Grow file so there is enough space for at least new_size objects
-             * of type T. If the file is large enough already, nothing is done.
-             * The file is never shrunk.
-             *
-             * @param new_size Number of objects of type T that should fit into this file
-             * @param fd File descriptor
-             * @throws std::system_error If ftruncate(2) call failed
-             */
-            static void grow_file(size_t new_size, int fd) {
-                if (file_size(fd) < new_size) {
-                    if (::ftruncate(fd, static_cast_with_assert<off_t>(sizeof(T) * new_size)) < 0) {
-                        throw std::system_error(errno, std::system_category(), "ftruncate failed");
-                    }
-                }
-            }
-
-            /**
-             * Grow file to given size (if it is smaller) and mmap it.
-             *
-             * @param size Number of objects of type T that should fit into this file
-             * @param fd File descriptor
-             * @throws Errors thrown by grow_file() or map()
-             */
-            static T* grow_and_map(size_t size, int fd) {
-                grow_file(size, fd);
-                return map(size, fd, true);
-            }
-
-        }; // class typed_mmap
-
-    } // namespace detail
-
-} // namespace osmium
-
-#endif // OSMIUM_INDEX_DETAIL_TYPED_MMAP_HPP
diff --git a/include/osmium/index/detail/vector_map.hpp b/include/osmium/index/detail/vector_map.hpp
index 73c5a37..9c1cf4e 100644
--- a/include/osmium/index/detail/vector_map.hpp
+++ b/include/osmium/index/detail/vector_map.hpp
@@ -68,7 +68,7 @@ namespace osmium {
                     m_vector(fd) {
                 }
 
-                ~VectorBasedDenseMap() {}
+                ~VectorBasedDenseMap() = default;
 
                 void reserve(const size_t size) override final {
                     m_vector.reserve(size);
@@ -97,6 +97,10 @@ namespace osmium {
                     return m_vector.size();
                 }
 
+                size_t byte_size() const {
+                    return m_vector.size() * sizeof(element_type);
+                }
+
                 size_t used_memory() const override final {
                     return sizeof(TValue) * size();
                 }
@@ -106,6 +110,10 @@ namespace osmium {
                     m_vector.shrink_to_fit();
                 }
 
+                void dump_as_array(const int fd) override final {
+                    osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(m_vector.data()), byte_size());
+                }
+
                 iterator begin() {
                     return m_vector.begin();
                 }
diff --git a/include/osmium/index/detail/vector_multimap.hpp b/include/osmium/index/detail/vector_multimap.hpp
index c2b2e1f..1d875fc 100644
--- a/include/osmium/index/detail/vector_multimap.hpp
+++ b/include/osmium/index/detail/vector_multimap.hpp
@@ -67,6 +67,16 @@ namespace osmium {
 
             public:
 
+                VectorBasedSparseMultimap() :
+                    m_vector() {
+                }
+
+                explicit VectorBasedSparseMultimap(int fd) :
+                    m_vector(fd) {
+                }
+
+                ~VectorBasedSparseMultimap() = default;
+
                 void set(const TId id, const TValue value) override final {
                     m_vector.push_back(element_type(id, value));
                 }
@@ -141,6 +151,30 @@ namespace osmium {
                     osmium::io::detail::reliable_write(fd, reinterpret_cast<const char*>(m_vector.data()), byte_size());
                 }
 
+                iterator begin() {
+                    return m_vector.begin();
+                }
+
+                iterator end() {
+                    return m_vector.end();
+                }
+
+                const_iterator cbegin() const {
+                    return m_vector.cbegin();
+                }
+
+                const_iterator cend() const {
+                    return m_vector.cend();
+                }
+
+                const_iterator begin() const {
+                    return m_vector.cbegin();
+                }
+
+                const_iterator end() const {
+                    return m_vector.cend();
+                }
+
             }; // class VectorBasedSparseMultimap
 
         } // namespace multimap
diff --git a/include/osmium/index/index.hpp b/include/osmium/index/index.hpp
index b73b319..f415192 100644
--- a/include/osmium/index/index.hpp
+++ b/include/osmium/index/index.hpp
@@ -67,7 +67,7 @@ namespace osmium {
         template <typename TKey>
         OSMIUM_NORETURN void not_found_error(TKey key) {
             std::stringstream s;
-            s << "id " << key << " no found";
+            s << "id " << key << " not found";
             throw not_found(s.str());
         }
 
diff --git a/include/osmium/index/map.hpp b/include/osmium/index/map.hpp
index 7b44b8e..61af672 100644
--- a/include/osmium/index/map.hpp
+++ b/include/osmium/index/map.hpp
@@ -148,7 +148,11 @@ namespace osmium {
                 }
 
                 virtual void dump_as_list(const int /*fd*/) {
-                    std::runtime_error("can't dump as list");
+                    throw std::runtime_error("can't dump as list");
+                }
+
+                virtual void dump_as_array(const int /*fd*/) {
+                    throw std::runtime_error("can't dump as array");
                 }
 
             }; // class Map
@@ -195,6 +199,10 @@ namespace osmium {
                 return m_callbacks.emplace(map_type_name, func).second;
             }
 
+            bool has_map_type(const std::string& map_type_name) const {
+                return m_callbacks.count(map_type_name);
+            }
+
             std::vector<std::string> map_types() const {
                 std::vector<std::string> result;
 
@@ -242,9 +250,13 @@ namespace osmium {
             });
         }
 
+#define OSMIUM_CONCATENATE_DETAIL_(x, y) x##y
+#define OSMIUM_CONCATENATE_(x, y) OSMIUM_CONCATENATE_DETAIL_(x, y)
+#define OSMIUM_MAKE_UNIQUE_(x) OSMIUM_CONCATENATE_(x, __COUNTER__)
+
 #define REGISTER_MAP(id, value, klass, name) \
 namespace { \
-    const bool registered_index_map_##name = osmium::index::register_map<id, value, klass>(#name); \
+    const bool OSMIUM_MAKE_UNIQUE_(registered_index_map_##name) = osmium::index::register_map<id, value, klass>(#name); \
 }
 
     } // namespace index
diff --git a/include/osmium/index/map/sparse_mem_table.hpp b/include/osmium/index/map/sparse_mem_table.hpp
index 8998a91..09ee81b 100644
--- a/include/osmium/index/map/sparse_mem_table.hpp
+++ b/include/osmium/index/map/sparse_mem_table.hpp
@@ -83,7 +83,7 @@ namespace osmium {
                 *                  The storage will grow by at least this size
                 *                  every time it runs out of space.
                 */
-                explicit SparseMemTable(const TId grow_size=10000) :
+                explicit SparseMemTable(const TId grow_size = 10000) :
                     m_grow_size(grow_size),
                     m_elements(grow_size) {
                 }
@@ -123,7 +123,7 @@ namespace osmium {
 
                 void dump_as_list(const int fd) override final {
                     std::vector<std::pair<TId, TValue>> v;
-                    int n=0;
+                    int n = 0;
                     for (const TValue value : m_elements) {
                         if (value != osmium::index::empty_value<TValue>()) {
                             v.emplace_back(n, value);
diff --git a/include/osmium/io/bzip2_compression.hpp b/include/osmium/io/bzip2_compression.hpp
index de1364c..7e86c15 100644
--- a/include/osmium/io/bzip2_compression.hpp
+++ b/include/osmium/io/bzip2_compression.hpp
@@ -82,7 +82,7 @@ namespace osmium {
 
         namespace detail {
 
-            OSMIUM_NORETURN inline void throw_bzip2_error(BZFILE* bzfile, const char* msg, int bzlib_error=0) {
+            OSMIUM_NORETURN inline void throw_bzip2_error(BZFILE* bzfile, const char* msg, int bzlib_error = 0) {
                 std::string error("bzip2 error: ");
                 error += msg;
                 error += ": ";
diff --git a/include/osmium/io/detail/pbf.hpp b/include/osmium/io/detail/pbf.hpp
index e64e51a..f2975c5 100644
--- a/include/osmium/io/detail/pbf.hpp
+++ b/include/osmium/io/detail/pbf.hpp
@@ -35,8 +35,6 @@ DEALINGS IN THE SOFTWARE.
 
 #include <stdexcept>
 
-#include <osmpbf/osmpbf.h>
-
 // needed for htonl and ntohl
 #ifndef _WIN32
 # include <netinet/in.h>
@@ -45,38 +43,9 @@ DEALINGS IN THE SOFTWARE.
 #endif
 
 #include <osmium/io/error.hpp>
-#include <osmium/osm/item_type.hpp>
 
 namespace osmium {
 
-// avoid g++ false positive
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wreturn-type"
-    inline item_type osmpbf_membertype_to_item_type(const OSMPBF::Relation::MemberType mt) {
-        switch (mt) {
-            case OSMPBF::Relation::NODE:
-                return item_type::node;
-            case OSMPBF::Relation::WAY:
-                return item_type::way;
-            case OSMPBF::Relation::RELATION:
-                return item_type::relation;
-        }
-    }
-#pragma GCC diagnostic pop
-
-    inline OSMPBF::Relation::MemberType item_type_to_osmpbf_membertype(const item_type type) {
-        switch (type) {
-            case item_type::node:
-                return OSMPBF::Relation::NODE;
-            case item_type::way:
-                return OSMPBF::Relation::WAY;
-            case item_type::relation:
-                return OSMPBF::Relation::RELATION;
-            default:
-                throw std::runtime_error("Unknown relation member type");
-        }
-    }
-
     /**
      * Exception thrown when there was a problem with parsing the PBF format of
      * a file.
diff --git a/include/osmium/io/detail/pbf_input_format.hpp b/include/osmium/io/detail/pbf_input_format.hpp
index ac69cbd..93ba6ca 100644
--- a/include/osmium/io/detail/pbf_input_format.hpp
+++ b/include/osmium/io/detail/pbf_input_format.hpp
@@ -51,6 +51,7 @@ DEALINGS IN THE SOFTWARE.
 
 #include <osmium/io/detail/input_format.hpp>
 #include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
+#include <osmium/io/detail/pbf_type_conv.hpp>
 #include <osmium/io/detail/pbf_parser.hpp>
 #include <osmium/io/error.hpp>
 #include <osmium/io/file.hpp>
@@ -152,7 +153,7 @@ namespace osmium {
 
                 void parse_osm_data(osmium::osm_entity_bits::type read_types) {
                     osmium::thread::set_thread_name("_osmium_pbf_in");
-                    int n=0;
+                    int n = 0;
                     while (auto size = read_blob_header("OSMData")) {
 
                         if (m_use_thread_pool) {
diff --git a/include/osmium/io/detail/pbf_output_format.hpp b/include/osmium/io/detail/pbf_output_format.hpp
index acce67d..7afd2ee 100644
--- a/include/osmium/io/detail/pbf_output_format.hpp
+++ b/include/osmium/io/detail/pbf_output_format.hpp
@@ -112,6 +112,7 @@ More complete outlines of real .osm.pbf files can be created using the osmpbf-ou
 #include <osmium/handler.hpp>
 #include <osmium/io/detail/output_format.hpp>
 #include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
+#include <osmium/io/detail/pbf_type_conv.hpp>
 #include <osmium/io/detail/pbf_stringtable.hpp>
 #include <osmium/io/detail/zlib.hpp>
 #include <osmium/io/file.hpp>
@@ -129,6 +130,7 @@ More complete outlines of real .osm.pbf files can be created using the osmpbf-ou
 #include <osmium/osm/timestamp.hpp>
 #include <osmium/osm/way.hpp>
 #include <osmium/util/cast.hpp>
+#include <osmium/util/delta.hpp>
 #include <osmium/visitor.hpp>
 
 namespace osmium {
@@ -190,34 +192,6 @@ namespace osmium {
             class PBFOutputFormat : public osmium::io::detail::OutputFormat, public osmium::handler::Handler {
 
                 /**
-                 * This class models a variable that keeps track of the value
-                 * it was last set to and returns the delta between old and
-                 * new value from the update() call.
-                 */
-                template <typename T>
-                class Delta {
-
-                    T m_value;
-
-                public:
-
-                    Delta() :
-                        m_value(0) {
-                    }
-
-                    void clear() {
-                        m_value = 0;
-                    }
-
-                    T update(T new_value) {
-                        using std::swap;
-                        swap(m_value, new_value);
-                        return m_value - new_value;
-                    }
-
-                }; // class Delta
-
-                /**
                  * Maximum number of items in a primitive block.
                  *
                  * The uncompressed length of a Blob *should* be less
@@ -332,13 +306,13 @@ namespace osmium {
                  * delta-encoding while storing dense-nodes. It holds the last seen values
                  * from which the difference is stored into the protobuf.
                  */
-                Delta<int64_t> m_delta_id;
-                Delta<int64_t> m_delta_lat;
-                Delta<int64_t> m_delta_lon;
-                Delta<int64_t> m_delta_timestamp;
-                Delta<int64_t> m_delta_changeset;
-                Delta<int64_t> m_delta_uid;
-                Delta<::google::protobuf::int32> m_delta_user_sid;
+                osmium::util::DeltaEncode<int64_t> m_delta_id;
+                osmium::util::DeltaEncode<int64_t> m_delta_lat;
+                osmium::util::DeltaEncode<int64_t> m_delta_lon;
+                osmium::util::DeltaEncode<int64_t> m_delta_timestamp;
+                osmium::util::DeltaEncode<int64_t> m_delta_changeset;
+                osmium::util::DeltaEncode<int64_t> m_delta_uid;
+                osmium::util::DeltaEncode<::google::protobuf::int32> m_delta_user_sid;
 
                 bool debug;
 
@@ -355,7 +329,7 @@ namespace osmium {
                         if (dense->has_denseinfo()) {
                             OSMPBF::DenseInfo* denseinfo = dense->mutable_denseinfo();
 
-                            for (int i=0, l=denseinfo->user_sid_size(); i<l; ++i) {
+                            for (int i = 0, l=denseinfo->user_sid_size(); i<l; ++i) {
                                 auto user_sid = denseinfo->user_sid(i);
                                 denseinfo->set_user_sid(i, m_delta_user_sid.update(user_sid));
                             }
@@ -374,7 +348,7 @@ namespace osmium {
                     // test, if the node-block has been allocated
                     if (pbf_nodes) {
                         // iterate over all nodes, passing them to the map_common_string_ids function
-                        for (int i=0, l=pbf_nodes->nodes_size(); i<l; ++i) {
+                        for (int i = 0, l=pbf_nodes->nodes_size(); i<l; ++i) {
                             map_common_string_ids(pbf_nodes->mutable_nodes(i));
                         }
 
@@ -386,7 +360,7 @@ namespace osmium {
                             // in the densenodes structure keys and vals are encoded in an intermixed
                             // array, individual nodes are seperated by a value of 0 (0 in the StringTable
                             // is always unused). String-ids of 0 are thus kept alone.
-                            for (int i=0, l=dense->keys_vals_size(); i<l; ++i) {
+                            for (int i = 0, l=dense->keys_vals_size(); i<l; ++i) {
                                 // map interim string-ids > 0 to real string ids
                                 auto sid = dense->keys_vals(i);
                                 if (sid > 0) {
@@ -400,7 +374,7 @@ namespace osmium {
                                 OSMPBF::DenseInfo* denseinfo = dense->mutable_denseinfo();
 
                                 // iterate over all username string-ids
-                                for (int i=0, l=denseinfo->user_sid_size(); i<l; ++i) {
+                                for (int i = 0, l=denseinfo->user_sid_size(); i<l; ++i) {
                                     // map interim string-ids > 0 to real string ids
                                     auto user_sid = string_table.map_string_id(denseinfo->user_sid(i));
 
@@ -414,7 +388,7 @@ namespace osmium {
                     // test, if the ways-block has been allocated
                     if (pbf_ways) {
                         // iterate over all ways, passing them to the map_common_string_ids function
-                        for (int i=0, l=pbf_ways->ways_size(); i<l; ++i) {
+                        for (int i = 0, l=pbf_ways->ways_size(); i<l; ++i) {
                             map_common_string_ids(pbf_ways->mutable_ways(i));
                         }
                     }
@@ -422,7 +396,7 @@ namespace osmium {
                     // test, if the relations-block has been allocated
                     if (pbf_relations) {
                         // iterate over all relations
-                        for (int i=0, l=pbf_relations->relations_size(); i<l; ++i) {
+                        for (int i = 0, l=pbf_relations->relations_size(); i<l; ++i) {
                             // get a pointer to the relation
                             OSMPBF::Relation* relation = pbf_relations->mutable_relations(i);
 
@@ -431,7 +405,7 @@ namespace osmium {
 
                             // iterate over all relation members, mapping the interim string-ids
                             // of the role to real string ids
-                            for (int mi=0; mi < relation->roles_sid_size(); ++mi) {
+                            for (int mi = 0; mi < relation->roles_sid_size(); ++mi) {
                                 relation->set_roles_sid(mi, string_table.map_string_id(relation->roles_sid(mi)));
                             }
                         }
@@ -454,7 +428,7 @@ namespace osmium {
                     }
 
                     // iterate over all tags and map the interim-ids of the key and the value to real ids
-                    for (int i=0, l=in->keys_size(); i<l; ++i) {
+                    for (int i = 0, l=in->keys_size(); i<l; ++i) {
                         in->set_keys(i, string_table.map_string_id(in->keys(i)));
                         in->set_vals(i, string_table.map_string_id(in->vals(i)));
                     }
@@ -690,7 +664,7 @@ namespace osmium {
                     apply_common_info(way, pbf_way);
 
                     // last way-node-id used for delta-encoding
-                    Delta<int64_t> delta_id;
+                    osmium::util::DeltaEncode<int64_t> delta_id;
 
                     for (const auto& node_ref : way.nodes()) {
                         // copy the way-node-id, delta encoded
@@ -713,7 +687,7 @@ namespace osmium {
                     // copy the common meta-info from the osmium-object to the pbf-object
                     apply_common_info(relation, pbf_relation);
 
-                    Delta<int64_t> delta_id;
+                    osmium::util::DeltaEncode<int64_t> delta_id;
 
                     for (const auto& member : relation.members()) {
                         // record the relation-member role to the interim stringtable and copy the
diff --git a/include/osmium/io/detail/pbf_parser.hpp b/include/osmium/io/detail/pbf_parser.hpp
index d99819d..f626b0b 100644
--- a/include/osmium/io/detail/pbf_parser.hpp
+++ b/include/osmium/io/detail/pbf_parser.hpp
@@ -42,6 +42,7 @@ DEALINGS IN THE SOFTWARE.
 
 #include <osmium/builder/osm_object_builder.hpp>
 #include <osmium/io/detail/pbf.hpp> // IWYU pragma: export
+#include <osmium/io/detail/pbf_type_conv.hpp>
 #include <osmium/io/detail/zlib.hpp>
 #include <osmium/io/header.hpp>
 #include <osmium/osm/location.hpp>
@@ -106,7 +107,7 @@ namespace osmium {
                     m_date_factor = pbf_primitive_block.date_granularity() / 1000;
                     m_granularity = pbf_primitive_block.granularity();
 
-                    for (int i=0; i < pbf_primitive_block.primitivegroup_size(); ++i) {
+                    for (int i = 0; i < pbf_primitive_block.primitivegroup_size(); ++i) {
                         const OSMPBF::PrimitiveGroup& group = pbf_primitive_block.primitivegroup(i);
 
                         if (group.has_dense())  {
@@ -148,7 +149,7 @@ namespace osmium {
                 }
 
                 void parse_node_group(const OSMPBF::PrimitiveGroup& group) {
-                    for (int i=0; i < group.nodes_size(); ++i) {
+                    for (int i = 0; i < group.nodes_size(); ++i) {
                         osmium::builder::NodeBuilder builder(m_buffer);
                         const OSMPBF::Node& pbf_node = group.nodes(i);
                         parse_attributes(builder, pbf_node);
@@ -161,7 +162,7 @@ namespace osmium {
 
                         if (pbf_node.keys_size() > 0) {
                             osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
-                            for (int tag=0; tag < pbf_node.keys_size(); ++tag) {
+                            for (int tag = 0; tag < pbf_node.keys_size(); ++tag) {
                                 tl_builder.add_tag(m_stringtable->s(static_cast<int>(pbf_node.keys(tag))),
                                                    m_stringtable->s(static_cast<int>(pbf_node.vals(tag))));
                             }
@@ -172,7 +173,7 @@ namespace osmium {
                 }
 
                 void parse_way_group(const OSMPBF::PrimitiveGroup& group) {
-                    for (int i=0; i < group.ways_size(); ++i) {
+                    for (int i = 0; i < group.ways_size(); ++i) {
                         osmium::builder::WayBuilder builder(m_buffer);
                         const OSMPBF::Way& pbf_way = group.ways(i);
                         parse_attributes(builder, pbf_way);
@@ -180,7 +181,7 @@ namespace osmium {
                         if (pbf_way.refs_size() > 0) {
                             osmium::builder::WayNodeListBuilder wnl_builder(m_buffer, &builder);
                             int64_t ref = 0;
-                            for (int n=0; n < pbf_way.refs_size(); ++n) {
+                            for (int n = 0; n < pbf_way.refs_size(); ++n) {
                                 ref += pbf_way.refs(n);
                                 wnl_builder.add_node_ref(ref);
                             }
@@ -188,7 +189,7 @@ namespace osmium {
 
                         if (pbf_way.keys_size() > 0) {
                             osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
-                            for (int tag=0; tag < pbf_way.keys_size(); ++tag) {
+                            for (int tag = 0; tag < pbf_way.keys_size(); ++tag) {
                                 tl_builder.add_tag(m_stringtable->s(static_cast<int>(pbf_way.keys(tag))),
                                                    m_stringtable->s(static_cast<int>(pbf_way.vals(tag))));
                             }
@@ -199,7 +200,7 @@ namespace osmium {
                 }
 
                 void parse_relation_group(const OSMPBF::PrimitiveGroup& group) {
-                    for (int i=0; i < group.relations_size(); ++i) {
+                    for (int i = 0; i < group.relations_size(); ++i) {
                         osmium::builder::RelationBuilder builder(m_buffer);
                         const OSMPBF::Relation& pbf_relation = group.relations(i);
                         parse_attributes(builder, pbf_relation);
@@ -207,7 +208,7 @@ namespace osmium {
                         if (pbf_relation.types_size() > 0) {
                             osmium::builder::RelationMemberListBuilder rml_builder(m_buffer, &builder);
                             int64_t ref = 0;
-                            for (int n=0; n < pbf_relation.types_size(); ++n) {
+                            for (int n = 0; n < pbf_relation.types_size(); ++n) {
                                 ref += pbf_relation.memids(n);
                                 rml_builder.add_member(osmpbf_membertype_to_item_type(pbf_relation.types(n)), ref, m_stringtable->s(pbf_relation.roles_sid(n)));
                             }
@@ -215,7 +216,7 @@ namespace osmium {
 
                         if (pbf_relation.keys_size() > 0) {
                             osmium::builder::TagListBuilder tl_builder(m_buffer, &builder);
-                            for (int tag=0; tag < pbf_relation.keys_size(); ++tag) {
+                            for (int tag = 0; tag < pbf_relation.keys_size(); ++tag) {
                                 tl_builder.add_tag(m_stringtable->s(static_cast<int>(pbf_relation.keys(tag))),
                                                    m_stringtable->s(static_cast<int>(pbf_relation.vals(tag))));
                             }
@@ -264,7 +265,7 @@ namespace osmium {
 
                     const OSMPBF::DenseNodes& dense = group.dense();
 
-                    for (int i=0; i < dense.id_size(); ++i) {
+                    for (int i = 0; i < dense.id_size(); ++i) {
                         bool visible = true;
 
                         last_dense_id        += dense.id(i);
@@ -361,7 +362,7 @@ namespace osmium {
                 }
 
                 osmium::io::Header header;
-                for (int i=0; i < pbf_header_block.required_features_size(); ++i) {
+                for (int i = 0; i < pbf_header_block.required_features_size(); ++i) {
                     const std::string& feature = pbf_header_block.required_features(i);
 
                     if (feature == "OsmSchema-V0.6") continue;
@@ -377,7 +378,7 @@ namespace osmium {
                     throw osmium::pbf_error(std::string("required feature not supported: ") + feature);
                 }
 
-                for (int i=0; i < pbf_header_block.optional_features_size(); ++i) {
+                for (int i = 0; i < pbf_header_block.optional_features_size(); ++i) {
                     const std::string& feature = pbf_header_block.optional_features(i);
                     header.set("pbf_optional_feature_" + std::to_string(i), feature);
                 }
diff --git a/include/osmium/io/detail/pbf_stringtable.hpp b/include/osmium/io/detail/pbf_stringtable.hpp
index e3d6fe3..5f540f1 100644
--- a/include/osmium/io/detail/pbf_stringtable.hpp
+++ b/include/osmium/io/detail/pbf_stringtable.hpp
@@ -162,7 +162,7 @@ namespace osmium {
                                             return std::pair<string_info, std::string>(p.second, p.first);
                                     });
 
-                        string_id_type n=0;
+                        string_id_type n = 0;
 
                         for (const auto& mapping : sortedbycount) {
                             // add the string of the current item to the pbf StringTable
diff --git a/include/osmium/io/detail/pbf.hpp b/include/osmium/io/detail/pbf_type_conv.hpp
similarity index 79%
copy from include/osmium/io/detail/pbf.hpp
copy to include/osmium/io/detail/pbf_type_conv.hpp
index e64e51a..799869a 100644
--- a/include/osmium/io/detail/pbf.hpp
+++ b/include/osmium/io/detail/pbf_type_conv.hpp
@@ -1,5 +1,5 @@
-#ifndef OSMIUM_IO_DETAIL_PBF_HPP
-#define OSMIUM_IO_DETAIL_PBF_HPP
+#ifndef OSMIUM_IO_DETAIL_PBF_TYPE_CONV_HPP
+#define OSMIUM_IO_DETAIL_PBF_TYPE_CONV_HPP
 
 /*
 
@@ -33,18 +33,8 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
-#include <stdexcept>
-
 #include <osmpbf/osmpbf.h>
 
-// needed for htonl and ntohl
-#ifndef _WIN32
-# include <netinet/in.h>
-#else
-# include <winsock2.h>
-#endif
-
-#include <osmium/io/error.hpp>
 #include <osmium/osm/item_type.hpp>
 
 namespace osmium {
@@ -77,22 +67,7 @@ namespace osmium {
         }
     }
 
-    /**
-     * Exception thrown when there was a problem with parsing the PBF format of
-     * a file.
-     */
-    struct pbf_error : public io_error {
-
-        pbf_error(const std::string& what) :
-            io_error(std::string("PBF error: ") + what) {
-        }
-
-        pbf_error(const char* what) :
-            io_error(std::string("PBF error: ") + what) {
-        }
-
-    }; // struct pbf_error
 
 } // namespace osmium
 
-#endif // OSMIUM_IO_DETAIL_PBF_HPP
+#endif // OSMIUM_IO_DETAIL_PBF_TYPE_CONV_HPP
diff --git a/include/osmium/io/detail/read_write.hpp b/include/osmium/io/detail/read_write.hpp
index 6651cce..9863bd7 100644
--- a/include/osmium/io/detail/read_write.hpp
+++ b/include/osmium/io/detail/read_write.hpp
@@ -122,7 +122,7 @@ namespace osmium {
              * @throws std::system_error On error.
              */
             inline void reliable_write(const int fd, const unsigned char* output_buffer, const size_t size) {
-                constexpr size_t max_write = 100 * 1024 * 1024; // Max 100 MByte per write
+                constexpr size_t max_write = 100L * 1024L * 1024L; // Max 100 MByte per write
                 size_t offset = 0;
                 do {
                     auto write_count = size - offset;
diff --git a/include/osmium/io/detail/xml_input_format.hpp b/include/osmium/io/detail/xml_input_format.hpp
index c03f84d..a1c6666 100644
--- a/include/osmium/io/detail/xml_input_format.hpp
+++ b/include/osmium/io/detail/xml_input_format.hpp
@@ -66,6 +66,7 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/osm/location.hpp>
 #include <osmium/osm/object.hpp>
 #include <osmium/osm/types.hpp>
+#include <osmium/osm/types_from_string.hpp>
 #include <osmium/thread/queue.hpp>
 #include <osmium/thread/util.hpp>
 #include <osmium/util/cast.hpp>
diff --git a/include/osmium/io/detail/xml_output_format.hpp b/include/osmium/io/detail/xml_output_format.hpp
index 73a7263..65ba171 100644
--- a/include/osmium/io/detail/xml_output_format.hpp
+++ b/include/osmium/io/detail/xml_output_format.hpp
@@ -130,7 +130,7 @@ namespace osmium {
                 const bool m_write_change_ops;
 
                 void write_spaces(int num) {
-                    for (; num!=0; --num) {
+                    for (; num != 0; --num) {
                         *m_out += ' ';
                     }
                 }
diff --git a/include/osmium/io/gzip_compression.hpp b/include/osmium/io/gzip_compression.hpp
index 8bc1e5a..2723977 100644
--- a/include/osmium/io/gzip_compression.hpp
+++ b/include/osmium/io/gzip_compression.hpp
@@ -76,7 +76,7 @@ namespace osmium {
 
         namespace detail {
 
-            OSMIUM_NORETURN inline void throw_gzip_error(gzFile gzfile, const char* msg, int zlib_error=0) {
+            OSMIUM_NORETURN inline void throw_gzip_error(gzFile gzfile, const char* msg, int zlib_error = 0) {
                 std::string error("gzip error: ");
                 error += msg;
                 error += ": ";
diff --git a/include/osmium/io/reader.hpp b/include/osmium/io/reader.hpp
index 8d24652..c68a8e1 100644
--- a/include/osmium/io/reader.hpp
+++ b/include/osmium/io/reader.hpp
@@ -109,7 +109,7 @@ namespace osmium {
                 }
                 if (pid == 0) { // child
                     // close all file descriptors except one end of the pipe
-                    for (int i=0; i < 32; ++i) {
+                    for (int i = 0; i < 32; ++i) {
                         if (i != pipefd[1]) {
                             ::close(i);
                         }
diff --git a/include/osmium/memory/buffer.hpp b/include/osmium/memory/buffer.hpp
index 85a3a46..ecf8f33 100644
--- a/include/osmium/memory/buffer.hpp
+++ b/include/osmium/memory/buffer.hpp
@@ -414,6 +414,15 @@ namespace osmium {
             }
 
             template <class T>
+            t_iterator<T> get_iterator(size_t offset) {
+                return t_iterator<T>(m_data + offset, m_data + m_committed);
+            }
+
+            iterator get_iterator(size_t offset) {
+                return iterator(m_data + offset, m_data + m_committed);
+            }
+
+            template <class T>
             t_iterator<T> end() {
                 return t_iterator<T>(m_data + m_committed, m_data + m_committed);
             }
@@ -432,6 +441,15 @@ namespace osmium {
             }
 
             template <class T>
+            t_const_iterator<T> get_iterator(size_t offset) const {
+                return t_const_iterator<T>(m_data + offset, m_data + m_committed);
+            }
+
+            const_iterator get_iterator(size_t offset) const {
+                return const_iterator(m_data + offset, m_data + m_committed);
+            }
+
+            template <class T>
             t_const_iterator<T> cend() const {
                 return t_const_iterator<T>(m_data + m_committed, m_data + m_committed);
             }
diff --git a/include/osmium/memory/item.hpp b/include/osmium/memory/item.hpp
index 58d63df..2679ca6 100644
--- a/include/osmium/memory/item.hpp
+++ b/include/osmium/memory/item.hpp
@@ -118,7 +118,7 @@ namespace osmium {
 
         protected:
 
-            explicit Item(item_size_type size=0, item_type type=item_type()) noexcept :
+            explicit Item(item_size_type size = 0, item_type type = item_type()) noexcept :
                 m_size(size),
                 m_type(type),
                 m_removed(false),
diff --git a/include/osmium/osm/changeset.hpp b/include/osmium/osm/changeset.hpp
index 0ab4e9b..20def70 100644
--- a/include/osmium/osm/changeset.hpp
+++ b/include/osmium/osm/changeset.hpp
@@ -44,6 +44,7 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/osm/tag.hpp>
 #include <osmium/osm/timestamp.hpp>
 #include <osmium/osm/types.hpp>
+#include <osmium/osm/types_from_string.hpp>
 
 namespace osmium {
 
diff --git a/include/osmium/osm/diff_object.hpp b/include/osmium/osm/diff_object.hpp
index 55a5cef..1e053fd 100644
--- a/include/osmium/osm/diff_object.hpp
+++ b/include/osmium/osm/diff_object.hpp
@@ -112,8 +112,35 @@ namespace osmium {
             return m_curr->timestamp();
         }
 
+        /**
+         * Return the timestamp when the current version of the object is
+         * not valid any more, ie the time when the next version of the object
+         * is valid. If this is the last version of the object, this will
+         * return a special "end of time" timestamp that is guaranteed to
+         * be larger than any normal timestamp.
+         */
         const osmium::Timestamp end_time() const noexcept {
-            return last() ? osmium::Timestamp() : m_next->timestamp();
+            return last() ? osmium::end_of_time() : m_next->timestamp();
+        }
+
+        /**
+         * Current object version is valid between time "from" (inclusive) and
+         * time "to" (not inclusive).
+         *
+         * This is a bit more complex than you'd think, because we have to
+         * handle the case properly where the start_time() == end_time().
+         */
+        bool is_between(const osmium::Timestamp& from, const osmium::Timestamp& to) const noexcept {
+            return start_time() < to &&
+                   ((start_time() != end_time() && end_time() >  from) ||
+                    (start_time() == end_time() && end_time() >= from));
+        }
+
+        /**
+         * Current object version is visible at the given timestamp.
+         */
+        bool is_visible_at(const osmium::Timestamp& timestamp) const noexcept {
+            return start_time() <= timestamp && end_time() > timestamp && m_curr->visible();
         }
 
     }; // class DiffObject
diff --git a/include/osmium/osm/item_type.hpp b/include/osmium/osm/item_type.hpp
index c2187a3..54975e3 100644
--- a/include/osmium/osm/item_type.hpp
+++ b/include/osmium/osm/item_type.hpp
@@ -33,6 +33,7 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
+#include <cassert>
 #include <cstdint> // IWYU pragma: keep
 #include <iosfwd>
 #include <stdexcept>
@@ -56,6 +57,25 @@ namespace osmium {
 
     }; // enum class item_type
 
+    /**
+     * Return item_type for index:
+     * 0 -> node, 1 -> way, 2 -> relation
+     */
+    inline item_type nwr_index_to_item_type(unsigned int i) noexcept {
+        assert(i <= 2);
+        return item_type(i+1);
+    }
+
+    /**
+     * Return index for item_type:
+     * node -> 0, way -> 1, relation -> 2
+     */
+    inline unsigned int item_type_to_nwr_index(item_type type) noexcept {
+        unsigned int i = static_cast<unsigned int>(type);
+        assert(i >= 1 && i <= 3);
+        return i - 1;
+    }
+
     inline item_type char_to_item_type(const char c) noexcept {
         switch (c) {
             case 'X':
diff --git a/include/osmium/osm/node_ref.hpp b/include/osmium/osm/node_ref.hpp
index 1a66607..76afa75 100644
--- a/include/osmium/osm/node_ref.hpp
+++ b/include/osmium/osm/node_ref.hpp
@@ -54,7 +54,7 @@ namespace osmium {
 
     public:
 
-        NodeRef(const osmium::object_id_type ref=0, const osmium::Location& location=Location()) noexcept :
+        NodeRef(const osmium::object_id_type ref = 0, const osmium::Location& location = Location()) noexcept :
             m_ref(ref),
             m_location(location) {
         }
diff --git a/include/osmium/osm/object.hpp b/include/osmium/osm/object.hpp
index d5ae48a..8c745ce 100644
--- a/include/osmium/osm/object.hpp
+++ b/include/osmium/osm/object.hpp
@@ -48,6 +48,7 @@ DEALINGS IN THE SOFTWARE.
 #include <osmium/osm/tag.hpp>
 #include <osmium/osm/timestamp.hpp>
 #include <osmium/osm/types.hpp>
+#include <osmium/osm/types_from_string.hpp>
 
 namespace osmium {
 
diff --git a/include/osmium/osm/relation.hpp b/include/osmium/osm/relation.hpp
index 6c03815..99a4f4c 100644
--- a/include/osmium/osm/relation.hpp
+++ b/include/osmium/osm/relation.hpp
@@ -101,7 +101,7 @@ namespace osmium {
 
         static constexpr item_type collection_type = item_type::relation_member_list;
 
-        RelationMember(const object_id_type ref=0, const item_type type=item_type(), const bool full=false) noexcept :
+        RelationMember(const object_id_type ref = 0, const item_type type = item_type(), const bool full = false) noexcept :
             m_ref(ref),
             m_type(type),
             m_flags(full ? 1 : 0) {
@@ -120,6 +120,11 @@ namespace osmium {
             return static_cast<unsigned_object_id_type>(std::abs(m_ref));
         }
 
+        RelationMember& set_ref(const osmium::object_id_type ref) noexcept {
+            m_ref = ref;
+            return *this;
+        }
+
         item_type type() const noexcept {
             return m_type;
         }
diff --git a/include/osmium/osm/timestamp.hpp b/include/osmium/osm/timestamp.hpp
index f36ea36..e4c6807 100644
--- a/include/osmium/osm/timestamp.hpp
+++ b/include/osmium/osm/timestamp.hpp
@@ -42,6 +42,7 @@ DEALINGS IN THE SOFTWARE.
 #include <time.h>
 
 #include <osmium/util/compatibility.hpp>
+#include <osmium/util/minmax.hpp>
 
 namespace osmium {
 
@@ -113,6 +114,10 @@ namespace osmium {
             return static_cast<time_t>(m_timestamp);
         }
 
+        explicit constexpr operator uint32_t() const noexcept {
+            return m_timestamp;
+        }
+
         template <typename T>
         void operator+=(T time_difference) noexcept {
             m_timestamp += time_difference;
@@ -166,6 +171,16 @@ namespace osmium {
         return out;
     }
 
+    template <>
+    inline osmium::Timestamp min_op_start_value<osmium::Timestamp>() {
+        return end_of_time();
+    }
+
+    template <>
+    inline osmium::Timestamp max_op_start_value<osmium::Timestamp>() {
+        return start_of_time();
+    }
+
 } // namespace osmium
 
 #endif // OSMIUM_OSM_TIMESTAMP_HPP
diff --git a/include/osmium/osm/types.hpp b/include/osmium/osm/types.hpp
index aea61bd..b3414e5 100644
--- a/include/osmium/osm/types.hpp
+++ b/include/osmium/osm/types.hpp
@@ -34,7 +34,6 @@ DEALINGS IN THE SOFTWARE.
 */
 
 #include <cstdint>
-#include <cstdlib>
 
 namespace osmium {
 
@@ -58,26 +57,6 @@ namespace osmium {
      */
     typedef uint16_t string_size_type;
 
-    inline object_id_type string_to_object_id(const char* string) {
-        return std::atoll(string);
-    }
-
-    inline object_version_type string_to_object_version(const char* string) {
-        return static_cast<object_version_type>(std::atol(string));
-    }
-
-    inline changeset_id_type string_to_changeset_id(const char* string) {
-        return static_cast<changeset_id_type>(std::atol(string));
-    }
-
-    inline signed_user_id_type string_to_user_id(const char* string) {
-        return static_cast<signed_user_id_type>(std::atol(string));
-    }
-
-    inline num_changes_type string_to_num_changes(const char* string) {
-        return static_cast<num_changes_type>(std::atol(string));
-    }
-
 } // namespace osmium
 
 #endif // OSMIUM_OSM_TYPES_HPP
diff --git a/include/osmium/osm/types_from_string.hpp b/include/osmium/osm/types_from_string.hpp
new file mode 100644
index 0000000..b8de14c
--- /dev/null
+++ b/include/osmium/osm/types_from_string.hpp
@@ -0,0 +1,116 @@
+#ifndef OSMIUM_OSM_TYPES_FROM_STRING_HPP
+#define OSMIUM_OSM_TYPES_FROM_STRING_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 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 <cassert>
+#include <cctype>
+#include <cstdint>
+#include <cstdlib>
+#include <limits>
+#include <string>
+#include <utility>
+
+#include <osmium/osm/entity_bits.hpp>
+#include <osmium/osm/types.hpp>
+
+namespace osmium {
+
+    inline object_id_type string_to_object_id(const char* input) {
+        assert(input);
+        if (*input != '\0' && !std::isspace(*input)) {
+            char* end;
+            auto id = std::strtoll(input, &end, 10);
+            if (id != std::numeric_limits<long long>::min() && id != std::numeric_limits<long long>::max() && *end == '\0') {
+                return id;
+            }
+        }
+        throw std::range_error(std::string("illegal id: '") + input + "'");
+    }
+
+    inline std::pair<osmium::item_type, osmium::object_id_type> string_to_object_id(const char* input, osmium::osm_entity_bits::type types) {
+        assert(input);
+        assert(types != osmium::osm_entity_bits::nothing);
+        if (*input != '\0') {
+            if (std::isdigit(*input)) {
+                return std::make_pair(osmium::item_type::undefined, string_to_object_id(input));
+            }
+            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));
+            }
+        }
+        throw std::range_error(std::string("not a valid id: '") + input + "'");
+    }
+
+    namespace detail {
+
+        inline 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);
+                if (value != std::numeric_limits<unsigned long>::max() && *end == '\0') {
+                    return value;
+                }
+            }
+            throw std::range_error(std::string("illegal ") + name + ": '" + input + "'");
+        }
+
+    } // namespace detail
+
+    inline object_version_type string_to_object_version(const char* input) {
+        assert(input);
+        return static_cast<object_version_type>(detail::string_to_ulong(input, "version"));
+    }
+
+    inline changeset_id_type string_to_changeset_id(const char* input) {
+        assert(input);
+        return static_cast<changeset_id_type>(detail::string_to_ulong(input, "changeset"));
+    }
+
+    inline signed_user_id_type string_to_user_id(const char* input) {
+        assert(input);
+        if (input[0] == '-' && input[1] == '1' && input[2] == '\0') {
+            return -1;
+        }
+        return static_cast<signed_user_id_type>(detail::string_to_ulong(input, "user id"));
+    }
+
+    inline num_changes_type string_to_num_changes(const char* input) {
+        assert(input);
+        return static_cast<num_changes_type>(detail::string_to_ulong(input, "value for num changes"));
+    }
+
+} // namespace osmium
+
+#endif // OSMIUM_OSM_TYPES_FROM_STRING_HPP
diff --git a/include/osmium/relations/collector.hpp b/include/osmium/relations/collector.hpp
index d4af916..40e3773 100644
--- a/include/osmium/relations/collector.hpp
+++ b/include/osmium/relations/collector.hpp
@@ -389,7 +389,7 @@ namespace osmium {
 
                 RelationMeta relation_meta(offset);
 
-                int n=0;
+                int n = 0;
                 for (auto& member : m_relations_buffer.get<osmium::Relation>(offset).members()) {
                     if (static_cast<TCollector*>(this)->keep_member(relation_meta, member)) {
                         member_meta(member.type()).emplace_back(member.ref(), m_relations.size(), n);
@@ -512,7 +512,7 @@ namespace osmium {
                     double percent = static_cast<double>(size_before - size_after);
                     percent /= size_before;
                     percent *= 100;
-                    std::cerr << "PURGE (size before=" << size_before << " after=" << size_after << " purged=" << (size_before - size_after) << " / " << static_cast<int>(percent) << "%)\n";
+//                    std::cerr << "PURGE (size before=" << size_before << " after=" << size_after << " purged=" << (size_before - size_after) << " / " << static_cast<int>(percent) << "%)\n";
                     m_count_complete = 0;
                 }
             }
diff --git a/include/osmium/thread/pool.hpp b/include/osmium/thread/pool.hpp
index bc1e9a8..3916031 100644
--- a/include/osmium/thread/pool.hpp
+++ b/include/osmium/thread/pool.hpp
@@ -128,7 +128,7 @@ namespace osmium {
                 }
 
                 try {
-                    for (int i=0; i < m_num_threads; ++i) {
+                    for (int i = 0; i < m_num_threads; ++i) {
                         m_threads.push_back(std::thread(&Pool::worker_thread, this));
                     }
                 } catch (...) {
@@ -149,6 +149,7 @@ namespace osmium {
 
             ~Pool() {
                 m_done = true;
+                m_work_queue.shutdown();
             }
 
             size_t queue_size() const {
diff --git a/include/osmium/thread/queue.hpp b/include/osmium/thread/queue.hpp
index 12e5932..7fa6469 100644
--- a/include/osmium/thread/queue.hpp
+++ b/include/osmium/thread/queue.hpp
@@ -49,7 +49,7 @@ namespace osmium {
 
     namespace thread {
 
-        OSMIUM_CONSTEXPR std::chrono::milliseconds full_queue_sleep_duration { 10 }; // XXX
+        static const std::chrono::milliseconds full_queue_sleep_duration { 10 }; // XXX
 
         /**
          *  A thread-safe queue.
@@ -71,6 +71,8 @@ namespace osmium {
             /// Used to signal readers when data is available in the queue.
             std::condition_variable m_data_available;
 
+            std::atomic<bool> m_done;
+
 #ifdef OSMIUM_DEBUG_QUEUE_SIZE
             /// The largest size the queue has been so far.
             size_t m_largest_size;
@@ -94,7 +96,8 @@ namespace osmium {
                 m_name(name),
                 m_mutex(),
                 m_queue(),
-                m_data_available()
+                m_data_available(),
+                m_done(false)
 #ifdef OSMIUM_DEBUG_QUEUE_SIZE
                 ,
                 m_largest_size(0),
@@ -104,6 +107,7 @@ namespace osmium {
             }
 
             ~Queue() {
+                shutdown();
 #ifdef OSMIUM_DEBUG_QUEUE_SIZE
                 std::cerr << "queue '" << m_name << "' with max_size=" << m_max_size << " had largest size " << m_largest_size << " and was full " << m_full_counter << " times\n";
 #endif
@@ -132,24 +136,33 @@ namespace osmium {
                 m_data_available.notify_one();
             }
 
+            void shutdown() {
+                m_done = true;
+                m_data_available.notify_all();
+            }
+
             void wait_and_pop(T& value) {
                 std::unique_lock<std::mutex> lock(m_mutex);
                 m_data_available.wait(lock, [this] {
-                    return !m_queue.empty();
+                    return !m_queue.empty() || m_done;
                 });
-                value=std::move(m_queue.front());
-                m_queue.pop();
+                if (!m_queue.empty()) {
+                    value = std::move(m_queue.front());
+                    m_queue.pop();
+                }
             }
 
             void wait_and_pop_with_timeout(T& value) {
                 std::unique_lock<std::mutex> lock(m_mutex);
                 if (!m_data_available.wait_for(lock, std::chrono::seconds(1), [this] {
-                    return !m_queue.empty();
+                    return !m_queue.empty() || m_done;
                 })) {
                     return;
                 }
-                value=std::move(m_queue.front());
-                m_queue.pop();
+                if (!m_queue.empty()) {
+                    value = std::move(m_queue.front());
+                    m_queue.pop();
+                }
             }
 
             bool try_pop(T& value) {
@@ -157,7 +170,7 @@ namespace osmium {
                 if (m_queue.empty()) {
                     return false;
                 }
-                value=std::move(m_queue.front());
+                value = std::move(m_queue.front());
                 m_queue.pop();
                 return true;
             }
diff --git a/include/osmium/thread/sorted_queue.hpp b/include/osmium/thread/sorted_queue.hpp
index e0181eb..e76ade1 100644
--- a/include/osmium/thread/sorted_queue.hpp
+++ b/include/osmium/thread/sorted_queue.hpp
@@ -107,7 +107,7 @@ namespace osmium {
                 m_data_available.wait(lock, [this] {
                     return !empty_intern();
                 });
-                value=std::move(m_queue.front());
+                value = std::move(m_queue.front());
                 m_queue.pop_front();
                 ++m_offset;
             }
@@ -122,7 +122,7 @@ namespace osmium {
                 if (empty_intern()) {
                     return false;
                 }
-                value=std::move(m_queue.front());
+                value = std::move(m_queue.front());
                 m_queue.pop_front();
                 ++m_offset;
                 return true;
diff --git a/include/osmium/util/cast.hpp b/include/osmium/util/cast.hpp
index 80adadf..4866fde 100644
--- a/include/osmium/util/cast.hpp
+++ b/include/osmium/util/cast.hpp
@@ -37,6 +37,7 @@ DEALINGS IN THE SOFTWARE.
 # include <cassert>
 #endif
 
+#include <cstdint>
 #include <limits>
 #include <type_traits>
 
@@ -93,7 +94,7 @@ namespace osmium {
 
     template <typename T, typename F, typename std::enable_if<are_real_integers<T, F>::value && !std::is_same<T, F>::value && (sizeof(T) <= sizeof(F)) && std::is_signed<T>::value && std::is_unsigned<F>::value, int>::type = 0>
     inline T static_cast_with_assert(const F value) {
-        assert(value <= std::numeric_limits<T>::max());
+        assert(static_cast<int64_t>(value) <= static_cast<int64_t>(std::numeric_limits<T>::max()));
         return static_cast<T>(value);
     }
 
diff --git a/include/osmium/util/data_file.hpp b/include/osmium/util/data_file.hpp
new file mode 100644
index 0000000..3194988
--- /dev/null
+++ b/include/osmium/util/data_file.hpp
@@ -0,0 +1,192 @@
+#ifndef OSMIUM_UTIL_DATA_FILE_HPP
+#define OSMIUM_UTIL_DATA_FILE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 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 <cerrno>
+#include <cstdio>
+#include <string>
+#include <system_error>
+
+#ifdef _WIN32
+# include <io.h>
+# include <windows.h>
+#endif
+
+#include <osmium/util/file.hpp>
+
+namespace osmium {
+
+    namespace util {
+
+        /**
+         * Class wrapper for convenient access to some low-level file
+         * functions.
+         */
+        class DataFile {
+
+            FILE* m_file;
+
+        public:
+
+            /**
+             * Create and open a temporary file. It is removed after opening.
+             *
+             * @throws std::system_error if something went wrong.
+             */
+            DataFile() :
+                m_file(::tmpfile()) {
+                if (!m_file) {
+                    throw std::system_error(errno, std::system_category(), "tmpfile failed");
+                }
+            }
+
+            /**
+             * Create and open a temporary file with the specified size. It
+             * is removed after opening.
+             *
+             * @throws std::system_error if something went wrong.
+             */
+            explicit DataFile(size_t size) :
+                DataFile() {
+                grow(size);
+            }
+
+            /**
+             * Create and open a named file.
+             *
+             * @param filename the name of the file
+             * @param writable should the file be writable?
+             * @throws std::system_error if something went wrong.
+             */
+            DataFile(const char* filename, bool writable) :
+                m_file(::fopen(filename, writable ? "wb+" : "rb" )) {
+                if (!m_file) {
+                    throw std::system_error(errno, std::system_category(), "fopen failed");
+                }
+            }
+
+            /**
+             * Create and open a named file.
+             *
+             * @param filename the name of the file
+             * @param writable should the file be writable?
+             * @throws std::system_error if something went wrong.
+             */
+            DataFile(const std::string& filename, bool writable) :
+                DataFile(filename.c_str(), writable) {
+            }
+
+            /**
+             * In boolean context the DataFile class returns true if the file
+             * is open.
+             */
+            operator bool() const noexcept {
+                return m_file != nullptr;
+            }
+
+            /**
+             * Close the file.
+             *
+             * Does nothing if the file is already closed.
+             *
+             * @throws std::system_error if file could not be closed
+             */
+            void close() {
+                if (m_file) {
+                    if (::fclose(m_file) != 0) {
+                        throw std::system_error(errno, std::system_category(), "fclose failed");
+                    }
+                    m_file = nullptr;
+                }
+            }
+
+            ~DataFile() noexcept {
+                try {
+                    close();
+                } catch (std::system_error&) {
+                    // ignore
+                }
+            }
+
+            /**
+             * Get file descriptor of underlying file.
+             *
+             * @throws std::runtime_errro if file is not open
+             * @throws std::system_error if fileno(3) call failed
+             */
+            int fd() const {
+                if (!m_file) {
+                    throw std::runtime_error("no open file");
+                }
+
+                int fd = ::fileno(m_file);
+
+                if (fd == -1) {
+                    throw std::system_error(errno, std::system_category(), "fileno failed");
+                }
+
+                return fd;
+            }
+
+            /**
+             * Ask the operating system for the size of this file.
+             *
+             * @throws std::system_error if fstat(2) call failed
+             */
+            size_t size() const {
+                return osmium::util::file_size(fd());
+            }
+
+            /**
+             * Grow file to given size.
+             *
+             * If the file is large enough already, nothing is done.
+             * The file is never shrunk.
+             *
+             * @throws std::system_error if ftruncate(2) call failed
+             */
+            void grow(size_t new_size) const {
+                if (size() < new_size) {
+                    osmium::util::resize_file(fd(), new_size);
+                }
+            }
+
+        }; // class DataFile
+
+    } // namespace util
+
+} // namespace osmium
+
+
+#endif // OSMIUM_UTIL_DATA_FILE_HPP
diff --git a/include/osmium/index/detail/mmap_vector_anon.hpp b/include/osmium/util/delta.hpp
similarity index 58%
copy from include/osmium/index/detail/mmap_vector_anon.hpp
copy to include/osmium/util/delta.hpp
index 0ea4f9d..dd733ce 100644
--- a/include/osmium/index/detail/mmap_vector_anon.hpp
+++ b/include/osmium/util/delta.hpp
@@ -1,5 +1,5 @@
-#ifndef OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
-#define OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
+#ifndef OSMIUM_UTIL_DELTA_HPP
+#define OSMIUM_UTIL_DELTA_HPP
 
 /*
 
@@ -33,46 +33,65 @@ DEALINGS IN THE SOFTWARE.
 
 */
 
-#ifdef __linux__
+#include <utility>
 
-#include <cstddef>
+namespace osmium {
 
-#include <osmium/index/detail/typed_mmap.hpp>
-#include <osmium/index/detail/mmap_vector_base.hpp>
+    namespace util {
 
-namespace osmium {
+        /**
+         * Helper class for delta encoding.
+         */
+        template <typename T>
+        class DeltaEncode {
+
+            T m_value;
+
+        public:
+
+            DeltaEncode() :
+                m_value(0) {
+            }
+
+            void clear() {
+                m_value = 0;
+            }
+
+            T update(T new_value) {
+                using std::swap;
+                swap(m_value, new_value);
+                return m_value - new_value;
+            }
 
-    namespace detail {
+        }; // class DeltaEncode
 
         /**
-         * This class looks and behaves like STL vector, but uses mmap internally.
+         * Helper class for delta decoding.
          */
         template <typename T>
-        class mmap_vector_anon : public mmap_vector_base<T, mmap_vector_anon> {
+        class DeltaDecode {
+
+            T m_value;
 
         public:
 
-            mmap_vector_anon() :
-                mmap_vector_base<T, osmium::detail::mmap_vector_anon>(
-                    -1,
-                    osmium::detail::mmap_vector_size_increment,
-                    0,
-                    osmium::detail::typed_mmap<T>::map(osmium::detail::mmap_vector_size_increment)) {
+            DeltaDecode() :
+                m_value(0) {
+            }
+
+            void clear() {
+                m_value = 0;
             }
 
-            void reserve(size_t new_capacity) {
-                if (new_capacity > this->capacity()) {
-                    this->data(osmium::detail::typed_mmap<T>::remap(this->data(), this->capacity(), new_capacity));
-                    this->m_capacity = new_capacity;
-                }
+            T update(T delta) {
+                m_value += delta;
+                return m_value;
             }
 
-        }; // class mmap_vector_anon
+        }; // class DeltaDecode
 
-    } // namespace detail
+    } // namespace util
 
 } // namespace osmium
 
-#endif // __linux__
-
-#endif // OSMIUM_INDEX_DETAIL_MMAP_VECTOR_ANON_HPP
+#endif // OSMIUM_UTIL_DELTA_HPP
diff --git a/include/osmium/util/file.hpp b/include/osmium/util/file.hpp
new file mode 100644
index 0000000..afc595b
--- /dev/null
+++ b/include/osmium/util/file.hpp
@@ -0,0 +1,118 @@
+#ifndef OSMIUM_UTIL_FILE_HPP
+#define OSMIUM_UTIL_FILE_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 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 <cerrno>
+#include <cstdio>
+#include <system_error>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef _WIN32
+# include <io.h>
+# include <windows.h>
+#endif
+
+#ifndef _MSC_VER
+# include <unistd.h>
+#else
+// https://msdn.microsoft.com/en-us/library/whx354w1.aspx
+# define ftruncate _chsize_s
+#endif
+
+namespace osmium {
+
+    namespace util {
+
+        /**
+         * Get file size.
+         * This is a small wrapper around a system call.
+         *
+         * @param fd File descriptor
+         * @returns file size
+         * @throws std::system_error If system call failed
+         */
+        inline size_t file_size(int fd) {
+#ifdef _MSC_VER
+            // Windows implementation
+            // https://msdn.microsoft.com/en-us/library/dfbc2kec.aspx
+            auto size = ::_filelengthi64(fd);
+            if (size == -1L) {
+                throw std::system_error(errno, std::system_category(), "_filelengthi64 failed");
+            }
+            return size_t(size);
+#else
+            // Unix implementation
+            struct stat s;
+            if (::fstat(fd, &s) != 0) {
+                throw std::system_error(errno, std::system_category(), "fstat failed");
+            }
+            return size_t(s.st_size);
+#endif
+        }
+
+        /**
+         * Resize file.
+         * Small wrapper around ftruncate(2) system call.
+         *
+         * @param fd File descriptor
+         * @param new_size New size
+         * @throws std::system_error If ftruncate(2) call failed
+         */
+        inline void resize_file(int fd, size_t new_size) {
+            if (::ftruncate(fd, new_size) != 0) {
+                throw std::system_error(errno, std::system_category(), "ftruncate failed");
+            }
+        }
+
+        /**
+         * Get the page size for this system.
+         */
+        inline size_t get_pagesize() {
+#ifdef _WIN32
+            // Windows implementation
+            SYSTEM_INFO si;
+            GetSystemInfo(&si);
+            return si.dwPageSize;
+#else
+            // Unix implementation
+            return ::sysconf(_SC_PAGESIZE);
+#endif
+        }
+
+    } // namespace util
+
+} // namespace osmium
+
+#endif // OSMIUM_UTIL_FILE_HPP
diff --git a/include/osmium/util/memory_mapping.hpp b/include/osmium/util/memory_mapping.hpp
new file mode 100644
index 0000000..3880000
--- /dev/null
+++ b/include/osmium/util/memory_mapping.hpp
@@ -0,0 +1,723 @@
+#ifndef OSMIUM_UTIL_MEMORY_MAPPING_HPP
+#define OSMIUM_UTIL_MEMORY_MAPPING_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 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 <cassert>
+#include <cerrno>
+#include <stdexcept>
+#include <system_error>
+
+#include <osmium/util/file.hpp>
+
+#ifndef _WIN32
+# include <sys/mman.h>
+#else
+# include <io.h>
+# include <windows.h>
+# include <sys/types.h>
+#endif
+
+namespace osmium {
+
+    namespace util {
+
+        /**
+         * Class for wrapping memory mapping system calls.
+         *
+         * Usage for anonymous mapping:
+         * @code
+         * MemoryMapping mapping(1024);          // create anonymous mapping with size
+         * auto ptr = mapping.get_addr<char*>(); // get pointer to memory
+         * mapping.unmap();                      // release mapping by calling unmap() (or at end of scope)
+         * @endcode
+         *
+         * Or for file-backed mapping:
+         * @code
+         * int fd = ::open(...);
+         * {
+         *     MemoryMapping mapping(1024, true, fd, offset);
+         *     // use mapping
+         * }
+         * ::close(fd);
+         * @endcode
+         *
+         * If the file backing a file-backed mapping is not large enough, it
+         * will be resized. This works, of course, only for writable files,
+         * so for read-only files you have to make sure they are large enough
+         * for any mapping you want.
+         *
+         * If you ask for a zero-sized mapping, a mapping of the systems page
+         * size will be created instead. For file-backed mapping this will only
+         * work if the file is writable.
+         *
+         * There are different implementations for Unix and Windows systems.
+         * On Unix systems this wraps the mmap(), munmap(), and the mremap()
+         * system calls. On Windows it wraps the CreateFileMapping(),
+         * CloseHandle(), MapViewOfFile(), and UnmapViewOfFile() functions.
+         */
+        class MemoryMapping {
+
+            /// The size of the mapping
+            size_t m_size;
+
+            /// Offset into the file
+            off_t m_offset;
+
+            /// File handle we got the mapping from
+            int m_fd;
+
+            /// Is the memory writable?
+            bool m_writable;
+
+#ifdef _WIN32
+            HANDLE m_handle;
+#endif
+
+            /// The address where the memory is mapped
+            void* m_addr;
+
+            bool is_valid() const noexcept;
+
+            void make_invalid() noexcept;
+
+#ifdef _WIN32
+            typedef DWORD flag_type;
+#else
+            typedef int flag_type;
+#endif
+
+            flag_type get_protection() const noexcept;
+
+            flag_type get_flags() const noexcept;
+
+            // A zero-sized mapping is not allowed by the operating system.
+            // So if the user asks for a mapping of size 0, we map a full
+            // page instead. This way we don't have a special case in the rest
+            // of the code.
+            static size_t initial_size(size_t size) {
+                if (size == 0) {
+                    return osmium::util::get_pagesize();
+                }
+                return size;
+            }
+
+#ifdef _WIN32
+            HANDLE get_handle() const noexcept;
+            HANDLE osmium::util::MemoryMapping::create_file_mapping() const noexcept;
+            void* osmium::util::MemoryMapping::map_view_of_file() const noexcept;
+#endif
+
+            int resize_fd(int fd) {
+                // Anonymous mapping doesn't need resizing.
+                if (fd == -1) {
+                    return -1;
+                }
+
+                // Make sure the file backing this mapping is large enough.
+                if (osmium::util::file_size(fd) < m_size + m_offset) {
+                    osmium::util::resize_file(fd, m_size + m_offset);
+                }
+                return fd;
+            }
+
+        public:
+
+            /**
+             * Create memory mapping of given size.
+             *
+             * If fd is not set (or fd == -1), an anonymous mapping will be
+             * created, otherwise a mapping based on the file descriptor will
+             * be created.
+             *
+             * @pre size > 0 or writable==true
+             *
+             * @param size Size of the mapping in bytes
+             * @param writable Should the mapping be writable?
+             * @param fd Open file descriptor of a file we want to map
+             * @param offset Offset into the file where the mapping should start
+             * @throws std::system_error if the mapping fails
+             */
+            MemoryMapping(size_t size, bool writable=true, int fd=-1, off_t offset=0);
+
+            /// You can not copy construct a MemoryMapping.
+            MemoryMapping(const MemoryMapping&) = delete;
+
+            /// You can not copy a MemoryMapping.
+            MemoryMapping& operator=(const MemoryMapping&) = delete;
+
+            /**
+             * Move construct a mapping from another one. The other mapping
+             * will be marked as invalid.
+             */
+            MemoryMapping(MemoryMapping&& other);
+
+            /**
+             * Move a mapping. The other mapping will be marked as invalid.
+             */
+            MemoryMapping& operator=(MemoryMapping&& other);
+
+            /**
+             * Releases the mapping by calling unmap(). Will never throw.
+             * Call unmap() instead if you want to be notified of any error.
+             */
+            ~MemoryMapping() noexcept {
+                try {
+                    unmap();
+                } catch (std::system_error&) {
+                    // ignore
+                }
+            }
+
+            /**
+             * Unmap a mapping. If the mapping is not valid, it will do
+             * nothing.
+             *
+             * @throws std::system_error if the unmapping fails
+             */
+            void unmap();
+
+            /**
+             * Resize a mapping to the given new size.
+             *
+             * On Linux systems this will use the mremap() function. On other
+             * systems it will unmap and remap the memory. This can only be
+             * done for file-based mappings, not anonymous mappings!
+             *
+             * @param new_size Number of bytes to resize to
+             * @throws std::system_error if the remapping fails
+             */
+            void resize(size_t new_size);
+
+            /**
+             * In a boolean context a MemoryMapping is true when it is a valid
+             * existing mapping.
+             */
+            operator bool() const noexcept {
+                return is_valid();
+            }
+
+            /**
+             * The number of bytes mapped. This is the same size you created
+             * the mapping with. The actual mapping will probably be larger
+             * because the system will round it to the page size.
+             */
+            size_t size() const noexcept {
+                return m_size;
+            }
+
+            /**
+             * The file descriptor this mapping was created from.
+             *
+             * @returns file descriptor, -1 for anonymous mappings
+             */
+            int fd() const noexcept {
+                return m_fd;
+            }
+
+            /**
+             * Was this mapping created as a writable mapping?
+             */
+            bool writable() const noexcept {
+                return m_writable;
+            }
+
+            /**
+             * Get the address of the mapping as any pointer type you like.
+             *
+             * @throws std::runtime_error if the mapping is invalid
+             */
+            template <typename T = void>
+            T* get_addr() const {
+                if (is_valid()) {
+                    return reinterpret_cast<T*>(m_addr);
+                }
+                throw std::runtime_error("invalid memory mapping");
+            }
+
+        }; // class MemoryMapping
+
+        /**
+         * Anonymous memory mapping.
+         *
+         * Usage for anonymous mapping:
+         * @code
+         * AnonymousMemoryMapping mapping(1024); // create anonymous mapping with size
+         * auto ptr = mapping.get_addr<char*>(); // get pointer to memory
+         * mapping.unmap();                      // release mapping by calling unmap() (or at end of scope)
+         * @endcode
+         */
+        class AnonymousMemoryMapping : public MemoryMapping {
+
+        public:
+
+            AnonymousMemoryMapping(size_t size) :
+                MemoryMapping(size) {
+            }
+
+#ifndef __linux__
+            /**
+             * On systems other than Linux anonymous mappings can not be
+             * resized!
+             */
+            void resize(size_t) = delete;
+#endif
+
+        }; // class AnonymousMemoryMapping
+
+        /**
+         * A thin wrapper around the MemoryMapping class used when all the
+         * data in the mapped memory is of the same type. Instead of thinking
+         * about the number of bytes mapped, this counts sizes in the number
+         * of objects of that type.
+         *
+         * Note that no effort is made to actually initialize the objects in
+         * this memory. This has to be done by the caller!
+         */
+        template <typename T>
+        class TypedMemoryMapping {
+
+            MemoryMapping m_mapping;
+
+        public:
+
+            /**
+             * Create anonymous memory mapping of given size.
+             *
+             * @param size Number of objects of type T to be mapped
+             * @throws std::system_error if the mapping fails
+             */
+            TypedMemoryMapping(size_t size) :
+                m_mapping(sizeof(T) * size) {
+            }
+
+            /**
+             * Create file-backed memory mapping of given size. The file must
+             * contain at least `sizeof(T) * size` bytes!
+             *
+             * @param size Number of objects of type T to be mapped
+             * @param writable Should the mapping be writable?
+             * @param fd Open file descriptor of a file we want to map
+             * @param offset Offset into the file where the mapping should start
+             * @throws std::system_error if the mapping fails
+             */
+            TypedMemoryMapping(size_t size, bool writable, int fd, off_t offset = 0) :
+                m_mapping(sizeof(T) * size, writable, fd, sizeof(T) * offset) {
+            }
+
+            /// You can not copy construct a TypedMemoryMapping.
+            TypedMemoryMapping(const TypedMemoryMapping&) = delete;
+
+            /// You can not copy a MemoryMapping.
+            TypedMemoryMapping& operator=(const TypedMemoryMapping&) = delete;
+
+            /**
+             * Move construct a mapping from another one. The other mapping
+             * will be marked as invalid.
+             */
+            TypedMemoryMapping(TypedMemoryMapping&& other) = default;
+
+            /**
+             * Move a mapping. The other mapping will be marked as invalid.
+             */
+            TypedMemoryMapping& operator=(TypedMemoryMapping&& other) = default;
+
+            /**
+             * Releases the mapping by calling unmap(). Will never throw.
+             * Call unmap() instead if you want to be notified of any error.
+             */
+            ~TypedMemoryMapping() = default;
+
+            /**
+             * Unmap a mapping. If the mapping is not valid, it will do
+             * nothing.
+             *
+             * @throws std::system_error if the unmapping fails
+             */
+            void unmap() {
+                m_mapping.unmap();
+            }
+
+            /**
+             * Resize a mapping to the given new size.
+             *
+             * On Linux systems this will use the mremap() function. On other
+             * systems it will unmap and remap the memory. This can only be
+             * done for file-based mappings, not anonymous mappings!
+             *
+             * @param new_size Number of objects of type T to resize to
+             * @throws std::system_error if the remapping fails
+             */
+            void resize(size_t new_size) {
+                m_mapping.resize(sizeof(T) * new_size);
+            }
+
+            /**
+             * In a boolean context a TypedMemoryMapping is true when it is
+             * a valid existing mapping.
+             */
+            operator bool() const noexcept {
+                return !!m_mapping;
+            }
+
+            /**
+             * The number of objects of class T mapped. This is the same size
+             * you created the mapping with. The actual mapping will probably
+             * be larger because the system will round it to the page size.
+             */
+            size_t size() const noexcept {
+                assert(m_mapping.size() % sizeof(T) == 0);
+                return m_mapping.size() / sizeof(T);
+            }
+
+            /**
+             * The file descriptor this mapping was created from.
+             *
+             * @returns file descriptor, -1 for anonymous mappings
+             */
+            int fd() const noexcept {
+                return m_mapping.fd();
+            }
+
+            /**
+             * Was this mapping created as a writable mapping?
+             */
+            bool writable() const noexcept {
+                return m_mapping.writable();
+            }
+
+            /**
+             * Get the address of the beginning of the mapping.
+             *
+             * @throws std::runtime_error if the mapping is invalid
+             */
+            T* begin() {
+                return m_mapping.get_addr<T>();
+            }
+
+            /**
+             * Get the address one past the end of the mapping.
+             *
+             * @throws std::runtime_error if the mapping is invalid
+             */
+            T* end() {
+                return m_mapping.get_addr<T>() + size();
+            }
+
+            const T* cbegin() const {
+                return m_mapping.get_addr<T>();
+            }
+
+            const T* cend() const {
+                return m_mapping.get_addr<T>() + size();
+            }
+
+            const T* begin() const {
+                return m_mapping.get_addr<T>();
+            }
+
+            const T* end() const {
+                return m_mapping.get_addr<T>() + size();
+            }
+
+        }; // class TypedMemoryMapping
+
+        template <typename T>
+        class AnonymousTypedMemoryMapping : public TypedMemoryMapping<T> {
+
+        public:
+
+            AnonymousTypedMemoryMapping(size_t size) :
+                TypedMemoryMapping<T>(size) {
+            }
+
+#ifndef __linux__
+            /**
+             * On systems other than Linux anonymous mappings can not be
+             * resized!
+             */
+            void resize(size_t) = delete;
+#endif
+
+        }; // class AnonymousTypedMemoryMapping
+
+    } // namespace util
+
+} // namespace osmium
+
+#ifndef _WIN32
+
+// =========== Unix implementation =============
+
+// MAP_FAILED is often a macro containing an old style cast
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+
+inline bool osmium::util::MemoryMapping::is_valid() const noexcept {
+    return m_addr != MAP_FAILED;
+}
+
+inline void osmium::util::MemoryMapping::make_invalid() noexcept {
+    m_addr = MAP_FAILED;
+}
+
+#pragma GCC diagnostic pop
+
+// for BSD systems
+#ifndef MAP_ANONYMOUS
+# define MAP_ANONYMOUS MAP_ANON
+#endif
+
+inline int osmium::util::MemoryMapping::get_protection() const noexcept {
+    if (m_writable) {
+        return PROT_READ | PROT_WRITE;
+    }
+    return PROT_READ;
+}
+
+inline int osmium::util::MemoryMapping::get_flags() const noexcept {
+    if (m_fd == -1) {
+        return MAP_PRIVATE | MAP_ANONYMOUS;
+    }
+    return MAP_SHARED;
+}
+
+inline osmium::util::MemoryMapping::MemoryMapping(size_t size, bool writable, int fd, off_t offset) :
+    m_size(initial_size(size)),
+    m_offset(offset),
+    m_fd(resize_fd(fd)),
+    m_writable(writable),
+    m_addr(::mmap(nullptr, m_size, get_protection(), get_flags(), m_fd, m_offset)) {
+    assert(writable || fd != -1);
+    if (!is_valid()) {
+        throw std::system_error(errno, std::system_category(), "mmap failed");
+    }
+}
+
+inline osmium::util::MemoryMapping::MemoryMapping(MemoryMapping&& other) :
+    m_size(other.m_size),
+    m_offset(other.m_offset),
+    m_fd(other.m_fd),
+    m_writable(other.m_writable),
+    m_addr(other.m_addr) {
+    other.make_invalid();
+}
+
+inline osmium::util::MemoryMapping& osmium::util::MemoryMapping::operator=(osmium::util::MemoryMapping&& other) {
+    unmap();
+    m_size     = other.m_size;
+    m_offset   = other.m_offset;
+    m_fd       = other.m_fd;
+    m_writable = other.m_writable;
+    m_addr     = other.m_addr;
+    other.make_invalid();
+    return *this;
+}
+
+inline void osmium::util::MemoryMapping::unmap() {
+    if (is_valid()) {
+        if (::munmap(m_addr, m_size) != 0) {
+            throw std::system_error(errno, std::system_category(), "munmap failed");
+        }
+        make_invalid();
+    }
+}
+
+inline void osmium::util::MemoryMapping::resize(size_t new_size) {
+    assert(new_size > 0 && "can not resize to zero size");
+    if (m_fd == -1) { // anonymous mapping
+#ifdef __linux__
+        m_addr = ::mremap(m_addr, m_size, new_size, MREMAP_MAYMOVE);
+        if (!is_valid()) {
+            throw std::system_error(errno, std::system_category(), "mremap failed");
+        }
+        m_size = new_size;
+#else
+        assert(false && "can't resize anonymous mappings on non-linux systems");
+#endif
+    } else { // file-based mapping
+        unmap();
+        m_size = new_size;
+        resize_fd(m_fd);
+        m_addr = ::mmap(nullptr, new_size, get_protection(), get_flags(), m_fd, m_offset);
+        if (!is_valid()) {
+            throw std::system_error(errno, std::system_category(), "mmap (remap) failed");
+        }
+    }
+}
+
+#else
+
+// =========== Windows implementation =============
+
+/* References:
+ * CreateFileMapping: http://msdn.microsoft.com/en-us/library/aa366537(VS.85).aspx
+ * CloseHandle:       http://msdn.microsoft.com/en-us/library/ms724211(VS.85).aspx
+ * MapViewOfFile:     http://msdn.microsoft.com/en-us/library/aa366761(VS.85).aspx
+ * UnmapViewOfFile:   http://msdn.microsoft.com/en-us/library/aa366882(VS.85).aspx
+ */
+
+namespace osmium {
+
+    namespace util {
+
+        inline DWORD dword_hi(uint64_t x) {
+            return static_cast<DWORD>(x >> 32);
+        }
+
+        inline DWORD dword_lo(uint64_t x) {
+            return static_cast<DWORD>(x & 0xffffffff);
+        }
+
+    } // namespace util
+
+} // namespace osmium
+
+inline DWORD osmium::util::MemoryMapping::get_protection() const noexcept {
+    if (m_writable) {
+        return PAGE_READWRITE;
+    }
+    return PAGE_READONLY;
+}
+
+inline DWORD osmium::util::MemoryMapping::get_flags() const noexcept {
+    if (m_fd == -1) {
+        return FILE_MAP_WRITE | FILE_MAP_COPY;
+    }
+    if (m_writable) {
+        return FILE_MAP_WRITE;
+    }
+    return FILE_MAP_READ;
+}
+
+inline HANDLE osmium::util::MemoryMapping::get_handle() const noexcept {
+    if (m_fd == -1) {
+        return INVALID_HANDLE_VALUE;
+    }
+    return reinterpret_cast<HANDLE>(_get_osfhandle(m_fd));
+}
+
+inline HANDLE osmium::util::MemoryMapping::create_file_mapping() const noexcept {
+    return CreateFileMapping(get_handle(), nullptr, get_protection(), osmium::util::dword_hi(static_cast<uint64_t>(m_size) + m_offset), osmium::util::dword_lo(static_cast<uint64_t>(m_size) + m_offset), nullptr);
+}
+
+inline void* osmium::util::MemoryMapping::map_view_of_file() const noexcept {
+    return MapViewOfFile(m_handle, get_flags(), osmium::util::dword_hi(m_offset), osmium::util::dword_lo(m_offset), m_size);
+}
+
+inline bool osmium::util::MemoryMapping::is_valid() const noexcept {
+    return m_addr != nullptr;
+}
+
+inline void osmium::util::MemoryMapping::make_invalid() noexcept {
+    m_addr = nullptr;
+}
+
+inline osmium::util::MemoryMapping::MemoryMapping(size_t size, bool writable, int fd, off_t offset) :
+    m_size(initial_size(size)),
+    m_offset(offset),
+    m_fd(resize_fd(fd)),
+    m_writable(writable),
+    m_handle(create_file_mapping()),
+    m_addr(nullptr) {
+
+    if (!m_handle) {
+        throw std::system_error(GetLastError(), std::system_category(), "CreateFileMapping failed");
+    }
+
+    m_addr = map_view_of_file();
+    if (!is_valid()) {
+        throw std::system_error(GetLastError(), std::system_category(), "MapViewOfFile failed");
+    }
+}
+
+inline osmium::util::MemoryMapping::MemoryMapping(MemoryMapping&& other) :
+    m_size(other.m_size),
+    m_offset(other.m_offset),
+    m_fd(other.m_fd),
+    m_writable(other.m_writable),
+    m_handle(std::move(other.m_handle)),
+    m_addr(other.m_addr) {
+    other.make_invalid();
+    other.m_handle = nullptr;
+}
+
+inline osmium::util::MemoryMapping& osmium::util::MemoryMapping::operator=(osmium::util::MemoryMapping&& other) {
+    unmap();
+    m_size     = other.m_size;
+    m_offset   = other.m_offset;
+    m_fd       = other.m_fd;
+    m_writable = other.m_writable;
+    m_handle   = std::move(other.m_handle);
+    m_addr     = other.m_addr;
+    other.make_invalid();
+    other.m_handle = nullptr;
+    return *this;
+}
+
+inline void osmium::util::MemoryMapping::unmap() {
+    if (is_valid()) {
+        if (! UnmapViewOfFile(m_addr)) {
+            throw std::system_error(GetLastError(), std::system_category(), "UnmapViewOfFile failed");
+        }
+        make_invalid();
+    }
+
+    if (m_handle) {
+        if (! CloseHandle(m_handle)) {
+            throw std::system_error(GetLastError(), std::system_category(), "CloseHandle failed");
+        }
+        m_handle = nullptr;
+    }
+}
+
+inline void osmium::util::MemoryMapping::resize(size_t new_size) {
+    unmap();
+
+    m_size = new_size;
+    resize_fd(m_fd);
+
+    m_handle = create_file_mapping();
+    if (!m_handle) {
+        throw std::system_error(GetLastError(), std::system_category(), "CreateFileMapping failed");
+    }
+
+    m_addr = map_view_of_file();
+    if (!is_valid()) {
+        throw std::system_error(GetLastError(), std::system_category(), "MapViewOfFile failed");
+    }
+}
+
+#endif
+
+#endif // OSMIUM_UTIL_MEMORY_MAPPING_HPP
diff --git a/include/osmium/util/minmax.hpp b/include/osmium/util/minmax.hpp
new file mode 100644
index 0000000..05bd39b
--- /dev/null
+++ b/include/osmium/util/minmax.hpp
@@ -0,0 +1,123 @@
+#ifndef OSMIUM_UTIL_MINMAX_HPP
+#define OSMIUM_UTIL_MINMAX_HPP
+
+/*
+
+This file is part of Osmium (http://osmcode.org/libosmium).
+
+Copyright 2013-2015 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 <functional>
+#include <limits>
+
+namespace osmium {
+
+    template <typename T>
+    inline T min_op_start_value() {
+         return std::numeric_limits<T>::max();
+    }
+
+    /**
+     * Class for calculating the minimum of a bunch of values.
+     * Works with any numeric type.
+     *
+     * Usage:
+     *
+     *    min_op<int> x;
+     *    x.update(27);
+     *    x.update(12);
+     *    auto min = x.get(); // 12
+     */
+    template <typename T>
+    class min_op {
+
+        T m_value;
+
+    public:
+
+        explicit min_op(T start_value = min_op_start_value<T>()) :
+            m_value(start_value) {
+        }
+
+        void update(T value) noexcept {
+            if (value < m_value) {
+                m_value = value;
+            }
+        }
+
+        T operator()() const noexcept {
+            return m_value;
+        }
+
+    };
+
+    template <typename T>
+    inline T max_op_start_value() {
+         return std::numeric_limits<T>::min();
+    }
+
+    /**
+     * Class for calculating the maximum of a bunch of values.
+     * Works with any numeric type.
+     *
+     * Usage:
+     *
+     *    max_op<int> x;
+     *    x.update(27);
+     *    x.update(12);
+     *    auto max = x.get(); // 27
+     */
+    template <typename T>
+    class max_op {
+
+        T m_value;
+
+    public:
+
+        explicit max_op(T start_value = max_op_start_value<T>()) :
+            m_value(start_value) {
+        }
+
+        void update(T value) noexcept {
+            if (value > m_value) {
+                m_value = value;
+            }
+        }
+
+        T operator()() const noexcept {
+            return m_value;
+        }
+
+    };
+
+
+
+} // namespace osmium
+
+#endif // OSMIUM_UTIL_MINMAX_HPP
diff --git a/include/osmium/util/string.hpp b/include/osmium/util/string.hpp
index 54eb361..55bfc6c 100644
--- a/include/osmium/util/string.hpp
+++ b/include/osmium/util/string.hpp
@@ -43,21 +43,55 @@ namespace osmium {
      * Split string on the separator character.
      *
      * @param str The string to be split.
-     * @param sep The separastor character.
+     * @param sep The separator character.
+     * @param compact Set this to true to remove empty strings from result
      * @returns Vector with the parts of the string split up.
      */
-    inline std::vector<std::string> split_string(const std::string& str, const char sep) {
+    inline std::vector<std::string> split_string(const std::string& str, const char sep, bool compact = false) {
         std::vector<std::string> tokens;
 
         if (!str.empty()) {
             size_t pos = 0;
             size_t nextpos = str.find_first_of(sep);
             while (nextpos != std::string::npos) {
-                tokens.push_back(str.substr(pos, nextpos-pos));
+                if (!compact || (nextpos - pos != 0)) {
+                    tokens.push_back(str.substr(pos, nextpos-pos));
+                }
                 pos = nextpos + 1;
                 nextpos = str.find_first_of(sep, pos);
             }
-            tokens.push_back(str.substr(pos));
+            if (!compact || pos != str.size()) {
+                tokens.push_back(str.substr(pos));
+            }
+        }
+
+        return tokens;
+    }
+
+    /**
+     * Split string on the separator character(s).
+     *
+     * @param str The string to be split.
+     * @param sep The separator character(s).
+     * @param compact Set this to true to remove empty strings from result
+     * @returns Vector with the parts of the string split up.
+     */
+    inline std::vector<std::string> split_string(const std::string& str, const char* sep, bool compact = false) {
+        std::vector<std::string> tokens;
+
+        if (!str.empty()) {
+            size_t pos = 0;
+            size_t nextpos = str.find_first_of(sep);
+            while (nextpos != std::string::npos) {
+                if (!compact || (nextpos - pos != 0)) {
+                    tokens.push_back(str.substr(pos, nextpos-pos));
+                }
+                pos = nextpos + 1;
+                nextpos = str.find_first_of(sep, pos);
+            }
+            if (!compact || pos != str.size()) {
+                tokens.push_back(str.substr(pos));
+            }
         }
 
         return tokens;
diff --git a/include/osmium/util/verbose_output.hpp b/include/osmium/util/verbose_output.hpp
index 178781e..249d67f 100644
--- a/include/osmium/util/verbose_output.hpp
+++ b/include/osmium/util/verbose_output.hpp
@@ -87,7 +87,7 @@ namespace osmium {
 
         public:
 
-            explicit VerboseOutput(bool verbose=false) noexcept :
+            explicit VerboseOutput(bool verbose = false) noexcept :
                 m_start(time(NULL)),
                 m_verbose(verbose),
                 m_newline(true) {
diff --git a/scripts/travis_install.sh b/scripts/travis_install.sh
new file mode 100755
index 0000000..528e860
--- /dev/null
+++ b/scripts/travis_install.sh
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+#  travis_install.sh
+#
+
+if [ "$TRAVIS_OS_NAME" = "linux" ]; then
+
+    # install dependencies
+    sudo apt-get install --yes make libgdal-dev
+
+elif [ "$TRAVIS_OS_NAME" = "osx" ]; then
+
+    brew install protobuf osm-pbf google-sparsehash || true
+
+    # workaround for gdal homebrew problem
+    brew remove gdal
+    brew install gdal
+
+fi
+
+cd ..
+git clone --quiet --depth 1 https://github.com/osmcode/osm-testdata.git
+git clone --quiet --depth 1 https://github.com/scrosby/OSM-binary.git
+cd OSM-binary/src
+make
+
diff --git a/scripts/travis_script.sh b/scripts/travis_script.sh
new file mode 100755
index 0000000..c0f511e
--- /dev/null
+++ b/scripts/travis_script.sh
@@ -0,0 +1,31 @@
+#!/bin/sh
+#
+#  travis_script.sh
+#
+
+mkdir build
+cd build
+
+# GCC ignores the pragmas in the code that disable the "return-type" warning
+# selectively, so use this workaround.
+if [ "${CXX}" = "g++" ]; then
+    WORKAROUND="-DCMAKE_CXX_FLAGS=-Wno-return-type"
+else
+    WORKAROUND=""
+fi
+
+if [ "${CXX}" = "g++" ]; then
+    CXX=g++-4.8
+    CC=gcc-4.8
+fi
+
+cmake -LA \
+    -DCMAKE_BUILD_TYPE=${BUILD_TYPE} \
+    -DOSMPBF_INCLUDE_DIR=${TRAVIS_BUILD_DIR}/../OSM-binary/include \
+    -DOSMPBF_LIBRARY=${TRAVIS_BUILD_DIR}/../OSM-binary/src/libosmpbf.a \
+    ${WORKAROUND} \
+    ..
+
+make VERBOSE=1
+ctest --output-on-failure
+
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 334fe31..02e2433 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -8,6 +8,7 @@
 
 message(STATUS "Configuring unit tests")
 
+include(CMakeParseArguments)
 include_directories(include)
 
 add_library(testlib STATIC test_main.cpp)
@@ -19,40 +20,62 @@ set(ALL_TESTS "")
 #
 #  Define function for adding tests
 #
-#  Call with parameters:
-#    TGROUP - test group (directory)
-#    TNAME  - name of test
-#    ARGV2  - flag to enable test (optional)
-#    ARGV3  - libraries to add (optional)
+#  add_unit_tests(group name [ENABLE_IF bool] [LIBS libs] [LABELS labels])
+#
+#  group  - test group (directory)
+#  name   - name of test
+#  bool   - boolean variable telling whether the test should be run (optional)
+#  libs   - lib or libs that should be used when compiling test (optional)
+#  labels - additional labels this test should get (optional)
 #
 #-----------------------------------------------------------------------------
-function(add_unit_test TGROUP TNAME)
-    set(ALL_TESTS "${ALL_TESTS};${TGROUP}/${TNAME}" PARENT_SCOPE)
-    if((${ARGC} EQUAL 2) OR (${ARGV2}))
+function(add_unit_test _tgroup _tname)
+    set(_testid "${_tgroup}_${_tname}")
+    set(_tpath "${_tgroup}/${_tname}")
+
+    set(ALL_TESTS "${ALL_TESTS};${_tpath}" PARENT_SCOPE)
+
+    cmake_parse_arguments(_param "" "ENABLE_IF" "LIBS;LABELS" ${ARGN})
+
+    if(Osmium_DEBUG)
+        message("${_testid} ENABLE_IF=[${_param_ENABLE_IF}] LIBS=[${_param_LIBS}] LABELS=[${_param_LABELS}]")
+    endif()
+
+    if((NOT(DEFINED _param_ENABLE_IF)) OR (_param_ENABLE_IF))
         if(Osmium_DEBUG)
-            message("Adding test ${TGROUP}/${TNAME}")
+            message("Adding test: ${_tpath}")
         endif()
-        set(TESTNAME "${TGROUP}_${TNAME}")
-        add_executable(${TESTNAME} t/${TGROUP}/${TNAME}.cpp)
-        target_link_libraries(${TESTNAME} testlib)
-        if((${ARGV2}) AND (DEFINED ARGV3))
+        add_executable(${_testid} t/${_tpath}.cpp)
+        target_link_libraries(${_testid} testlib)
+
+        if(DEFINED _param_LIBS)
             if(Osmium_DEBUG)
-                message("  Adding libs ${ARGV3}")
+                message("  Adding libs: ${_param_LIBS}")
             endif()
-            target_link_libraries(${TESTNAME} ${ARGV3})
+            target_link_libraries(${_testid} ${_param_LIBS})
         endif()
-        add_test(NAME ${TESTNAME}
+
+        add_test(NAME ${_testid}
                  WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
-                 COMMAND ${TESTNAME}
+                 COMMAND ${_testid}
         )
-        set_tests_properties(${TESTNAME} PROPERTIES
-            LABELS "unit;fast;${TGROUP}"
+
+        set(_labels "unit;fast;${_tgroup}")
+        if(DEFINED _param_LABELS)
+            if(Osmium_DEBUG)
+                message("  Adding labels: ${_param_LABELS}")
+            endif()
+            set(_labels "${_labels};${_param_LABELS}")
+        endif()
+
+        set_tests_properties(${_testid} PROPERTIES
+            LABELS "${_labels}"
             ENVIRONMENT "OSMIUM_TEST_DATA_DIR=${CMAKE_CURRENT_SOURCE_DIR}"
         )
     else()
-        message("Skipped test ${TGROUP}/${TNAME} because a dependency was not found")
+        message("Skipped test ${_tpath} because a dependency was not found")
         set(OSMIUM_SKIPPED_TESTS
-            "${OSMIUM_SKIPPED_TESTS} ${TGROUP}/${TNAME}"
+            "${OSMIUM_SKIPPED_TESTS} ${_tpath}"
             CACHE STRING "Tests that were skipped because of missing dependecies")
     endif()
 endfunction()
@@ -74,6 +97,7 @@ add_unit_test(basic test_node_ref)
 add_unit_test(basic test_object_comparisons)
 add_unit_test(basic test_relation)
 add_unit_test(basic test_timestamp)
+add_unit_test(basic test_types_from_string)
 add_unit_test(basic test_way)
 
 add_unit_test(buffer test_buffer_node)
@@ -85,34 +109,40 @@ else()
     set(GEOS_AND_PROJ_FOUND FALSE)
 endif()
 add_unit_test(geom test_factory_with_projection
-    ${GEOS_AND_PROJ_FOUND}
-    "${GEOS_LIBRARY};${PROJ_LIBRARY}")
+    ENABLE_IF ${GEOS_AND_PROJ_FOUND}
+    LIBS ${GEOS_LIBRARY} ${PROJ_LIBRARY})
 
+add_unit_test(geom test_exception)
 add_unit_test(geom test_geojson)
-add_unit_test(geom test_geos ${GEOS_FOUND} ${GEOS_LIBRARY})
-add_unit_test(geom test_geos_wkb ${GEOS_FOUND} ${GEOS_LIBRARY})
+add_unit_test(geom test_geos ENABLE_IF ${GEOS_FOUND} LIBS ${GEOS_LIBRARY})
+add_unit_test(geom test_geos_wkb ENABLE_IF ${GEOS_FOUND} LIBS ${GEOS_LIBRARY})
 add_unit_test(geom test_mercator)
-add_unit_test(geom test_ogr ${GDAL_FOUND} ${GDAL_LIBRARY})
-add_unit_test(geom test_projection ${PROJ_FOUND} ${PROJ_LIBRARY})
+add_unit_test(geom test_ogr ENABLE_IF ${GDAL_FOUND} LIBS ${GDAL_LIBRARY})
+add_unit_test(geom test_projection ENABLE_IF ${PROJ_FOUND} LIBS ${PROJ_LIBRARY})
+add_unit_test(geom test_tile)
 add_unit_test(geom test_wkb)
 add_unit_test(geom test_wkt)
 
-add_unit_test(index test_id_to_location ${SPARSEHASH_FOUND})
-add_unit_test(index test_typed_mmap)
+add_unit_test(index test_id_to_location ENABLE_IF ${SPARSEHASH_FOUND})
 
-add_unit_test(io test_bzip2 ${BZIP2_FOUND} ${BZIP2_LIBRARIES})
+add_unit_test(io test_bzip2 ENABLE_IF ${BZIP2_FOUND} LIBS ${BZIP2_LIBRARIES})
 add_unit_test(io test_file_formats)
-add_unit_test(io test_reader TRUE "${OSMIUM_XML_LIBRARIES}")
-add_unit_test(io test_output_iterator ${Threads_FOUND} ${CMAKE_THREAD_LIBS_INIT})
+add_unit_test(io test_reader LIBS "${OSMIUM_XML_LIBRARIES}")
+add_unit_test(io test_output_iterator ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT})
 
 add_unit_test(tags test_filter)
 add_unit_test(tags test_operators)
 add_unit_test(tags test_tag_list)
 
-add_unit_test(thread test_pool ${Threads_FOUND} ${CMAKE_THREAD_LIBS_INIT})
+add_unit_test(thread test_pool ENABLE_IF ${Threads_FOUND} LIBS ${CMAKE_THREAD_LIBS_INIT})
 
 add_unit_test(util test_cast_with_assert)
+add_unit_test(util test_data_file)
+add_unit_test(util test_delta)
 add_unit_test(util test_double)
+add_unit_test(util test_file)
+add_unit_test(util test_memory_mapping)
+add_unit_test(util test_minmax)
 add_unit_test(util test_options)
 add_unit_test(util test_string)
 
diff --git a/test/data-tests/testdata-xml.cpp b/test/data-tests/testdata-xml.cpp
index 1969171..1f846ef 100644
--- a/test/data-tests/testdata-xml.cpp
+++ b/test/data-tests/testdata-xml.cpp
@@ -286,12 +286,13 @@ TEST_CASE("Reading OSM XML 121") {
     }
 
     SECTION("Using Reader") {
-        REQUIRE_THROWS_AS({
+        // can throw osmium::gzip_error or osmium::xml_error
+        REQUIRE_THROWS({
             osmium::io::Reader reader(filename("121-truncated_gzip_file", "osm.gz"));
             osmium::io::Header header = reader.header();
             osmium::memory::Buffer buffer = reader.read();
             reader.close();
-        }, osmium::gzip_error);
+        });
     }
 
 }
@@ -339,6 +340,8 @@ TEST_CASE("Reading OSM XML 140") {
 
             REQUIRE(!strcmp(uc, t["unicode_xml"]));
 
+// workaround for missing support for u8 string literals on Windows
+#if !defined(_MSC_VER)
             switch (count) {
                 case 1:
                     REQUIRE(!strcmp(uc, u8"a"));
@@ -358,6 +361,7 @@ TEST_CASE("Reading OSM XML 140") {
                 default:
                     REQUIRE(false); // should not be here
             }
+#endif
         }
         REQUIRE(count == 5);
     }
diff --git a/test/t/area/test_area_id.cpp b/test/t/area/test_area_id.cpp
index 45bf87c..fbd8d78 100644
--- a/test/t/area/test_area_id.cpp
+++ b/test/t/area/test_area_id.cpp
@@ -4,22 +4,22 @@
 
 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("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));
-}
+    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));
+    }
 
 }
diff --git a/test/t/area/test_node_ref_segment.cpp b/test/t/area/test_node_ref_segment.cpp
index fd67e6c..3097687 100644
--- a/test/t/area/test_node_ref_segment.cpp
+++ b/test/t/area/test_node_ref_segment.cpp
@@ -6,110 +6,110 @@ using osmium::area::detail::NodeRefSegment;
 
 TEST_CASE("NodeRefSegmentClass") {
 
-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());
-}
-
-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 });
-
-    NodeRefSegment s1(nr1, nr2, nullptr, nullptr);
-    REQUIRE(s1.first().ref() == 1);
-    REQUIRE(s1.second().ref() == 2);
-
-    NodeRefSegment s2(nr2, nr3, nullptr, nullptr);
-    REQUIRE(s2.first().ref() == 3);
-    REQUIRE(s2.second().ref() == 2);
-
-    NodeRefSegment s3(nr3, nr4, nullptr, nullptr);
-    REQUIRE(s3.first().ref() == 3);
-    REQUIRE(s3.second().ref() == 4);
-}
-
-SECTION("intersection") {
-    NodeRefSegment s1({ 1, {0.0, 0.0}}, { 2, {2.0, 2.0}}, nullptr, nullptr);
-    NodeRefSegment s2({ 3, {0.0, 2.0}}, { 4, {2.0, 0.0}}, nullptr, nullptr);
-    NodeRefSegment s3({ 5, {2.0, 0.0}}, { 6, {4.0, 2.0}}, nullptr, nullptr);
-    NodeRefSegment s4({ 7, {1.0, 0.0}}, { 8, {3.0, 2.0}}, nullptr, nullptr);
-    NodeRefSegment s5({ 9, {0.0, 4.0}}, {10, {4.0, 0.0}}, nullptr, nullptr);
-    NodeRefSegment s6({11, {0.0, 0.0}}, {12, {1.0, 1.0}}, nullptr, nullptr);
-    NodeRefSegment s7({13, {1.0, 1.0}}, {14, {3.0, 3.0}}, nullptr, nullptr);
-
-    REQUIRE(calculate_intersection(s1, s2) == osmium::Location(1.0, 1.0));
-    REQUIRE(calculate_intersection(s1, s3) == osmium::Location());
-    REQUIRE(calculate_intersection(s2, s3) == osmium::Location());
-    REQUIRE(calculate_intersection(s1, s4) == osmium::Location());
-    REQUIRE(calculate_intersection(s1, s5) == osmium::Location(2.0, 2.0));
-    REQUIRE(calculate_intersection(s1, s1) == osmium::Location());
-    REQUIRE(calculate_intersection(s1, s6) == osmium::Location());
-    REQUIRE(calculate_intersection(s1, s7) == osmium::Location());
-}
-
-SECTION("to_left_of") {
-    osmium::Location loc { 2.0, 2.0 };
-
-    REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {4.0, 0.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
-    REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-
-    REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {3.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {4.0, 3.0}}, nullptr, nullptr).to_left_of(loc) == false);
-
-    REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {2.0, 0.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {3.0, 1.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {3.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
-
-    REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
-
-    REQUIRE(NodeRefSegment({0, {2.0, 0.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {2.0, 0.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
-    REQUIRE(NodeRefSegment({0, {2.0, 2.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
-
-    REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 1.0}}, nullptr, nullptr).to_left_of(loc) == false);
-    REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {0.0, 1.0}}, nullptr, nullptr).to_left_of(loc) == false);
-
-    REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {1.0, 3.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {2.0, 0.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {3.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
-
-    REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {1.0, 2.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {1.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
-    REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
-
-    REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 2.0}}, nullptr, nullptr).to_left_of(loc));
-    REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
-
-    REQUIRE(NodeRefSegment({0, {0.0, 1.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
-    REQUIRE(NodeRefSegment({0, {2.0, 2.0}}, {1, {4.0, 0.0}}, nullptr, nullptr).to_left_of(loc) == false);
-}
-
-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));
-}
+    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());
+    }
+
+    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 });
+
+        NodeRefSegment s1(nr1, nr2, nullptr, nullptr);
+        REQUIRE(s1.first().ref() == 1);
+        REQUIRE(s1.second().ref() == 2);
+
+        NodeRefSegment s2(nr2, nr3, nullptr, nullptr);
+        REQUIRE(s2.first().ref() == 3);
+        REQUIRE(s2.second().ref() == 2);
+
+        NodeRefSegment s3(nr3, nr4, nullptr, nullptr);
+        REQUIRE(s3.first().ref() == 3);
+        REQUIRE(s3.second().ref() == 4);
+    }
+
+    SECTION("intersection") {
+        NodeRefSegment s1({ 1, {0.0, 0.0}}, { 2, {2.0, 2.0}}, nullptr, nullptr);
+        NodeRefSegment s2({ 3, {0.0, 2.0}}, { 4, {2.0, 0.0}}, nullptr, nullptr);
+        NodeRefSegment s3({ 5, {2.0, 0.0}}, { 6, {4.0, 2.0}}, nullptr, nullptr);
+        NodeRefSegment s4({ 7, {1.0, 0.0}}, { 8, {3.0, 2.0}}, nullptr, nullptr);
+        NodeRefSegment s5({ 9, {0.0, 4.0}}, {10, {4.0, 0.0}}, nullptr, nullptr);
+        NodeRefSegment s6({11, {0.0, 0.0}}, {12, {1.0, 1.0}}, nullptr, nullptr);
+        NodeRefSegment s7({13, {1.0, 1.0}}, {14, {3.0, 3.0}}, nullptr, nullptr);
+
+        REQUIRE(calculate_intersection(s1, s2) == osmium::Location(1.0, 1.0));
+        REQUIRE(calculate_intersection(s1, s3) == osmium::Location());
+        REQUIRE(calculate_intersection(s2, s3) == osmium::Location());
+        REQUIRE(calculate_intersection(s1, s4) == osmium::Location());
+        REQUIRE(calculate_intersection(s1, s5) == osmium::Location(2.0, 2.0));
+        REQUIRE(calculate_intersection(s1, s1) == osmium::Location());
+        REQUIRE(calculate_intersection(s1, s6) == osmium::Location());
+        REQUIRE(calculate_intersection(s1, s7) == osmium::Location());
+    }
+
+    SECTION("to_left_of") {
+        osmium::Location loc { 2.0, 2.0 };
+
+        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {4.0, 0.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
+
+        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {3.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {4.0, 3.0}}, nullptr, nullptr).to_left_of(loc) == false);
+
+        REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {2.0, 0.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {3.0, 1.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {1.0, 3.0}}, {1, {3.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
+
+        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
+
+        REQUIRE(NodeRefSegment({0, {2.0, 0.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {2.0, 0.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(NodeRefSegment({0, {2.0, 2.0}}, {1, {2.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+
+        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 1.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {0.0, 1.0}}, nullptr, nullptr).to_left_of(loc) == false);
+
+        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {1.0, 3.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {2.0, 0.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {3.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+
+        REQUIRE(NodeRefSegment({0, {1.0, 0.0}}, {1, {1.0, 2.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {1.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {1.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+
+        REQUIRE(NodeRefSegment({0, {0.0, 0.0}}, {1, {0.0, 2.0}}, nullptr, nullptr).to_left_of(loc));
+        REQUIRE(NodeRefSegment({0, {0.0, 2.0}}, {1, {4.0, 4.0}}, nullptr, nullptr).to_left_of(loc) == false);
+
+        REQUIRE(NodeRefSegment({0, {0.0, 1.0}}, {1, {2.0, 2.0}}, nullptr, nullptr).to_left_of(loc) == false);
+        REQUIRE(NodeRefSegment({0, {2.0, 2.0}}, {1, {4.0, 0.0}}, nullptr, nullptr).to_left_of(loc) == false);
+    }
+
+    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));
+    }
 
 }
 
diff --git a/test/t/basic/test_box.cpp b/test/t/basic/test_box.cpp
index 6ebf95c..8182fbf 100644
--- a/test/t/basic/test_box.cpp
+++ b/test/t/basic/test_box.cpp
@@ -7,85 +7,85 @@
 
 TEST_CASE("Box") {
 
-SECTION("instantiation") {
-    osmium::Box b;
-    REQUIRE(!b);
-    REQUIRE(!b.bottom_left());
-    REQUIRE(!b.top_right());
-    REQUIRE_THROWS_AS(b.size(), osmium::invalid_location);
-}
-
-SECTION("instantiation_and_extend_with_undefined") {
-    osmium::Box b;
-    REQUIRE(!b);
-    b.extend(osmium::Location());
-    REQUIRE(!b.bottom_left());
-    REQUIRE(!b.top_right());
-}
-
-SECTION("instantiation_and_extend") {
-    osmium::Box b;
-    osmium::Location loc1 { 1.2, 3.4 };
-    b.extend(loc1);
-    REQUIRE(!!b);
-    REQUIRE(!!b.bottom_left());
-    REQUIRE(!!b.top_right());
-    REQUIRE(b.contains(loc1));
-
-    osmium::Location loc2 { 3.4, 4.5 };
-    osmium::Location loc3 { 5.6, 7.8 };
-
-    b.extend(loc2);
-    b.extend(loc3);
-    REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4));
-    REQUIRE(b.top_right() == osmium::Location(5.6, 7.8));
-
-    // extend with undefined doesn't change anything
-    b.extend(osmium::Location());
-    REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4));
-    REQUIRE(b.top_right() == osmium::Location(5.6, 7.8));
-
-    REQUIRE(b.contains(loc1));
-    REQUIRE(b.contains(loc2));
-    REQUIRE(b.contains(loc3));
-}
-
-SECTION("output_defined") {
-    osmium::Box b;
-    b.extend(osmium::Location(1.2, 3.4));
-    b.extend(osmium::Location(5.6, 7.8));
-    std::stringstream out;
-    out << b;
-    REQUIRE(out.str() == "(1.2,3.4,5.6,7.8)");
-    REQUIRE(b.size() == Approx(19.36).epsilon(0.000001));
-}
-
-SECTION("output_undefined") {
-    osmium::Box b;
-    std::stringstream out;
-    out << b;
-    REQUIRE(out.str() == "(undefined)");
-}
-
-SECTION("box_inside_box") {
-    osmium::Box outer;
-    outer.extend(osmium::Location(1, 1));
-    outer.extend(osmium::Location(10, 10));
-
-    osmium::Box inner;
-    inner.extend(osmium::Location(2, 2));
-    inner.extend(osmium::Location(4, 4));
-
-    osmium::Box overlap;
-    overlap.extend(osmium::Location(3, 3));
-    overlap.extend(osmium::Location(5, 5));
-
-    REQUIRE( osmium::geom::contains(inner, outer));
-    REQUIRE(!osmium::geom::contains(outer, inner));
-
-    REQUIRE(!osmium::geom::contains(overlap, inner));
-    REQUIRE(!osmium::geom::contains(inner, overlap));
-}
+    SECTION("instantiation") {
+        osmium::Box b;
+        REQUIRE(!b);
+        REQUIRE(!b.bottom_left());
+        REQUIRE(!b.top_right());
+        REQUIRE_THROWS_AS(b.size(), osmium::invalid_location);
+    }
+
+    SECTION("instantiation_and_extend_with_undefined") {
+        osmium::Box b;
+        REQUIRE(!b);
+        b.extend(osmium::Location());
+        REQUIRE(!b.bottom_left());
+        REQUIRE(!b.top_right());
+    }
+
+    SECTION("instantiation_and_extend") {
+        osmium::Box b;
+        osmium::Location loc1 { 1.2, 3.4 };
+        b.extend(loc1);
+        REQUIRE(!!b);
+        REQUIRE(!!b.bottom_left());
+        REQUIRE(!!b.top_right());
+        REQUIRE(b.contains(loc1));
+
+        osmium::Location loc2 { 3.4, 4.5 };
+        osmium::Location loc3 { 5.6, 7.8 };
+
+        b.extend(loc2);
+        b.extend(loc3);
+        REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4));
+        REQUIRE(b.top_right() == osmium::Location(5.6, 7.8));
+
+        // extend with undefined doesn't change anything
+        b.extend(osmium::Location());
+        REQUIRE(b.bottom_left() == osmium::Location(1.2, 3.4));
+        REQUIRE(b.top_right() == osmium::Location(5.6, 7.8));
+
+        REQUIRE(b.contains(loc1));
+        REQUIRE(b.contains(loc2));
+        REQUIRE(b.contains(loc3));
+    }
+
+    SECTION("output_defined") {
+        osmium::Box b;
+        b.extend(osmium::Location(1.2, 3.4));
+        b.extend(osmium::Location(5.6, 7.8));
+        std::stringstream out;
+        out << b;
+        REQUIRE(out.str() == "(1.2,3.4,5.6,7.8)");
+        REQUIRE(b.size() == Approx(19.36).epsilon(0.000001));
+    }
+
+    SECTION("output_undefined") {
+        osmium::Box b;
+        std::stringstream out;
+        out << b;
+        REQUIRE(out.str() == "(undefined)");
+    }
+
+    SECTION("box_inside_box") {
+        osmium::Box outer;
+        outer.extend(osmium::Location(1, 1));
+        outer.extend(osmium::Location(10, 10));
+
+        osmium::Box inner;
+        inner.extend(osmium::Location(2, 2));
+        inner.extend(osmium::Location(4, 4));
+
+        osmium::Box overlap;
+        overlap.extend(osmium::Location(3, 3));
+        overlap.extend(osmium::Location(5, 5));
+
+        REQUIRE( osmium::geom::contains(inner, outer));
+        REQUIRE(!osmium::geom::contains(outer, inner));
+
+        REQUIRE(!osmium::geom::contains(overlap, inner));
+        REQUIRE(!osmium::geom::contains(inner, overlap));
+    }
 
 }
 
diff --git a/test/t/basic/test_entity_bits.cpp b/test/t/basic/test_entity_bits.cpp
index a200f48..f15068b 100644
--- a/test/t/basic/test_entity_bits.cpp
+++ b/test/t/basic/test_entity_bits.cpp
@@ -4,28 +4,28 @@
 
 TEST_CASE("entity_bits") {
 
-SECTION("can_be_set_and_checked") {
-    osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::node | osmium::osm_entity_bits::way;
-    REQUIRE(entities == (osmium::osm_entity_bits::node | osmium::osm_entity_bits::way));
+    SECTION("can_be_set_and_checked") {
+        osmium::osm_entity_bits::type entities = osmium::osm_entity_bits::node | osmium::osm_entity_bits::way;
+        REQUIRE(entities == (osmium::osm_entity_bits::node | osmium::osm_entity_bits::way));
 
-    entities |= osmium::osm_entity_bits::relation;
-    REQUIRE((entities & osmium::osm_entity_bits::object));
+        entities |= osmium::osm_entity_bits::relation;
+        REQUIRE((entities & osmium::osm_entity_bits::object));
 
-    entities |= osmium::osm_entity_bits::area;
-    REQUIRE(entities == osmium::osm_entity_bits::object);
+        entities |= osmium::osm_entity_bits::area;
+        REQUIRE(entities == osmium::osm_entity_bits::object);
 
-    REQUIRE(! (entities & osmium::osm_entity_bits::changeset));
+        REQUIRE(! (entities & osmium::osm_entity_bits::changeset));
 
-    entities &= osmium::osm_entity_bits::node;
-    REQUIRE((entities & osmium::osm_entity_bits::node));
-    REQUIRE(! (entities & osmium::osm_entity_bits::way));
-    REQUIRE(entities == osmium::osm_entity_bits::node);
+        entities &= osmium::osm_entity_bits::node;
+        REQUIRE((entities & osmium::osm_entity_bits::node));
+        REQUIRE(! (entities & osmium::osm_entity_bits::way));
+        REQUIRE(entities == osmium::osm_entity_bits::node);
 
-    REQUIRE(osmium::osm_entity_bits::node      == osmium::osm_entity_bits::from_item_type(osmium::item_type::node));
-    REQUIRE(osmium::osm_entity_bits::way       == osmium::osm_entity_bits::from_item_type(osmium::item_type::way));
-    REQUIRE(osmium::osm_entity_bits::relation  == osmium::osm_entity_bits::from_item_type(osmium::item_type::relation));
-    REQUIRE(osmium::osm_entity_bits::changeset == osmium::osm_entity_bits::from_item_type(osmium::item_type::changeset));
-    REQUIRE(osmium::osm_entity_bits::area      == osmium::osm_entity_bits::from_item_type(osmium::item_type::area));
-}
+        REQUIRE(osmium::osm_entity_bits::node      == osmium::osm_entity_bits::from_item_type(osmium::item_type::node));
+        REQUIRE(osmium::osm_entity_bits::way       == osmium::osm_entity_bits::from_item_type(osmium::item_type::way));
+        REQUIRE(osmium::osm_entity_bits::relation  == osmium::osm_entity_bits::from_item_type(osmium::item_type::relation));
+        REQUIRE(osmium::osm_entity_bits::changeset == osmium::osm_entity_bits::from_item_type(osmium::item_type::changeset));
+        REQUIRE(osmium::osm_entity_bits::area      == osmium::osm_entity_bits::from_item_type(osmium::item_type::area));
+    }
 
 }
diff --git a/test/t/basic/test_location.cpp b/test/t/basic/test_location.cpp
index 25545a4..3fd8d15 100644
--- a/test/t/basic/test_location.cpp
+++ b/test/t/basic/test_location.cpp
@@ -10,145 +10,145 @@ TEST_CASE("Location") {
 // fails on MSVC and doesn't really matter
 // static_assert(std::is_literal_type<osmium::Location>::value, "osmium::Location not literal type");
 
-SECTION("instantiation_with_default_parameters") {
-    osmium::Location loc;
-    REQUIRE(!loc);
-    REQUIRE_THROWS_AS(loc.lon(), osmium::invalid_location);
-    REQUIRE_THROWS_AS(loc.lat(), osmium::invalid_location);
-}
-
-SECTION("instantiation_with_double_parameters") {
-    osmium::Location loc1(1.2, 4.5);
-    REQUIRE(!!loc1);
-    REQUIRE(12000000 == loc1.x());
-    REQUIRE(45000000 == loc1.y());
-    REQUIRE(1.2 == loc1.lon());
-    REQUIRE(4.5 == loc1.lat());
-
-    osmium::Location loc2(loc1);
-    REQUIRE(4.5 == loc2.lat());
-
-    osmium::Location loc3 = loc1;
-    REQUIRE(4.5 == loc3.lat());
-}
-
-SECTION("instantiation_with_double_parameters_constructor_with_universal_initializer") {
-    osmium::Location loc { 2.2, 3.3 };
-    REQUIRE(2.2 == loc.lon());
-    REQUIRE(3.3 == loc.lat());
-}
-
-SECTION("instantiation_with_double_parameters_constructor_with_initializer_list") {
-    osmium::Location loc({ 4.4, 5.5 });
-    REQUIRE(4.4 == loc.lon());
-    REQUIRE(5.5 == loc.lat());
-}
-
-SECTION("instantiation_with_double_parameters_operator_equal") {
-    osmium::Location loc = { 5.5, 6.6 };
-    REQUIRE(5.5 == loc.lon());
-    REQUIRE(6.6 == loc.lat());
-}
-
-SECTION("equality") {
-    osmium::Location loc1(1.2, 4.5);
-    osmium::Location loc2(1.2, 4.5);
-    osmium::Location loc3(1.5, 1.5);
-    REQUIRE(loc1 == loc2);
-    REQUIRE(loc1 != loc3);
-}
-
-SECTION("order") {
-    REQUIRE(osmium::Location(-1.2, 10.0) < osmium::Location(1.2, 10.0));
-    REQUIRE(osmium::Location(1.2, 10.0) > osmium::Location(-1.2, 10.0));
-
-    REQUIRE(osmium::Location(10.2, 20.0) < osmium::Location(11.2, 20.2));
-    REQUIRE(osmium::Location(10.2, 20.2) < osmium::Location(11.2, 20.0));
-    REQUIRE(osmium::Location(11.2, 20.2) > osmium::Location(10.2, 20.0));
-}
-
-SECTION("validity") {
-    REQUIRE(osmium::Location(0.0, 0.0).valid());
-    REQUIRE(osmium::Location(1.2, 4.5).valid());
-    REQUIRE(osmium::Location(-1.2, 4.5).valid());
-    REQUIRE(osmium::Location(-180.0, -90.0).valid());
-    REQUIRE(osmium::Location(180.0, -90.0).valid());
-    REQUIRE(osmium::Location(-180.0, 90.0).valid());
-    REQUIRE(osmium::Location(180.0, 90.0).valid());
-
-    REQUIRE(!osmium::Location(200.0, 4.5).valid());
-    REQUIRE(!osmium::Location(-1.2, -100.0).valid());
-    REQUIRE(!osmium::Location(-180.0, 90.005).valid());
-}
-
-
-SECTION("output_to_iterator_comma_separator") {
-    char buffer[100];
-    osmium::Location loc(-3.2, 47.3);
-    *loc.as_string(buffer, ',') = 0;
-    REQUIRE(std::string("-3.2,47.3") == buffer);
-}
-
-SECTION("output_to_iterator_space_separator") {
-    char buffer[100];
-    osmium::Location loc(0.0, 7.0);
-    *loc.as_string(buffer, ' ') = 0;
-    REQUIRE(std::string("0 7") == buffer);
-}
-
-SECTION("output_to_iterator_check_precision") {
-    char buffer[100];
-    osmium::Location loc(-179.9999999, -90.0);
-    *loc.as_string(buffer, ' ') = 0;
-    REQUIRE(std::string("-179.9999999 -90") == buffer);
-}
-
-SECTION("output_to_iterator_undefined_location") {
-    char buffer[100];
-    osmium::Location loc;
-    REQUIRE_THROWS_AS(loc.as_string(buffer, ','), osmium::invalid_location);
-}
-
-SECTION("output_to_string_comman_separator") {
-    std::string s;
-    osmium::Location loc(-3.2, 47.3);
-    loc.as_string(std::back_inserter(s), ',');
-    REQUIRE(s == "-3.2,47.3");
-}
-
-SECTION("output_to_string_space_separator") {
-    std::string s;
-    osmium::Location loc(0.0, 7.0);
-    loc.as_string(std::back_inserter(s), ' ');
-    REQUIRE(s == "0 7");
-}
-
-SECTION("output_to_string_check_precision") {
-    std::string s;
-    osmium::Location loc(-179.9999999, -90.0);
-    loc.as_string(std::back_inserter(s), ' ');
-    REQUIRE(s == "-179.9999999 -90");
-}
-
-SECTION("output_to_string_undefined_location") {
-    std::string s;
-    osmium::Location loc;
-    REQUIRE_THROWS_AS(loc.as_string(std::back_inserter(s), ','), osmium::invalid_location);
-}
-
-SECTION("output_defined") {
-    osmium::Location p(-3.2, 47.3);
-    std::stringstream out;
-    out << p;
-    REQUIRE(out.str() == "(-3.2,47.3)");
-}
-
-SECTION("output_undefined") {
-    osmium::Location p;
-    std::stringstream out;
-    out << p;
-    REQUIRE(out.str() == "(undefined,undefined)");
-}
+    SECTION("instantiation_with_default_parameters") {
+        osmium::Location loc;
+        REQUIRE(!loc);
+        REQUIRE_THROWS_AS(loc.lon(), osmium::invalid_location);
+        REQUIRE_THROWS_AS(loc.lat(), osmium::invalid_location);
+    }
+
+    SECTION("instantiation_with_double_parameters") {
+        osmium::Location loc1(1.2, 4.5);
+        REQUIRE(!!loc1);
+        REQUIRE(12000000 == loc1.x());
+        REQUIRE(45000000 == loc1.y());
+        REQUIRE(1.2 == loc1.lon());
+        REQUIRE(4.5 == loc1.lat());
+
+        osmium::Location loc2(loc1);
+        REQUIRE(4.5 == loc2.lat());
+
+        osmium::Location loc3 = loc1;
+        REQUIRE(4.5 == loc3.lat());
+    }
+
+    SECTION("instantiation_with_double_parameters_constructor_with_universal_initializer") {
+        osmium::Location loc { 2.2, 3.3 };
+        REQUIRE(2.2 == loc.lon());
+        REQUIRE(3.3 == loc.lat());
+    }
+
+    SECTION("instantiation_with_double_parameters_constructor_with_initializer_list") {
+        osmium::Location loc({ 4.4, 5.5 });
+        REQUIRE(4.4 == loc.lon());
+        REQUIRE(5.5 == loc.lat());
+    }
+
+    SECTION("instantiation_with_double_parameters_operator_equal") {
+        osmium::Location loc = { 5.5, 6.6 };
+        REQUIRE(5.5 == loc.lon());
+        REQUIRE(6.6 == loc.lat());
+    }
+
+    SECTION("equality") {
+        osmium::Location loc1(1.2, 4.5);
+        osmium::Location loc2(1.2, 4.5);
+        osmium::Location loc3(1.5, 1.5);
+        REQUIRE(loc1 == loc2);
+        REQUIRE(loc1 != loc3);
+    }
+
+    SECTION("order") {
+        REQUIRE(osmium::Location(-1.2, 10.0) < osmium::Location(1.2, 10.0));
+        REQUIRE(osmium::Location(1.2, 10.0) > osmium::Location(-1.2, 10.0));
+
+        REQUIRE(osmium::Location(10.2, 20.0) < osmium::Location(11.2, 20.2));
+        REQUIRE(osmium::Location(10.2, 20.2) < osmium::Location(11.2, 20.0));
+        REQUIRE(osmium::Location(11.2, 20.2) > osmium::Location(10.2, 20.0));
+    }
+
+    SECTION("validity") {
+        REQUIRE(osmium::Location(0.0, 0.0).valid());
+        REQUIRE(osmium::Location(1.2, 4.5).valid());
+        REQUIRE(osmium::Location(-1.2, 4.5).valid());
+        REQUIRE(osmium::Location(-180.0, -90.0).valid());
+        REQUIRE(osmium::Location(180.0, -90.0).valid());
+        REQUIRE(osmium::Location(-180.0, 90.0).valid());
+        REQUIRE(osmium::Location(180.0, 90.0).valid());
+
+        REQUIRE(!osmium::Location(200.0, 4.5).valid());
+        REQUIRE(!osmium::Location(-1.2, -100.0).valid());
+        REQUIRE(!osmium::Location(-180.0, 90.005).valid());
+    }
+
+
+    SECTION("output_to_iterator_comma_separator") {
+        char buffer[100];
+        osmium::Location loc(-3.2, 47.3);
+        *loc.as_string(buffer, ',') = 0;
+        REQUIRE(std::string("-3.2,47.3") == buffer);
+    }
+
+    SECTION("output_to_iterator_space_separator") {
+        char buffer[100];
+        osmium::Location loc(0.0, 7.0);
+        *loc.as_string(buffer, ' ') = 0;
+        REQUIRE(std::string("0 7") == buffer);
+    }
+
+    SECTION("output_to_iterator_check_precision") {
+        char buffer[100];
+        osmium::Location loc(-179.9999999, -90.0);
+        *loc.as_string(buffer, ' ') = 0;
+        REQUIRE(std::string("-179.9999999 -90") == buffer);
+    }
+
+    SECTION("output_to_iterator_undefined_location") {
+        char buffer[100];
+        osmium::Location loc;
+        REQUIRE_THROWS_AS(loc.as_string(buffer, ','), osmium::invalid_location);
+    }
+
+    SECTION("output_to_string_comman_separator") {
+        std::string s;
+        osmium::Location loc(-3.2, 47.3);
+        loc.as_string(std::back_inserter(s), ',');
+        REQUIRE(s == "-3.2,47.3");
+    }
+
+    SECTION("output_to_string_space_separator") {
+        std::string s;
+        osmium::Location loc(0.0, 7.0);
+        loc.as_string(std::back_inserter(s), ' ');
+        REQUIRE(s == "0 7");
+    }
+
+    SECTION("output_to_string_check_precision") {
+        std::string s;
+        osmium::Location loc(-179.9999999, -90.0);
+        loc.as_string(std::back_inserter(s), ' ');
+        REQUIRE(s == "-179.9999999 -90");
+    }
+
+    SECTION("output_to_string_undefined_location") {
+        std::string s;
+        osmium::Location loc;
+        REQUIRE_THROWS_AS(loc.as_string(std::back_inserter(s), ','), osmium::invalid_location);
+    }
+
+    SECTION("output_defined") {
+        osmium::Location p(-3.2, 47.3);
+        std::stringstream out;
+        out << p;
+        REQUIRE(out.str() == "(-3.2,47.3)");
+    }
+
+    SECTION("output_undefined") {
+        osmium::Location p;
+        std::stringstream out;
+        out << p;
+        REQUIRE(out.str() == "(undefined,undefined)");
+    }
 
 }
 
diff --git a/test/t/basic/test_node_ref.cpp b/test/t/basic/test_node_ref.cpp
index 5c7b670..ac7ccbf 100644
--- a/test/t/basic/test_node_ref.cpp
+++ b/test/t/basic/test_node_ref.cpp
@@ -4,54 +4,54 @@
 
 TEST_CASE("NodeRef") {
 
-SECTION("instantiation_with_default_parameters") {
-    osmium::NodeRef node_ref;
-    REQUIRE(node_ref.ref() == 0);
+    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);
-}
-
-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));
-}
-
-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());
-}
-
-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));
-}
+    }
+
+    SECTION("instantiation_with_id") {
+        osmium::NodeRef node_ref(7);
+        REQUIRE(node_ref.ref() == 7);
+    }
+
+    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));
+    }
+
+    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());
+    }
+
+    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));
+    }
 
 }
 
diff --git a/test/t/basic/test_object_comparisons.cpp b/test/t/basic/test_object_comparisons.cpp
index ffffbcc..2bfdcad 100644
--- a/test/t/basic/test_object_comparisons.cpp
+++ b/test/t/basic/test_object_comparisons.cpp
@@ -6,142 +6,142 @@
 
 TEST_CASE("Object_Comparisons") {
 
-SECTION("order") {
-    osmium::memory::Buffer buffer(10 * 1000);
-
-    {
-        // add node 1
-        osmium::builder::NodeBuilder node_builder(buffer);
-        node_builder.add_user("testuser");
-        buffer.commit();
+    SECTION("order") {
+        osmium::memory::Buffer buffer(10 * 1000);
+
+        {
+            // add node 1
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("testuser");
+            buffer.commit();
+        }
+
+        {
+            // add node 2
+            osmium::builder::NodeBuilder node_builder(buffer);
+            node_builder.add_user("testuser");
+            buffer.commit();
+        }
+
+        auto it = buffer.begin();
+        osmium::Node& node1 = static_cast<osmium::Node&>(*it);
+        osmium::Node& node2 = static_cast<osmium::Node&>(*(++it));
+
+        node1.set_id(10);
+        node1.set_version(1);
+        node2.set_id(15);
+        node2.set_version(2);
+        REQUIRE(true == (node1 < node2));
+        REQUIRE(false == (node1 > node2));
+        node1.set_id(20);
+        node1.set_version(1);
+        node2.set_id(20);
+        node2.set_version(2);
+        REQUIRE(true == (node1 < node2));
+        REQUIRE(false == (node1 > node2));
+        node1.set_id(-10);
+        node1.set_version(2);
+        node2.set_id(-15);
+        node2.set_version(1);
+        REQUIRE(true == (node1 < node2));
+        REQUIRE(false == (node1 > node2));
     }
 
-    {
-        // add node 2
-        osmium::builder::NodeBuilder node_builder(buffer);
-        node_builder.add_user("testuser");
-        buffer.commit();
+    SECTION("order_types") {
+        osmium::memory::Buffer buffer(10 * 1000);
+
+        {
+            // add node 1
+            osmium::builder::NodeBuilder node_builder(buffer);
+            osmium::Node& node = node_builder.object();
+            REQUIRE(osmium::item_type::node == node.type());
+
+            node.set_id(3);
+            node.set_version(3);
+            node_builder.add_user("testuser");
+
+            buffer.commit();
+        }
+
+        {
+            // add node 2
+            osmium::builder::NodeBuilder node_builder(buffer);
+            osmium::Node& node = node_builder.object();
+            REQUIRE(osmium::item_type::node == node.type());
+
+            node.set_id(3);
+            node.set_version(4);
+            node_builder.add_user("testuser");
+
+            buffer.commit();
+        }
+
+        {
+            // add node 3
+            osmium::builder::NodeBuilder node_builder(buffer);
+            osmium::Node& node = node_builder.object();
+            REQUIRE(osmium::item_type::node == node.type());
+
+            node.set_id(3);
+            node.set_version(4);
+            node_builder.add_user("testuser");
+
+            buffer.commit();
+        }
+
+        {
+            // add way
+            osmium::builder::WayBuilder way_builder(buffer);
+            osmium::Way& way = way_builder.object();
+            REQUIRE(osmium::item_type::way == way.type());
+
+            way.set_id(2);
+            way.set_version(2);
+            way_builder.add_user("testuser");
+
+            buffer.commit();
+        }
+
+        {
+            // add relation
+            osmium::builder::RelationBuilder relation_builder(buffer);
+            osmium::Relation& relation = relation_builder.object();
+            REQUIRE(osmium::item_type::relation == relation.type());
+
+            relation.set_id(1);
+            relation.set_version(1);
+            relation_builder.add_user("testuser");
+
+            buffer.commit();
+        }
+
+        auto it = buffer.begin();
+        const osmium::Node& node1 = static_cast<const osmium::Node&>(*it);
+        const osmium::Node& node2 = static_cast<const osmium::Node&>(*(++it));
+        const osmium::Node& node3 = static_cast<const osmium::Node&>(*(++it));
+        const osmium::Way& way = static_cast<const osmium::Way&>(*(++it));
+        const osmium::Relation& relation = static_cast<const osmium::Relation&>(*(++it));
+
+        REQUIRE(true == (node1 < node2));
+        REQUIRE(true == (node2 < way));
+        REQUIRE(false == (node2 > way));
+        REQUIRE(true == (way < relation));
+        REQUIRE(true == (node1 < relation));
+
+        REQUIRE(true == osmium::object_order_type_id_version()(node1, node2));
+        REQUIRE(true == osmium::object_order_type_id_reverse_version()(node2, node1));
+        REQUIRE(true == osmium::object_order_type_id_version()(node1, way));
+        REQUIRE(true == osmium::object_order_type_id_reverse_version()(node1, way));
+
+        REQUIRE(false == osmium::object_equal_type_id_version()(node1, node2));
+        REQUIRE(true == osmium::object_equal_type_id_version()(node2, node3));
+
+        REQUIRE(true == osmium::object_equal_type_id()(node1, node2));
+        REQUIRE(true == osmium::object_equal_type_id()(node2, node3));
+
+        REQUIRE(false == osmium::object_equal_type_id_version()(node1, way));
+        REQUIRE(false == osmium::object_equal_type_id_version()(node1, relation));
+        REQUIRE(false == osmium::object_equal_type_id()(node1, relation));
     }
 
-    auto it = buffer.begin();
-    osmium::Node& node1 = static_cast<osmium::Node&>(*it);
-    osmium::Node& node2 = static_cast<osmium::Node&>(*(++it));
-
-    node1.set_id(10);
-    node1.set_version(1);
-    node2.set_id(15);
-    node2.set_version(2);
-    REQUIRE(true == (node1 < node2));
-    REQUIRE(false == (node1 > node2));
-    node1.set_id(20);
-    node1.set_version(1);
-    node2.set_id(20);
-    node2.set_version(2);
-    REQUIRE(true == (node1 < node2));
-    REQUIRE(false == (node1 > node2));
-    node1.set_id(-10);
-    node1.set_version(2);
-    node2.set_id(-15);
-    node2.set_version(1);
-    REQUIRE(true == (node1 < node2));
-    REQUIRE(false == (node1 > node2));
-}
-
-SECTION("order_types") {
-    osmium::memory::Buffer buffer(10 * 1000);
-
-    {
-        // add node 1
-        osmium::builder::NodeBuilder node_builder(buffer);
-        osmium::Node& node = node_builder.object();
-        REQUIRE(osmium::item_type::node == node.type());
-
-        node.set_id(3);
-        node.set_version(3);
-        node_builder.add_user("testuser");
-
-        buffer.commit();
-    }
-
-    {
-        // add node 2
-        osmium::builder::NodeBuilder node_builder(buffer);
-        osmium::Node& node = node_builder.object();
-        REQUIRE(osmium::item_type::node == node.type());
-
-        node.set_id(3);
-        node.set_version(4);
-        node_builder.add_user("testuser");
-
-        buffer.commit();
-    }
-
-    {
-        // add node 3
-        osmium::builder::NodeBuilder node_builder(buffer);
-        osmium::Node& node = node_builder.object();
-        REQUIRE(osmium::item_type::node == node.type());
-
-        node.set_id(3);
-        node.set_version(4);
-        node_builder.add_user("testuser");
-
-        buffer.commit();
-    }
-
-    {
-        // add way
-        osmium::builder::WayBuilder way_builder(buffer);
-        osmium::Way& way = way_builder.object();
-        REQUIRE(osmium::item_type::way == way.type());
-
-        way.set_id(2);
-        way.set_version(2);
-        way_builder.add_user("testuser");
-
-        buffer.commit();
-    }
-
-    {
-        // add relation
-        osmium::builder::RelationBuilder relation_builder(buffer);
-        osmium::Relation& relation = relation_builder.object();
-        REQUIRE(osmium::item_type::relation == relation.type());
-
-        relation.set_id(1);
-        relation.set_version(1);
-        relation_builder.add_user("testuser");
-
-        buffer.commit();
-    }
-
-    auto it = buffer.begin();
-    const osmium::Node& node1 = static_cast<const osmium::Node&>(*it);
-    const osmium::Node& node2 = static_cast<const osmium::Node&>(*(++it));
-    const osmium::Node& node3 = static_cast<const osmium::Node&>(*(++it));
-    const osmium::Way& way = static_cast<const osmium::Way&>(*(++it));
-    const osmium::Relation& relation = static_cast<const osmium::Relation&>(*(++it));
-
-    REQUIRE(true == (node1 < node2));
-    REQUIRE(true == (node2 < way));
-    REQUIRE(false == (node2 > way));
-    REQUIRE(true == (way < relation));
-    REQUIRE(true == (node1 < relation));
-
-    REQUIRE(true == osmium::object_order_type_id_version()(node1, node2));
-    REQUIRE(true == osmium::object_order_type_id_reverse_version()(node2, node1));
-    REQUIRE(true == osmium::object_order_type_id_version()(node1, way));
-    REQUIRE(true == osmium::object_order_type_id_reverse_version()(node1, way));
-
-    REQUIRE(false == osmium::object_equal_type_id_version()(node1, node2));
-    REQUIRE(true == osmium::object_equal_type_id_version()(node2, node3));
-
-    REQUIRE(true == osmium::object_equal_type_id()(node1, node2));
-    REQUIRE(true == osmium::object_equal_type_id()(node2, node3));
-
-    REQUIRE(false == osmium::object_equal_type_id_version()(node1, way));
-    REQUIRE(false == osmium::object_equal_type_id_version()(node1, relation));
-    REQUIRE(false == osmium::object_equal_type_id()(node1, relation));
-}
-
 }
diff --git a/test/t/basic/test_timestamp.cpp b/test/t/basic/test_timestamp.cpp
index 453b94b..f015730 100644
--- a/test/t/basic/test_timestamp.cpp
+++ b/test/t/basic/test_timestamp.cpp
@@ -6,40 +6,57 @@
 
 TEST_CASE("Timestamp") {
 
-SECTION("can be default initialized to invalid value") {
-    osmium::Timestamp t;
-    REQUIRE(0 == t);
-    REQUIRE("" == t.to_iso());
-}
-
-SECTION("invalid value is zero") {
-    osmium::Timestamp t(static_cast<time_t>(0));
-    REQUIRE(0 == t);
-    REQUIRE("" == t.to_iso());
-}
-
-SECTION("can be initialized from time_t") {
-    osmium::Timestamp t(static_cast<time_t>(1));
-    REQUIRE(1 == t);
-    REQUIRE("1970-01-01T00:00:01Z" == t.to_iso());
-}
-
-SECTION("can be initialized from string") {
-    osmium::Timestamp t("2000-01-01T00:00:00Z");
-    REQUIRE("2000-01-01T00:00:00Z" == t.to_iso());
-}
-
-SECTION("can be compared") {
-    osmium::Timestamp t1(10);
-    osmium::Timestamp t2(50);
-    REQUIRE(t1 < t2);
-}
-
-SECTION("can be written to stream") {
-    std::stringstream ss;
-    osmium::Timestamp t(1);
-    ss << t;
-    REQUIRE("1970-01-01T00:00:01Z" == ss.str());
-}
+    SECTION("can be default initialized to invalid value") {
+        osmium::Timestamp t;
+        REQUIRE(0 == t);
+        REQUIRE("" == t.to_iso());
+    }
+
+    SECTION("invalid value is zero") {
+        osmium::Timestamp t(static_cast<time_t>(0));
+        REQUIRE(0 == t);
+        REQUIRE("" == t.to_iso());
+    }
+
+    SECTION("can be initialized from time_t") {
+        osmium::Timestamp t(static_cast<time_t>(1));
+        REQUIRE(1 == t);
+        REQUIRE("1970-01-01T00:00:01Z" == t.to_iso());
+    }
+
+    SECTION("can be initialized from string") {
+        osmium::Timestamp t("2000-01-01T00:00:00Z");
+        REQUIRE("2000-01-01T00:00:00Z" == t.to_iso());
+    }
+
+    SECTION("throws if initialized from bad string") {
+        REQUIRE_THROWS_AS(osmium::Timestamp("x"), std::invalid_argument);
+    }
+
+    SECTION("can be implicitly cast to time_t") {
+        osmium::Timestamp t(4242);
+        time_t x = t;
+        REQUIRE(x == 4242);
+    }
+
+    SECTION("uint32_t can be initialized from Timestamp") {
+        osmium::Timestamp t(4242);
+        uint32_t x { t };
+
+        REQUIRE(x == 4242);
+    }
+
+    SECTION("can be compared") {
+        osmium::Timestamp t1(10);
+        osmium::Timestamp t2(50);
+        REQUIRE(t1 < t2);
+    }
+
+    SECTION("can be written to stream") {
+        std::stringstream ss;
+        osmium::Timestamp t(1);
+        ss << t;
+        REQUIRE("1970-01-01T00:00:01Z" == ss.str());
+    }
 
 }
diff --git a/test/t/basic/test_types_from_string.cpp b/test/t/basic/test_types_from_string.cpp
new file mode 100644
index 0000000..2481ae8
--- /dev/null
+++ b/test/t/basic/test_types_from_string.cpp
@@ -0,0 +1,90 @@
+#include "catch.hpp"
+
+#include <osmium/osm/types.hpp>
+#include <osmium/osm/types_from_string.hpp>
+
+TEST_CASE("set ID from string") {
+    REQUIRE(osmium::string_to_object_id("0") == 0);
+    REQUIRE(osmium::string_to_object_id("17") == 17);
+    REQUIRE(osmium::string_to_object_id("-17") == -17);
+    REQUIRE(osmium::string_to_object_id("01") == 1);
+
+    REQUIRE_THROWS_AS(osmium::string_to_object_id(""), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_id(" "), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_id(" 22"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_id("x"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_id("0x1"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_id("12a"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_id("12345678901234567890"), std::range_error);
+}
+
+TEST_CASE("set type and ID from string") {
+    auto n17 = osmium::string_to_object_id("n17", osmium::osm_entity_bits::nwr);
+    REQUIRE(n17.first == osmium::item_type::node);
+    REQUIRE(n17.second == 17);
+
+    auto w42 = osmium::string_to_object_id("w42", osmium::osm_entity_bits::nwr);
+    REQUIRE(w42.first == osmium::item_type::way);
+    REQUIRE(w42.second == 42);
+
+    auto r_2 = osmium::string_to_object_id("r-2", osmium::osm_entity_bits::nwr);
+    REQUIRE(r_2.first == osmium::item_type::relation);
+    REQUIRE(r_2.second == -2);
+
+    auto x3 = osmium::string_to_object_id("3", osmium::osm_entity_bits::nwr);
+    REQUIRE(x3.first == osmium::item_type::undefined);
+    REQUIRE(x3.second == 3);
+
+    REQUIRE_THROWS_AS(osmium::string_to_object_id("", osmium::osm_entity_bits::nwr), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_id("n", osmium::osm_entity_bits::nwr), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_id("x3", osmium::osm_entity_bits::nwr), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_id("nx3", osmium::osm_entity_bits::nwr), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_id("n3", osmium::osm_entity_bits::way), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_id("n3a", osmium::osm_entity_bits::nwr), std::range_error);
+}
+
+TEST_CASE("set object version from string") {
+    REQUIRE(osmium::string_to_object_version("0") == 0);
+    REQUIRE(osmium::string_to_object_version("1") == 1);
+
+    REQUIRE_THROWS_AS(osmium::string_to_object_version("-1"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_version(""), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_version(" "), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_version(" 22"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_object_version("x"), std::range_error);
+}
+
+TEST_CASE("set changeset id from string") {
+    REQUIRE(osmium::string_to_changeset_id("0") == 0);
+    REQUIRE(osmium::string_to_changeset_id("1") == 1);
+
+    REQUIRE_THROWS_AS(osmium::string_to_changeset_id("-1"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_changeset_id(""), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_changeset_id(" "), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_changeset_id(" 22"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_changeset_id("x"), std::range_error);
+}
+
+TEST_CASE("set user id from string") {
+    REQUIRE(osmium::string_to_user_id("0") == 0);
+    REQUIRE(osmium::string_to_user_id("1") == 1);
+    REQUIRE(osmium::string_to_user_id("-1") == -1);
+
+    REQUIRE_THROWS_AS(osmium::string_to_user_id("-2"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_user_id(""), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_user_id(" "), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_user_id(" 22"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_user_id("x"), std::range_error);
+}
+
+TEST_CASE("set num changes from string") {
+    REQUIRE(osmium::string_to_num_changes("0") == 0);
+    REQUIRE(osmium::string_to_num_changes("1") == 1);
+
+    REQUIRE_THROWS_AS(osmium::string_to_num_changes("-1"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_num_changes(""), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_num_changes(" "), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_num_changes(" 22"), std::range_error);
+    REQUIRE_THROWS_AS(osmium::string_to_num_changes("x"), std::range_error);
+}
+
diff --git a/test/t/buffer/test_buffer_node.cpp b/test/t/buffer/test_buffer_node.cpp
index aabe6cb..9bc8f70 100644
--- a/test/t/buffer/test_buffer_node.cpp
+++ b/test/t/buffer/test_buffer_node.cpp
@@ -58,78 +58,78 @@ void check_node_2(osmium::Node& node) {
 
 TEST_CASE("Buffer_Node") {
 
-SECTION("buffer_node") {
-    constexpr size_t buffer_size = 10000;
-    unsigned char data[buffer_size];
+    SECTION("buffer_node") {
+        constexpr size_t buffer_size = 10000;
+        unsigned char data[buffer_size];
 
-    osmium::memory::Buffer buffer(data, buffer_size, 0);
+        osmium::memory::Buffer buffer(data, buffer_size, 0);
 
-    {
-        // add node 1
-        osmium::builder::NodeBuilder node_builder(buffer);
-        osmium::Node& node = node_builder.object();
-        REQUIRE(osmium::item_type::node == node.type());
-
-        node.set_id(1);
-        node.set_version(3);
-        node.set_visible(true);
-        node.set_changeset(333);
-        node.set_uid(21);
-        node.set_timestamp(123);
-        node.set_location(osmium::Location(3.5, 4.7));
-
-        node_builder.add_user("testuser");
-
-        buffer.commit();
-    }
-
-    {
-        // add node 2
-        osmium::builder::NodeBuilder node_builder(buffer);
-        osmium::Node& node = node_builder.object();
-        REQUIRE(osmium::item_type::node == node.type());
-
-        node.set_id(2);
-        node.set_version(3);
-        node.set_visible(true);
-        node.set_changeset(333);
-        node.set_uid(21);
-        node.set_timestamp(123);
-        node.set_location(osmium::Location(3.5, 4.7));
-
-        node_builder.add_user("testuser");
+        {
+            // add node 1
+            osmium::builder::NodeBuilder node_builder(buffer);
+            osmium::Node& node = node_builder.object();
+            REQUIRE(osmium::item_type::node == node.type());
+
+            node.set_id(1);
+            node.set_version(3);
+            node.set_visible(true);
+            node.set_changeset(333);
+            node.set_uid(21);
+            node.set_timestamp(123);
+            node.set_location(osmium::Location(3.5, 4.7));
+
+            node_builder.add_user("testuser");
+
+            buffer.commit();
+        }
 
         {
-            osmium::builder::TagListBuilder tag_builder(buffer, &node_builder);
-            tag_builder.add_tag("amenity", "bank");
-            tag_builder.add_tag("name", "OSM Savings");
+            // add node 2
+            osmium::builder::NodeBuilder node_builder(buffer);
+            osmium::Node& node = node_builder.object();
+            REQUIRE(osmium::item_type::node == node.type());
+
+            node.set_id(2);
+            node.set_version(3);
+            node.set_visible(true);
+            node.set_changeset(333);
+            node.set_uid(21);
+            node.set_timestamp(123);
+            node.set_location(osmium::Location(3.5, 4.7));
+
+            node_builder.add_user("testuser");
+
+            {
+                osmium::builder::TagListBuilder tag_builder(buffer, &node_builder);
+                tag_builder.add_tag("amenity", "bank");
+                tag_builder.add_tag("name", "OSM Savings");
+            }
+
+            buffer.commit();
         }
 
-        buffer.commit();
-    }
+        REQUIRE(2 == std::distance(buffer.begin(), buffer.end()));
+        int item_no = 0;
+        for (osmium::memory::Item& item : buffer) {
+            REQUIRE(osmium::item_type::node == item.type());
 
-    REQUIRE(2 == std::distance(buffer.begin(), buffer.end()));
-    int item_no = 0;
-    for (osmium::memory::Item& item : buffer) {
-        REQUIRE(osmium::item_type::node == item.type());
+            osmium::Node& node = static_cast<osmium::Node&>(item);
 
-        osmium::Node& node = static_cast<osmium::Node&>(item);
+            switch (item_no) {
+                case 0:
+                    check_node_1(node);
+                    break;
+                case 1:
+                    check_node_2(node);
+                    break;
+                default:
+                    break;
+            }
 
-        switch (item_no) {
-            case 0:
-                check_node_1(node);
-                break;
-            case 1:
-                check_node_2(node);
-                break;
-            default:
-                break;
-        }
+            ++item_no;
 
-        ++item_no;
+        }
 
     }
 
 }
-
-}
diff --git a/test/t/buffer/test_buffer_purge.cpp b/test/t/buffer/test_buffer_purge.cpp
index 10cdfe7..a72db1b 100644
--- a/test/t/buffer/test_buffer_purge.cpp
+++ b/test/t/buffer/test_buffer_purge.cpp
@@ -108,7 +108,7 @@ TEST_CASE("Purge data from buffer") {
             node_builder.object().set_removed(true);
         }
         buffer.commit();
-        size_t size2 = buffer.committed() - size1;
+
         REQUIRE(std::distance(buffer.begin(), buffer.end()) == 2);
 
         CallbackClass callback;
@@ -127,20 +127,20 @@ TEST_CASE("Purge data from buffer") {
             node_builder.add_user("testuser_longer_name");
         }
         buffer.commit();
-        size_t size1 = buffer.committed();
+
         {
             osmium::builder::NodeBuilder node_builder(buffer);
             node_builder.add_user("testuser");
             node_builder.object().set_removed(true);
         }
         buffer.commit();
-        size_t size2 = buffer.committed() - size1;
+
         {
             osmium::builder::NodeBuilder node_builder(buffer);
             node_builder.add_user("sn");
         }
         buffer.commit();
-        size_t size3 = buffer.committed() - (size1 + size2);
+
         REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3);
 
         CallbackClass callback;
@@ -159,21 +159,21 @@ TEST_CASE("Purge data from buffer") {
             node_builder.object().set_removed(true);
         }
         buffer.commit();
-        size_t size1 = buffer.committed();
+
         {
             osmium::builder::NodeBuilder node_builder(buffer);
             node_builder.add_user("testuser");
             node_builder.object().set_removed(true);
         }
         buffer.commit();
-        size_t size2 = buffer.committed() - size1;
+
         {
             osmium::builder::NodeBuilder node_builder(buffer);
             node_builder.add_user("sn");
             node_builder.object().set_removed(true);
         }
         buffer.commit();
-        size_t size3 = buffer.committed() - (size1 + size2);
+
         REQUIRE(std::distance(buffer.begin(), buffer.end()) == 3);
 
         CallbackClass callback;
diff --git a/test/t/geom/test_exception.cpp b/test/t/geom/test_exception.cpp
new file mode 100644
index 0000000..fe95043
--- /dev/null
+++ b/test/t/geom/test_exception.cpp
@@ -0,0 +1,16 @@
+#include "catch.hpp"
+
+#include <string>
+
+#include <osmium/geom/factory.hpp>
+
+TEST_CASE("Geometry exception") {
+
+    SECTION("geometry_error") {
+        osmium::geometry_error e("some error message", "node", 17);
+        REQUIRE(e.id() == 17);
+        REQUIRE(std::string(e.what()) == "some error message (node_id=17)");
+    }
+
+}
+
diff --git a/test/t/geom/test_factory_with_projection.cpp b/test/t/geom/test_factory_with_projection.cpp
index 9bc8c06..42fc864 100644
--- a/test/t/geom/test_factory_with_projection.cpp
+++ b/test/t/geom/test_factory_with_projection.cpp
@@ -10,32 +10,32 @@
 
 TEST_CASE("Projection") {
 
-SECTION("point_mercator") {
-    osmium::geom::WKTFactory<osmium::geom::MercatorProjection> factory(2);
+    SECTION("point_mercator") {
+        osmium::geom::WKTFactory<osmium::geom::MercatorProjection> factory(2);
 
-    std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))};
-    REQUIRE(std::string{"POINT(356222.37 467961.14)"} == wkt);
-}
+        std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))};
+        REQUIRE(std::string {"POINT(356222.37 467961.14)"} == wkt);
+    }
 
-SECTION("point_epsg_3857") {
-    osmium::geom::WKTFactory<osmium::geom::Projection> factory(osmium::geom::Projection(3857), 2);
+    SECTION("point_epsg_3857") {
+        osmium::geom::WKTFactory<osmium::geom::Projection> factory(osmium::geom::Projection(3857), 2);
 
-    std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))};
-    REQUIRE(std::string{"POINT(356222.37 467961.14)"} == wkt);
-}
+        std::string wkt {factory.create_point(osmium::Location(3.2, 4.2))};
+        REQUIRE(std::string {"POINT(356222.37 467961.14)"} == wkt);
+    }
 
-SECTION("wkb_with_parameter") {
-    osmium::geom::WKBFactory<osmium::geom::Projection> wkb_factory(osmium::geom::Projection(3857), osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
-    osmium::geom::GEOSFactory<osmium::geom::Projection> geos_factory(osmium::geom::Projection(3857));
+    SECTION("wkb_with_parameter") {
+        osmium::geom::WKBFactory<osmium::geom::Projection> wkb_factory(osmium::geom::Projection(3857), osmium::geom::wkb_type::wkb, osmium::geom::out_type::hex);
+        osmium::geom::GEOSFactory<osmium::geom::Projection> geos_factory(osmium::geom::Projection(3857));
 
-    std::string wkb = wkb_factory.create_point(osmium::Location(3.2, 4.2));
-    std::unique_ptr<geos::geom::Point> geos_point = geos_factory.create_point(osmium::Location(3.2, 4.2));
-    REQUIRE(geos_to_wkb(geos_point.get()) == wkb);
-}
+        std::string wkb = wkb_factory.create_point(osmium::Location(3.2, 4.2));
+        std::unique_ptr<geos::geom::Point> geos_point = geos_factory.create_point(osmium::Location(3.2, 4.2));
+        REQUIRE(geos_to_wkb(geos_point.get()) == wkb);
+    }
 
-SECTION("cleanup") {
-    // trying to make valgrind happy, but there is still a memory leak in proj library
-    pj_deallocate_grids();
-}
+    SECTION("cleanup") {
+        // trying to make valgrind happy, but there is still a memory leak in proj library
+        pj_deallocate_grids();
+    }
 
 }
diff --git a/test/t/geom/test_mercator.cpp b/test/t/geom/test_mercator.cpp
index a6ce2ee..cc16e55 100644
--- a/test/t/geom/test_mercator.cpp
+++ b/test/t/geom/test_mercator.cpp
@@ -4,34 +4,34 @@
 
 TEST_CASE("Mercator") {
 
-SECTION("mercator_projection") {
-    osmium::geom::MercatorProjection projection;
-    REQUIRE(3857 == projection.epsg());
-    REQUIRE("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" == projection.proj_string());
-}
-
-SECTION("low_level_mercator_functions") {
-    osmium::geom::Coordinates c1(17.839, -3.249);
-    osmium::geom::Coordinates r1 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c1));
-    REQUIRE(r1.x == Approx(c1.x).epsilon(0.000001));
-    REQUIRE(r1.y == Approx(c1.y).epsilon(0.000001));
-
-    osmium::geom::Coordinates c2(-89.2, 15.915);
-    osmium::geom::Coordinates r2 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c2));
-    REQUIRE(r2.x == Approx(c2.x).epsilon(0.000001));
-    REQUIRE(r2.y == Approx(c2.y).epsilon(0.000001));
-
-    osmium::geom::Coordinates c3(180.0, 85.0);
-    osmium::geom::Coordinates r3 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c3));
-    REQUIRE(r3.x == Approx(c3.x).epsilon(0.000001));
-    REQUIRE(r3.y == Approx(c3.y).epsilon(0.000001));
-}
-
-SECTION("mercator_bounds") {
-    osmium::Location mmax(180.0, osmium::geom::MERCATOR_MAX_LAT);
-    osmium::geom::Coordinates c = osmium::geom::lonlat_to_mercator(mmax);
-    REQUIRE(c.x == Approx(c.y).epsilon(0.001));
-    REQUIRE(osmium::geom::detail::y_to_lat(osmium::geom::detail::lon_to_x(180.0)) == Approx(osmium::geom::MERCATOR_MAX_LAT).epsilon(0.0000001));
-}
+    SECTION("mercator_projection") {
+        osmium::geom::MercatorProjection projection;
+        REQUIRE(3857 == projection.epsg());
+        REQUIRE("+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs" == projection.proj_string());
+    }
+
+    SECTION("low_level_mercator_functions") {
+        osmium::geom::Coordinates c1(17.839, -3.249);
+        osmium::geom::Coordinates r1 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c1));
+        REQUIRE(r1.x == Approx(c1.x).epsilon(0.000001));
+        REQUIRE(r1.y == Approx(c1.y).epsilon(0.000001));
+
+        osmium::geom::Coordinates c2(-89.2, 15.915);
+        osmium::geom::Coordinates r2 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c2));
+        REQUIRE(r2.x == Approx(c2.x).epsilon(0.000001));
+        REQUIRE(r2.y == Approx(c2.y).epsilon(0.000001));
+
+        osmium::geom::Coordinates c3(180.0, 85.0);
+        osmium::geom::Coordinates r3 = osmium::geom::mercator_to_lonlat(osmium::geom::lonlat_to_mercator(c3));
+        REQUIRE(r3.x == Approx(c3.x).epsilon(0.000001));
+        REQUIRE(r3.y == Approx(c3.y).epsilon(0.000001));
+    }
+
+    SECTION("mercator_bounds") {
+        osmium::Location mmax(180.0, osmium::geom::MERCATOR_MAX_LAT);
+        osmium::geom::Coordinates c = osmium::geom::lonlat_to_mercator(mmax);
+        REQUIRE(c.x == Approx(c.y).epsilon(0.001));
+        REQUIRE(osmium::geom::detail::y_to_lat(osmium::geom::detail::lon_to_x(180.0)) == Approx(osmium::geom::MERCATOR_MAX_LAT).epsilon(0.0000001));
+    }
 
 }
diff --git a/test/t/geom/test_projection.cpp b/test/t/geom/test_projection.cpp
index 2257d7f..5885410 100644
--- a/test/t/geom/test_projection.cpp
+++ b/test/t/geom/test_projection.cpp
@@ -1,5 +1,7 @@
 #include "catch.hpp"
 
+#include <random>
+
 #include <osmium/geom/factory.hpp>
 #include <osmium/geom/mercator_projection.hpp>
 #include <osmium/geom/projection.hpp>
@@ -128,4 +130,20 @@ SECTION("compare_mercators") {
     }
 }
 
+SECTION("compare_mercators") {
+    osmium::geom::MercatorProjection projection_merc;
+    osmium::geom::Projection projection_3857(3857);
+
+    std::random_device rd;
+    std::mt19937 gen(rd());
+    std::uniform_real_distribution<> dis_x(-180.0, 180.0);
+    std::uniform_real_distribution<> dis_y(-90.0, 90.0);
+
+    for (int n = 0; n < 100000; ++n) {
+        const osmium::Location loc(dis_x(gen), dis_y(gen));
+        REQUIRE(projection_merc(loc).x == Approx(projection_3857(loc).x).epsilon(0.1));
+        REQUIRE(projection_merc(loc).y == Approx(projection_3857(loc).y).epsilon(0.1));
+    }
+}
+
 }
diff --git a/test/t/geom/test_tile.cpp b/test/t/geom/test_tile.cpp
new file mode 100644
index 0000000..e80cb96
--- /dev/null
+++ b/test/t/geom/test_tile.cpp
@@ -0,0 +1,93 @@
+#include "catch.hpp"
+
+#include <sstream>
+
+#include <osmium/geom/tile.hpp>
+
+#include "helper.hpp"
+
+#include "test_tile_data.hpp"
+
+TEST_CASE("Tile") {
+
+    SECTION("x0.0 y0.0 zoom 0") {
+        osmium::Location l(0.0, 0.0);
+
+        osmium::geom::Tile t(0, l);
+
+        REQUIRE(t.x == 0);
+        REQUIRE(t.y == 0);
+        REQUIRE(t.z == 0);
+    }
+
+    SECTION("x180.0 y90.0 zoom 0") {
+        osmium::Location l(180.0, 90.0);
+
+        osmium::geom::Tile t(0, l);
+
+        REQUIRE(t.x == 0);
+        REQUIRE(t.y == 0);
+        REQUIRE(t.z == 0);
+    }
+
+    SECTION("x180.0 y90.0 zoom 4") {
+        osmium::Location l(180.0, 90.0);
+
+        osmium::geom::Tile t(4, l);
+
+        REQUIRE(t.x == (1 << 4) - 1);
+        REQUIRE(t.y == 0);
+        REQUIRE(t.z == 4);
+    }
+
+    SECTION("x0.0 y0.0 zoom 4") {
+        osmium::Location l(0.0, 0.0);
+
+        osmium::geom::Tile t(4, l);
+
+        auto n = 1 << (4-1);
+        REQUIRE(t.x == n);
+        REQUIRE(t.y == n);
+        REQUIRE(t.z == 4);
+    }
+
+    SECTION("equality") {
+        osmium::geom::Tile a(4, 3, 4);
+        osmium::geom::Tile b(4, 3, 4);
+        osmium::geom::Tile c(4, 4, 3);
+        REQUIRE(a == b);
+        REQUIRE(a != c);
+        REQUIRE(b != c);
+    }
+
+    SECTION("order") {
+        osmium::geom::Tile a(2, 3, 4);
+        osmium::geom::Tile b(4, 3, 4);
+        osmium::geom::Tile c(4, 4, 3);
+        osmium::geom::Tile d(4, 4, 2);
+        REQUIRE(a < b);
+        REQUIRE(a < c);
+        REQUIRE(b < c);
+        REQUIRE(d < c);
+    }
+
+    SECTION("tilelist") {
+        std::istringstream input_data(s);
+        while (input_data) {
+            double lon, lat;
+            uint32_t x, y, zoom;
+            input_data >> lon;
+            input_data >> lat;
+            input_data >> x;
+            input_data >> y;
+            input_data >> zoom;
+
+            osmium::Location l(lon, lat);
+            osmium::geom::Tile t(zoom, l);
+            REQUIRE(t.x == x);
+            REQUIRE(t.y == y);
+        }
+    }
+
+}
+
diff --git a/test/t/geom/test_tile_data.hpp b/test/t/geom/test_tile_data.hpp
new file mode 100644
index 0000000..e5c0953
--- /dev/null
+++ b/test/t/geom/test_tile_data.hpp
@@ -0,0 +1,475 @@
+
+std::string s = R"(127.4864358 16.8380041 223904 118630 18
+163.1103174 39.4760232 121 48 7
+-4.1372725 -22.5105386 31 36 6
+98.7193066 -36.2312406 1 1 1
+63.5773661 -13.47636 21 17 5
+-88.4518148 37.9805485 260 395 10
+-14.5903133 -28.2989812 3763 4767 13
+-13.4971239 -34.4080035 14 19 5
+-169.156223 -64.0900356 3 93 7
+27.1473191 -4.1993125 4713 4191 13
+-160.9733129 54.3684314 13 81 8
+129.0194139 14.2576156 439 235 9
+-69.5085993 -56.8253221 10057 22700 15
+-77.8387486 18.1961517 1162 1837 12
+-76.2695325 -18.2494296 147 282 9
+-91.594905 7.6698071 1 3 3
+-116.7926741 -20.6060813 179 571 10
+109.0552776 -1.9947569 52620 33131 16
+-156.1846426 -79.3817554 33 449 9
+-95.3403755 -27.8978407 1 4 3
+-55.1827573 -73.2293796 44 103 7
+-108.5207885 -48.0099293 50 167 8
+23.7540108 -15.3395164 9273 8898 14
+-155.6662842 -68.3295899 0 0 0
+75.8139119 30.9914252 363 209 9
+-135.8034544 64.7242469 0 1 2
+-48.743352 70.9392876 23 13 6
+-38.6968026 7.7867812 0 0 0
+-18.5234838 11.8557704 29395 30594 16
+-152.5632568 19.4069834 78 455 10
+-63.2089431 -80.5909713 0 0 0
+-94.1255611 -81.2028822 244 930 10
+175.0862205 -33.0865142 3 2 2
+-179.6241926 -37.0256609 1 625 10
+135.6783824 -38.6011643 459739 323170 19
+-139.6407533 -83.2495209 0 7 3
+-14.1336447 -56.5465949 58 88 7
+-30.7414568 -32.9543731 26 38 6
+10.3306861 73.2444693 1082 399 11
+-84.8379263 29.2363222 16 26 6
+-94.0685822 -39.5503996 7821 20309 15
+-86.5944356 -41.7491891 265 642 10
+11.9182172 -80.5613703 34937 58784 16
+91.8773752 -32.1741317 0 0 0
+126.2879157 20.5759564 1742 904 11
+-160.7743029 -47.3192128 27999 340565 19
+-4.2449045 -50.3288332 31 42 6
+-66.6857158 61.4380757 10 9 5
+169.7372317 -74.3365704 3 3 2
+87.4815328 75.6218888 95 21 7
+60.3544927 28.3165267 0 0 0
+-48.9614619 -59.3292497 2 5 3
+-123.2935018 -59.5454886 80 362 9
+-31.5909316 -14.8985476 13 17 5
+58.1862173 59.0957666 2710 1209 12
+-72.8881665 -2.2648849 1218 2073 12
+-33.7267461 8.6006817 106512 124785 18
+175.723181 46.4929742 7 2 3
+-127.1963326 76.0328786 0 0 0
+-161.7444367 -26.7634497 13293 151310 18
+-163.976298 -8.1169845 91 1070 11
+63.7757109 5.6049418 2 1 2
+12.7012434 22.1157713 70160 57276 17
+19.5832849 -25.0284049 1135 1171 11
+-22.6619642 54.8831276 111 81 8
+78.7736907 24.0919863 5888 3530 13
+121.9003045 -64.6256685 107 94 7
+-64.8766793 61.6655916 81 71 8
+113.0498445 -70.0016518 416 397 9
+-51.5116259 68.1532424 46781 31217 17
+-89.9005747 87.9523248 512 0 11
+59.2536822 -75.1520493 10 13 4
+17.375372 74.9259262 287448 93371 19
+75.6426945 8.4808632 186153 124873 18
+144.89589 75.1647661 14786 2875 14
+174.4350898 -29.268169 258091 153376 18
+-91.8773113 50.6182166 0 0 0
+5.3822308 45.1391794 134991 94156 18
+43.0978373 -10.0764237 324909 276895 19
+55.6682917 -40.9015342 21451 20470 15
+-37.584032 52.253723 6 5 4
+131.0141997 29.0271798 28309 13621 15
+69.8830721 -86.8548645 363918 524287 19
+-107.7917548 -76.9626654 26290 110787 17
+-57.1736642 49.9345991 2 2 3
+165.5170226 -84.9735363 982 1021 10
+-139.7208984 -73.8754873 1 12 4
+-154.5959463 -10.596718 4 33 6
+-106.4164918 36.5042686 3348 6405 14
+-92.9688259 -11.2262505 0 2 2
+138.2722283 8.1109779 7 3 3
+123.1389319 -40.3505677 862 637 10
+-171.4780435 24.9112482 0 13 5
+89.3668763 -63.1809434 392293 381781 19
+-79.6906176 -13.376312 9130 17612 15
+133.1329522 -28.9032634 445 298 9
+115.0369496 70.6965823 6 1 3
+-74.8926697 -78.9770185 2391 7144 13
+51.9175766 73.0184277 164 50 8
+178.1319784 43.7111421 509 186 9
+162.4098389 61.1595443 0 0 0
+7.6241126 -75.6182944 2 3 2
+172.8902767 28.5068027 125 53 7
+156.4030739 -76.7309238 478 431 9
+-131.3963189 62.2039684 1 2 3
+34.6057088 -36.2933264 2441 2491 12
+-3.3896413 -48.2734347 15 20 5
+83.528842 -48.219886 2998 2675 12
+16.6000512 -31.6322244 17894 19421 15
+-18.2425749 21.0749052 14 14 5
+35.6035336 46.9916228 78498 46105 17
+-109.3453091 -34.2312523 12 38 6
+88.4909962 58.6104749 47 19 6
+-129.7372783 21.7408241 571 1794 12
+25.7269392 -40.1240139 1 1 1
+26.3829579 5.248998 37570 31811 16
+141.1768247 -41.4653038 14617 10269 14
+151.3788752 -38.0160512 241302 161042 18
+-92.7471483 -35.391745 15883 39664 16
+142.5902623 -14.1023743 28 17 5
+179.3461485 -86.7573357 3 3 2
+-40.9746194 61.5689546 790 576 11
+128.6399735 -54.8895009 56186 44772 16
+-141.391583 -63.8272 0 2 2
+-3.5004218 19.3089998 4016 3648 13
+16.138208 -42.7868627 1 1 1
+78.502315 -18.91667 2 2 2
+-44.6311826 -15.7483106 98572 142686 18
+-120.431941 -39.3431529 0 0 0
+-70.4915024 25.4763412 9967 13984 15
+118.0711114 -66.5691073 211 192 8
+-114.945538 38.1764389 0 0 0
+142.991315 -46.3378741 14699 10577 14
+34.2770562 -84.7173756 312063 518834 19
+109.5563415 -85.9138266 105424 131071 17
+-171.8032643 70.1948223 0 3 4
+-90.8434294 89.7252122 126 0 9
+163.5677082 -53.9885419 3 2 2
+128.884016 -63.0732584 112461 95358 17
+116.5348575 -56.1616143 843 705 10
+-171.5770602 37.9225943 11 197 9
+48.1396653 -49.3932234 5191 5392 13
+-157.6786731 15.3895763 32507 239456 19
+15.7385145 -37.1364397 1113 1251 11
+137.5179466 61.2025671 1 0 1
+-70.4767722 42.9798424 19938 24086 16
+27.8867901 43.4642562 9461 5991 14
+68.5677462 -84.9560937 11312 16334 14
+-56.790151 -67.124147 179437 395476 19
+108.850832 43.3493384 26291 11996 15
+-139.0655153 8.2673118 1 7 4
+59.3726661 83.3857699 21788 1515 15
+-158.4718709 -12.2313178 1 17 5
+30.358316 -83.020501 2393 3871 12
+-169.9667863 71.1152107 1 13 6
+-89.9418297 -7.7258969 131156 273429 19
+-16.5050312 47.1843923 116 89 8
+-151.9641886 -36.3579042 5103 39881 16
+169.7406916 60.0950674 62 18 6
+32.6694543 -1.8435981 2 2 2
+97.7880811 77.5295169 50569 9674 16
+87.7554889 -85.6741368 389947 524287 19
+-19.0174909 29.1940122 0 0 1
+-86.6180019 82.6445919 1 0 2
+-50.1997802 -21.1913243 1476 2294 12
+73.8710121 75.2201385 5 1 3
+41.7434624 79.1410045 322937 65770 19
+-122.4312562 -68.4513916 10 48 6
+-54.6116448 -23.1761137 1 2 2
+-35.8774263 -51.5317442 102 170 8
+179.2976644 -76.2729885 127 107 7
+-111.6934402 -85.2696836 1 7 3
+-24.2137947 28.4102025 3 3 3
+-168.0816395 -64.0317696 16 375 9
+43.2514876 6.4266793 2540 1974 12
+25.5877932 49.3486828 4 2 3
+-133.3671657 -25.0795973 8 36 6
+-140.2002274 -37.3713396 452 2507 12
+73.6326557 -21.9898207 360 288 9
+-83.0901448 -69.1893461 2205 6305 13
+-32.83227 -11.1061854 3348 4350 13
+-151.8897244 14.6891958 0 0 0
+-118.1125151 82.3085937 704 288 12
+119.9903922 1.4884267 53 31 6
+-116.9455865 -48.1971821 0 2 2
+111.4279587 -52.6024326 828 688 10
+-78.9207774 28.6183104 147207 218615 19
+45.1367631 24.1416251 40 27 6
+115.0905685 7.2971256 6714 3929 13
+-58.0837371 -55.4033522 43 87 7
+-44.6785779 83.0751777 6158 877 14
+80.2002966 84.3087089 2960 91 12
+-167.3118039 -59.2698371 72 1445 11
+139.9974902 -74.5994693 455 419 9
+-27.5858357 -36.1890547 6936 9960 14
+68.8572424 20.2096749 353 226 9
+-168.7172825 69.1607462 2053 15104 16
+62.1361934 74.6007777 44079 11896 16
+-102.9645124 65.3735325 112191 135144 19
+178.8434887 18.6231394 65325 29316 16
+-138.0494072 -80.3786289 29 228 8
+33.3858934 62.9029909 155382 71699 18
+-1.6915643 74.737228 1 0 2
+18.2680756 6.6441482 1 0 1
+34.0511738 -28.6862516 2 2 2
+-24.0705994 -0.6026568 28386 32877 16
+-176.8765092 -14.3252022 568 35403 16
+154.6553417 -79.7390666 1903 1809 11
+151.6055263 59.552346 14 4 4
+-101.7681729 45.3498263 113933 187876 19
+-52.3703658 -89.8260406 181 511 9
+-85.7937259 16.5632413 2143 3713 13
+114.7030999 6.6846014 104 61 7
+13.6027861 88.6863877 2 0 2
+60.5726928 38.256536 43794 25219 16
+141.0903381 -35.4468007 7306 4959 13
+-38.8182454 -55.2332792 200 350 9
+179.7818018 -79.6600052 523970 462627 19
+-45.2102175 82.7381225 196301 32058 19
+-36.5811826 -35.3991346 0 0 0
+-26.7719575 -75.7014102 223154 435372 19
+77.3451937 -65.4613594 1 1 1
+-37.3286225 38.3250599 51945 50407 17
+70.8235495 35.3870419 365288 206979 19
+99.8381373 -83.4101655 1 1 1
+25.8851923 67.415543 9370 3991 14
+-12.3393758 68.5972027 7 3 4
+-142.9400273 -36.2904852 1 9 4
+110.085689 -50.9514972 422467 348655 19
+121.5093657 53.9939447 54888 21044 16
+151.1307841 -33.0798435 58 38 6
+100.4346499 -57.0129759 6 5 3
+114.0564882 63.4987339 428250 141474 19
+-17.3978586 19.2981593 57 57 7
+-129.101039 -66.3423574 579 3067 12
+117.6639917 14.0568892 433504 241463 19
+97.7014577 69.1261732 202216 60490 18
+-174.7931333 81.4174709 7583 46045 19
+47.205341 -48.8608019 20680 21495 15
+167.416796 -7.5063098 247 133 8
+63.6744589 21.3392888 0 0 0
+-106.0719817 33.8353547 53832 104862 18
+-48.4051268 27.0438152 191648 221209 19
+64.5675545 -27.0683253 5 4 3
+110.7800249 -35.8217841 0 0 0
+164.8954138 81.9558801 31393 2538 15
+-54.4595565 -23.0128432 357 579 10
+43.2542709 67.694011 2540 989 12
+137.6537233 -78.7052092 28913 28450 15
+76.9271563 -60.0777348 0 0 0
+-145.0240216 47.3869213 6367 22947 16
+136.2328853 -73.4534547 3598 3304 12
+48.8047148 74.3001023 81 23 7
+-144.6978756 33.6270048 200 820 11
+64.6723407 -37.196427 21 19 5
+-58.3356482 -19.0812366 22148 36307 16
+19.611535 -31.3013119 0 0 0
+-63.8923439 -25.9560287 1 2 2
+-152.5288047 -30.9747477 0 1 1
+-99.8509683 -69.0258965 7295 25181 15
+-22.9015207 -21.0915082 13 17 5
+161.4402438 61.1430241 15539 4652 14
+-102.9499976 -1.2629414 1 4 3
+-159.7995801 51.1570142 14 85 8
+10.5265485 -86.8526122 67 127 7
+47.1581041 -73.1628608 646 823 10
+2.7577906 -66.3257104 66540 98133 17
+96.9653593 -9.8258675 6 4 3
+-55.4199846 25.3325116 2834 3499 13
+-47.3590106 59.5085133 2 2 3
+179.1441501 22.1324479 7 3 3
+16.2649007 47.8480398 4 2 3
+-27.8373963 -0.3926597 27700 32839 16
+-99.7938802 -48.8013068 912 2685 12
+133.4858024 28.457146 3 1 2
+-174.7621207 -25.8318084 238 9409 14
+106.856378 19.640472 203 113 8
+31.4361995 -80.2488406 76981 116886 17
+148.6539554 -70.0347611 116 99 7
+45.1821773 17.5961595 80 57 7
+-30.3630114 71.3238998 54481 27877 17
+106.547704 44.9731358 6520 2947 13
+-132.8057564 23.3928795 67 221 9
+116.6455816 -10.1097592 52 33 6
+172.5512559 -53.2307289 3 2 2
+-114.2084519 42.834332 2994 6030 14
+91.0172087 87.6025 1 0 1
+-101.666812 -84.7858729 7130 32495 15
+-14.8074931 68.3277393 0 0 0
+140.3354277 -7.8709618 14 8 4
+-130.4961987 -69.4777523 9011 50594 16
+-121.239479 1.0896367 1 3 3
+-141.2241052 3.4495348 56471 257117 19
+42.6041377 -19.1328846 2532 2269 12
+141.3750885 -1.8110503 468036 264781 19
+-26.545021 -30.9641274 218 302 9
+87.6154866 10.1978751 5 3 3
+88.002559 -33.4108714 762 612 10
+170.9910642 -7.1925019 3993 2130 12
+-66.5680487 -88.1144993 165197 524287 19
+-71.5925378 45.720316 0 0 0
+-16.457642 57.1688038 930 625 11
+20.8379624 -11.1485568 35 33 6
+-72.9399538 -16.6504502 1 2 2
+52.8746845 -71.5213577 10598 12927 14
+93.2392918 12.5073156 48 29 6
+-3.7803834 73.7748237 128319 49794 18
+93.5713795 -38.8308882 24 19 5
+87.3474619 -6.9970464 23 16 5
+161.2199467 48.5612779 30 11 5
+87.343969 -31.3153618 47 37 6
+60.5318627 58.0869948 42 19 6
+161.4388458 42.7612385 1 0 1
+14.1262999 -26.2101142 4 4 3
+-135.776346 -21.4248586 503 2297 12
+-100.3980364 82.0236388 1 0 3
+-55.8093833 -87.2455194 0 0 0
+-15.6397352 64.454076 935 540 11
+131.5974423 39.1251372 110 48 7
+56.8045509 27.5658629 168 107 8
+146.3634996 14.9287416 3 1 2
+51.5699041 74.0370231 82 23 7
+29.3100901 33.7208834 152414 104963 18
+19.0622442 1.6781772 141 126 8
+-22.2575472 73.6467471 114864 50126 18
+125.0208495 47.8533914 3470 1426 12
+-92.4804503 29.8409387 995 1691 12
+113.6362202 86.1158625 0 0 0
+113.6203756 28.4267778 26 13 5
+124.472202 25.8347062 13856 6974 14
+79.3654306 -33.7864728 46 38 6
+-121.169233 -66.7642843 2 12 4
+179.5816311 1.0182064 0 0 0
+179.1123794 53.367624 0 0 0
+-25.6611689 32.6575586 14048 13236 15
+92.3370554 58.8306651 12 4 4
+142.2756448 63.6603101 3 1 2
+-162.8914282 -84.54406 6229 129034 17
+-54.0759935 53.1086102 0 0 0
+9.1647045 -39.4876873 34436 40604 16
+59.3708822 -43.9789068 10894 10425 14
+-16.6397572 46.5985252 929 723 11
+-36.1053642 -67.6882841 0 1 1
+-82.5851985 -69.5277766 4 12 4
+117.0059969 -63.2697116 432546 382068 19
+127.9740251 85.5341901 112129 0 17
+-13.9255265 59.7720207 120931 76457 18
+-167.7778382 41.7974194 69 761 11
+-100.1217856 -70.5624949 454 1599 11
+-42.4790343 -2.0016191 25034 33132 16
+-8.0782317 65.9680074 30 16 6
+-37.0481466 -55.2856125 203 350 9
+149.074382 16.7121888 58 28 6
+-53.1010518 -15.8492068 0 0 0
+50.8517919 -30.8366257 42025 38674 16
+-175.0355994 77.1929422 0 4 5
+-33.3221217 72.5636809 6 3 4
+-139.9201802 52.3400823 7296 21546 16
+86.0299659 23.4535286 1513 886 11
+105.6297238 -50.5521342 25998 21733 15
+104.2700533 36.5417507 206999 102450 18
+58.5238253 84.0320186 86843 3912 17
+-121.9177826 65.4393267 1321 2108 13
+-152.689211 57.4112922 39774 159515 19
+94.2583831 80.8625455 6240 801 13
+118.8199874 -37.6116567 53 39 6
+5.787144 7.0599251 4227 3934 13
+152.1682213 11.778732 236 119 8
+37.4503636 64.311498 2474 1084 12
+35.2616139 -59.8196291 38 45 6
+-20.2808572 -26.9983008 29075 37875 16
+-88.8541607 -20.4896703 66370 146320 18
+125.7726709 41.2603793 27 11 5
+-55.4322696 22.1767236 11338 14313 15
+-38.2393439 -56.4681037 6 11 4
+-139.4517643 -81.3621632 3 29 5
+-146.7954207 -74.6050826 6044 53642 16
+161.6654898 -71.7313805 15549 12957 14
+-85.4720514 -73.177675 2 6 3
+-25.7457381 -42.9842376 13 20 5
+-2.0766311 51.0142455 0 0 1
+-82.9179334 1.4865326 8836 16248 15
+140.5661302 61.5056993 28 9 5
+-30.2614099 35.5786378 54518 51659 17
+-49.2072142 -38.6965571 0 1 1
+124.6587534 9.5039576 433 242 9
+-113.4516741 81.4585593 24229 11410 17
+-4.5134723 68.229217 3 1 3
+75.4838529 -44.997406 2906 2622 12
+6.4715239 28.6900832 8486 6828 14
+91.9187555 -24.8192643 6187 4679 13
+-28.2510181 -42.6238777 26 40 6
+-151.1042237 -21.4553189 2630 18384 15
+-149.6272551 0.4235911 1382 8172 14
+-51.9171488 74.630423 0 0 0
+-36.5660117 37.242858 101 99 8
+91.7136677 -30.9077617 772 604 10
+61.6846009 -85.9378164 5 7 3
+11.3772008 69.40183 34839 14980 16
+82.6825938 9.4496443 11954 7759 14
+61.8446231 -40.0114106 2751 2545 12
+-51.6223196 -11.7880324 5 8 4
+113.0309076 -73.4376173 13336 13217 14
+-169.3808275 -72.7209175 0 25 5
+-98.5653414 -80.0893122 231 910 10
+-20.4653707 29.9801495 3 3 3
+78.9156686 2.599349 0 0 0
+76.0635255 -71.0823347 2913 3216 12
+-26.1185551 22.3616029 28013 28589 16
+177.8803853 -56.3662368 4071 2828 12
+-157.7926463 78.4998022 15 34 8
+168.6834344 -34.5535211 7934 4934 13
+-77.208013 -44.0964079 0 1 1
+-56.6162078 28.1240365 10 13 5
+8.6548899 72.178831 137374 53767 18
+-27.9342497 8.2525327 1 1 2
+91.6356971 -13.5230128 6181 4406 13
+-161.9980398 -75.4443511 0 13 4
+46.8556576 -27.1078679 5162 4737 13
+147.2806954 -48.1491071 465 334 9
+-168.2679875 -29.0171568 0 2 2
+10.0251187 -3.144812 8 8 4
+109.0281873 81.9713348 26307 2528 15
+-129.6281276 -36.9614028 73359 320148 19
+7.3831244 -18.3270273 2132 2260 12
+-34.4625217 53.2837646 52988 42524 17
+129.8855275 -26.30807 3525 2358 12
+-69.6374195 -45.7769025 10045 21081 15
+59.9868336 50.3716565 43688 22120 16
+126.4653338 -75.607391 1743 1698 11
+-34.6459616 53.2502443 51 41 7
+152.2415169 -71.7528837 1 1 1
+-83.2126752 32.6685119 35239 52939 17
+178.6388805 70.5727365 31 7 5
+-153.5646223 -81.9491561 2 29 5
+178.2668159 11.8222247 0 0 0
+-27.136902 18.4287365 6 7 4
+-104.7744923 -64.7769211 54777 193540 18
+58.2318889 9.5897974 2710 1938 12
+138.9707487 -65.2734433 14516 12149 14
+-115.29331 -84.3402223 2944 16033 14
+-66.3487123 -81.8340211 20 58 6
+76.1802855 40.4007156 364 193 9
+-77.5026834 -30.653231 145 301 9
+-52.6095007 -79.7155889 11595 28942 15
+-2.5594899 -31.6276806 7 9 4
+-58.6267217 41.4851391 2 2 3
+-0.7245205 -70.7329433 509 801 10
+161.2822996 -79.0311957 124257 114418 17
+144.8720197 80.1059269 14785 1811 14
+159.3764075 -23.5100054 0 0 0
+82.3109493 -7.0673699 95504 68115 17
+94.3931949 63.0474034 97 34 7
+87.4523391 -73.2690527 3043 3297 12
+101.4256455 -0.8267694 12 8 4
+-112.7666152 -82.3670281 12239 61007 16
+-82.0948447 -38.0810449 8 19 5
+113.2906728 -19.193243 104 70 7
+16.953131 78.402684 35 8 6
+-81.7974685 -53.9596787 34 86 7
+69.8051889 58.1416894 181902 78760 18
+-9.2435464 -53.4296594 3 5 3
+30.0415066 11.4884737 4 3 3
+125.2704157 -69.6324197 54 49 6
+-41.2060435 63.8501787 789 548 11
+120.407662 -4.9889504 6835 4209 13
+92.0345558 -8.0809262 24761 17121 15
+127.1061722 83.1311204 6988 428 13
+-178.9414629 64.0086678 770 69897 18
+49.0743035 -4.3000419 10425 8387 14
+-45.109002 -55.1435498 1534 2803 12
+3.2795498 75.95918 32 10 6
+)";
+
diff --git a/test/t/geom/test_wkt.cpp b/test/t/geom/test_wkt.cpp
index ff1417c..5383074 100644
--- a/test/t/geom/test_wkt.cpp
+++ b/test/t/geom/test_wkt.cpp
@@ -74,6 +74,14 @@ SECTION("linestring_with_two_same_locations") {
     });
 
     REQUIRE_THROWS_AS(factory.create_linestring(wnl), osmium::geometry_error);
+
+    try {
+        factory.create_linestring(wnl);
+    } catch (osmium::geometry_error& e) {
+        REQUIRE(e.id() == 0);
+        REQUIRE(std::string(e.what()) == "need at least two points for linestring");
+    }
+
     REQUIRE_THROWS_AS(factory.create_linestring(wnl, osmium::geom::use_nodes::unique, osmium::geom::direction::backward), osmium::geometry_error);
 
     {
diff --git a/test/t/index/test_id_to_location.cpp b/test/t/index/test_id_to_location.cpp
index fbb3f6a..4aca238 100644
--- a/test/t/index/test_id_to_location.cpp
+++ b/test/t/index/test_id_to_location.cpp
@@ -58,113 +58,113 @@ void test_func_real(TIndex& index) {
 
 TEST_CASE("IdToLocation") {
 
-SECTION("Dummy") {
-    typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> index_type;
+    SECTION("Dummy") {
+        typedef osmium::index::map::Dummy<osmium::unsigned_object_id_type, osmium::Location> index_type;
 
-    index_type index1;
+        index_type index1;
 
-    REQUIRE(0 == index1.size());
-    REQUIRE(0 == index1.used_memory());
+        REQUIRE(0 == index1.size());
+        REQUIRE(0 == index1.used_memory());
 
-    test_func_all<index_type>(index1);
+        test_func_all<index_type>(index1);
 
-    REQUIRE(0 == index1.size());
-    REQUIRE(0 == index1.used_memory());
-}
+        REQUIRE(0 == index1.size());
+        REQUIRE(0 == index1.used_memory());
+    }
 
-SECTION("DenseMemArray") {
-    typedef osmium::index::map::DenseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
+    SECTION("DenseMemArray") {
+        typedef osmium::index::map::DenseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
 
-    index_type index1;
-    index1.reserve(1000);
-    test_func_all<index_type>(index1);
+        index_type index1;
+        index1.reserve(1000);
+        test_func_all<index_type>(index1);
 
-    index_type index2;
-    index2.reserve(1000);
-    test_func_real<index_type>(index2);
-}
+        index_type index2;
+        index2.reserve(1000);
+        test_func_real<index_type>(index2);
+    }
 
 #ifdef __linux__
-SECTION("DenseMmapArray") {
-    typedef osmium::index::map::DenseMmapArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
+    SECTION("DenseMmapArray") {
+        typedef osmium::index::map::DenseMmapArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
 
-    index_type index1;
-    test_func_all<index_type>(index1);
+        index_type index1;
+        test_func_all<index_type>(index1);
 
-    index_type index2;
-    test_func_real<index_type>(index2);
-}
+        index_type index2;
+        test_func_real<index_type>(index2);
+    }
 #else
 # pragma message("not running 'DenseMapMmap' test case on this machine")
 #endif
 
-SECTION("DenseFileArray") {
-    typedef osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
+    SECTION("DenseFileArray") {
+        typedef osmium::index::map::DenseFileArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
 
-    index_type index1;
-    test_func_all<index_type>(index1);
+        index_type index1;
+        test_func_all<index_type>(index1);
 
-    index_type index2;
-    test_func_real<index_type>(index2);
-}
+        index_type index2;
+        test_func_real<index_type>(index2);
+    }
 
 #ifdef OSMIUM_WITH_SPARSEHASH
 
-SECTION("SparseMemTable") {
-    typedef osmium::index::map::SparseMemTable<osmium::unsigned_object_id_type, osmium::Location> index_type;
+    SECTION("SparseMemTable") {
+        typedef osmium::index::map::SparseMemTable<osmium::unsigned_object_id_type, osmium::Location> index_type;
 
-    index_type index1;
-    test_func_all<index_type>(index1);
+        index_type index1;
+        test_func_all<index_type>(index1);
 
-    index_type index2;
-    test_func_real<index_type>(index2);
-}
+        index_type index2;
+        test_func_real<index_type>(index2);
+    }
 
 #endif
 
-SECTION("SparseMemMap") {
-    typedef osmium::index::map::SparseMemMap<osmium::unsigned_object_id_type, osmium::Location> index_type;
+    SECTION("SparseMemMap") {
+        typedef osmium::index::map::SparseMemMap<osmium::unsigned_object_id_type, osmium::Location> index_type;
 
-    index_type index1;
-    test_func_all<index_type>(index1);
+        index_type index1;
+        test_func_all<index_type>(index1);
 
-    index_type index2;
-    test_func_real<index_type>(index2);
-}
+        index_type index2;
+        test_func_real<index_type>(index2);
+    }
 
-SECTION("SparseMemArray") {
-    typedef osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
+    SECTION("SparseMemArray") {
+        typedef osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location> index_type;
 
-    index_type index1;
+        index_type index1;
 
-    REQUIRE(0 == index1.size());
-    REQUIRE(0 == index1.used_memory());
+        REQUIRE(0 == index1.size());
+        REQUIRE(0 == index1.used_memory());
 
-    test_func_all<index_type>(index1);
+        test_func_all<index_type>(index1);
 
-    REQUIRE(2 == index1.size());
+        REQUIRE(2 == index1.size());
 
-    index_type index2;
-    test_func_real<index_type>(index2);
-}
+        index_type index2;
+        test_func_real<index_type>(index2);
+    }
 
-SECTION("Dynamic map choice") {
-    typedef osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location> map_type;
-    const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
+    SECTION("Dynamic map choice") {
+        typedef osmium::index::map::Map<osmium::unsigned_object_id_type, osmium::Location> map_type;
+        const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
 
-    std::vector<std::string> map_type_names = map_factory.map_types();
-    REQUIRE(map_type_names.size() >= 5);
+        std::vector<std::string> map_type_names = map_factory.map_types();
+        REQUIRE(map_type_names.size() >= 5);
 
-    for (const auto& map_type_name : map_type_names) {
-        std::unique_ptr<map_type> index1 = map_factory.create_map(map_type_name);
-        index1->reserve(1000);
-        test_func_all<map_type>(*index1);
+        for (const auto& map_type_name : map_type_names) {
+            std::unique_ptr<map_type> index1 = map_factory.create_map(map_type_name);
+            index1->reserve(1000);
+            test_func_all<map_type>(*index1);
 
-        std::unique_ptr<map_type> index2 = map_factory.create_map(map_type_name);
-        index2->reserve(1000);
-        test_func_real<map_type>(*index2);
+            std::unique_ptr<map_type> index2 = map_factory.create_map(map_type_name);
+            index2->reserve(1000);
+            test_func_real<map_type>(*index2);
+        }
     }
-}
 
 }
 
diff --git a/test/t/index/test_typed_mmap.cpp b/test/t/index/test_typed_mmap.cpp
deleted file mode 100644
index df95dc2..0000000
--- a/test/t/index/test_typed_mmap.cpp
+++ /dev/null
@@ -1,99 +0,0 @@
-#include "catch.hpp"
-
-#include <osmium/index/detail/typed_mmap.hpp>
-
-#if defined(_MSC_VER) || (defined(__GNUC__) && defined(_WIN32))
-  #include "win_mkstemp.hpp"
-#endif
-
-TEST_CASE("TypedMmap") {
-
-SECTION("Mmap") {
-    uint64_t* data = osmium::detail::typed_mmap<uint64_t>::map(10);
-
-    data[0] = 4ul;
-    data[3] = 9ul;
-    data[9] = 25ul;
-
-    REQUIRE(4ul == data[0]);
-    REQUIRE(9ul == data[3]);
-    REQUIRE(25ul == data[9]);
-
-    osmium::detail::typed_mmap<uint64_t>::unmap(data, 10);
-}
-
-SECTION("MmapSizeZero") {
-    REQUIRE_THROWS_AS(osmium::detail::typed_mmap<uint64_t>::map(0), std::system_error);
-}
-
-SECTION("MmapHugeSize") {
-    // this is a horrible hack to only run the test on 64bit machines.
-    if (sizeof(size_t) >= 8) {
-        REQUIRE_THROWS_AS(osmium::detail::typed_mmap<uint64_t>::map(1ULL << (sizeof(size_t) * 6)), std::system_error);
-    }
-}
-
-#ifdef __linux__
-SECTION("Remap") {
-    uint64_t* data = osmium::detail::typed_mmap<uint64_t>::map(10);
-
-    data[0] = 4ul;
-    data[3] = 9ul;
-    data[9] = 25ul;
-
-    uint64_t* new_data = osmium::detail::typed_mmap<uint64_t>::remap(data, 10, 1000);
-
-    REQUIRE(4ul == new_data[0]);
-    REQUIRE(9ul == new_data[3]);
-    REQUIRE(25ul == new_data[9]);
-}
-#else
-# pragma message("not running 'Remap' test case on this machine")
-#endif
-
-SECTION("FileSize") {
-    const int size = 100;
-    char filename[] = "test_mmap_file_size_XXXXXX";
-    const int fd = mkstemp(filename);
-    REQUIRE(fd > 0);
-    REQUIRE(0 == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
-    REQUIRE(0 == ftruncate(fd, size * sizeof(uint64_t)));
-    REQUIRE(size == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
-
-    osmium::detail::typed_mmap<uint64_t>::grow_file(size / 2, fd);
-    REQUIRE(size == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
-
-    osmium::detail::typed_mmap<uint64_t>::grow_file(size, fd);
-    REQUIRE(size == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
-
-    osmium::detail::typed_mmap<uint64_t>::grow_file(size * 2, fd);
-    REQUIRE((size * 2) == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
-
-    REQUIRE(0 == close(fd));
-    REQUIRE(0 == unlink(filename));
-}
-
-SECTION("GrowAndMap") {
-    const int size = 100;
-    char filename[] = "test_mmap_grow_and_map_XXXXXX";
-    const int fd = mkstemp(filename);
-    REQUIRE(fd > 0);
-
-    uint64_t* data = osmium::detail::typed_mmap<uint64_t>::grow_and_map(size, fd);
-    REQUIRE(size == osmium::detail::typed_mmap<uint64_t>::file_size(fd));
-
-    data[0] = 1ul;
-    data[1] = 8ul;
-    data[99] = 27ul;
-
-    REQUIRE(1ul == data[0]);
-    REQUIRE(8ul == data[1]);
-    REQUIRE(27ul == data[99]);
-
-    osmium::detail::typed_mmap<uint64_t>::unmap(data, size);
-
-    REQUIRE(0 == close(fd));
-    REQUIRE(0 == unlink(filename));
-}
-
-}
diff --git a/test/t/io/test_bzip2.cpp b/test/t/io/test_bzip2.cpp
index e6342e1..5cc30b4 100644
--- a/test/t/io/test_bzip2.cpp
+++ b/test/t/io/test_bzip2.cpp
@@ -9,25 +9,25 @@
 
 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;
+    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;
+            }
         }
-    }
 
-    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 03f9578..e8785d6 100644
--- a/test/t/io/test_file_formats.cpp
+++ b/test/t/io/test_file_formats.cpp
@@ -6,246 +6,246 @@
 
 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("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);
+    }
 
 }
 
diff --git a/test/t/tags/test_filter.cpp b/test/t/tags/test_filter.cpp
index e65f63d..eefa5b0 100644
--- a/test/t/tags/test_filter.cpp
+++ b/test/t/tags/test_filter.cpp
@@ -25,192 +25,192 @@ void check_filter(const osmium::TagList& tag_list, const TFilter filter, const s
 
 TEST_CASE("Filter") {
 
-SECTION("KeyFilter_matches_some_tags") {
-    osmium::tags::KeyFilter filter(false);
-    filter.add(true, "highway").add(true, "railway");
+    SECTION("KeyFilter_matches_some_tags") {
+        osmium::tags::KeyFilter filter(false);
+        filter.add(true, "highway").add(true, "railway");
 
-    osmium::memory::Buffer buffer(10240);
-    const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },  // match
-        { "name", "Main Street" }, // no match
-        { "source", "GPS" }        // no match
-    });
+        osmium::memory::Buffer buffer(10240);
+        const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },  // match
+            { "name", "Main Street" }, // no match
+            { "source", "GPS" }        // no match
+        });
 
-    std::vector<bool> results = { true, false, false };
+        std::vector<bool> results = { true, false, false };
 
-    check_filter(tag_list, filter, results);
-}
+        check_filter(tag_list, filter, results);
+    }
 
-SECTION("KeyFilter_iterator_filters_tags") {
-    osmium::tags::KeyFilter filter(false);
-    filter.add(true, "highway").add(true, "source");
-
-    osmium::memory::Buffer buffer(10240);
-    const osmium::TagList& tl = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },  // match
-        { "name", "Main Street" }, // no match
-        { "source", "GPS" }        // no match
-    });
-
-    osmium::tags::KeyFilter::iterator it(filter, tl.begin(), tl.end());
-    const osmium::tags::KeyFilter::iterator end(filter, tl.end(), tl.end());
-
-    REQUIRE(2 == std::distance(it, end));
-
-    REQUIRE(it != end);
-    REQUIRE(std::string("highway") == it->key());
-    REQUIRE(std::string("primary") == it->value());
-    ++it;
-    REQUIRE(std::string("source") == it->key());
-    REQUIRE(std::string("GPS") == it->value());
-    REQUIRE(++it == end);
-}
+    SECTION("KeyFilter_iterator_filters_tags") {
+        osmium::tags::KeyFilter filter(false);
+        filter.add(true, "highway").add(true, "source");
+
+        osmium::memory::Buffer buffer(10240);
+        const osmium::TagList& tl = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },  // match
+            { "name", "Main Street" }, // no match
+            { "source", "GPS" }        // no match
+        });
+
+        osmium::tags::KeyFilter::iterator it(filter, tl.begin(), tl.end());
+        const osmium::tags::KeyFilter::iterator end(filter, tl.end(), tl.end());
+
+        REQUIRE(2 == std::distance(it, end));
+
+        REQUIRE(it != end);
+        REQUIRE(std::string("highway") == it->key());
+        REQUIRE(std::string("primary") == it->value());
+        ++it;
+        REQUIRE(std::string("source") == it->key());
+        REQUIRE(std::string("GPS") == it->value());
+        REQUIRE(++it == end);
+    }
 
-SECTION("KeyValueFilter_matches_some_tags") {
-    osmium::tags::KeyValueFilter filter(false);
+    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 = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },
-        { "railway", "tram" },
-        { "source", "GPS" }
-    });
+        osmium::memory::Buffer buffer(10240);
+        const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },
+            { "railway", "tram" },
+            { "source", "GPS" }
+        });
 
-    std::vector<bool> results = {true, true, false};
+        std::vector<bool> results = {true, true, false};
 
-    check_filter(tag_list, filter, results);
-}
+        check_filter(tag_list, filter, results);
+    }
 
-SECTION("KeyValueFilter_ordering_matters") {
-    osmium::tags::KeyValueFilter filter1(false);
-    filter1.add(true, "highway").add(false, "highway", "road");
+    SECTION("KeyValueFilter_ordering_matters") {
+        osmium::tags::KeyValueFilter filter1(false);
+        filter1.add(true, "highway").add(false, "highway", "road");
 
-    osmium::tags::KeyValueFilter filter2(false);
-    filter2.add(false, "highway", "road").add(true, "highway");
+        osmium::tags::KeyValueFilter filter2(false);
+        filter2.add(false, "highway", "road").add(true, "highway");
 
-    osmium::memory::Buffer buffer(10240);
+        osmium::memory::Buffer buffer(10240);
 
-    const osmium::TagList& tag_list1 = osmium::builder::build_tag_list(buffer, {
-        { "highway", "road" },
-        { "name", "Main Street" }
-    });
+        const osmium::TagList& tag_list1 = osmium::builder::build_tag_list(buffer, {
+            { "highway", "road" },
+            { "name", "Main Street" }
+        });
 
-    const osmium::TagList& tag_list2 = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },
-        { "name", "Main Street" }
-    });
+        const osmium::TagList& tag_list2 = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },
+            { "name", "Main Street" }
+        });
 
-    check_filter(tag_list1, filter1, {true, false});
-    check_filter(tag_list1, filter2, {false, false});
-    check_filter(tag_list2, filter2, {true, false});
-}
+        check_filter(tag_list1, filter1, {true, false});
+        check_filter(tag_list1, filter2, {false, false});
+        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 = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },
-        { "railway", "tram" },
-        { "source", "GPS" }
-    });
+        osmium::memory::Buffer buffer(10240);
+        const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },
+            { "railway", "tram" },
+            { "source", "GPS" }
+        });
 
-    REQUIRE( osmium::tags::match_any_of(tag_list, filter));
-    REQUIRE(!osmium::tags::match_all_of(tag_list, filter));
-    REQUIRE(!osmium::tags::match_none_of(tag_list, filter));
-}
+        REQUIRE( osmium::tags::match_any_of(tag_list, filter));
+        REQUIRE(!osmium::tags::match_all_of(tag_list, 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 = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },
-        { "name", "Main Street" }
-    });
+        osmium::memory::Buffer buffer(10240);
+        const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },
+            { "name", "Main Street" }
+        });
 
-    REQUIRE( osmium::tags::match_any_of(tag_list, filter));
-    REQUIRE( osmium::tags::match_all_of(tag_list, filter));
-    REQUIRE(!osmium::tags::match_none_of(tag_list, filter));
-}
+        REQUIRE( osmium::tags::match_any_of(tag_list, filter));
+        REQUIRE( osmium::tags::match_all_of(tag_list, 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 = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },
-        { "name", "Main Street" }
-    });
+        osmium::memory::Buffer buffer(10240);
+        const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },
+            { "name", "Main Street" }
+        });
 
-    REQUIRE(!osmium::tags::match_any_of(tag_list, filter));
-    REQUIRE(!osmium::tags::match_all_of(tag_list, filter));
-    REQUIRE( osmium::tags::match_none_of(tag_list, filter));
-}
+        REQUIRE(!osmium::tags::match_any_of(tag_list, filter));
+        REQUIRE(!osmium::tags::match_all_of(tag_list, 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);
-    const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },
-        { "railway", "tram" },
-        { "source", "GPS" }
-    });
+    SECTION("KeyValueFilter_matches_against_taglist_with_any_called_with_rvalue") {
+        osmium::memory::Buffer buffer(10240);
+        const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },
+            { "railway", "tram" },
+            { "source", "GPS" }
+        });
 
-    REQUIRE(osmium::tags::match_any_of(tag_list,
-        osmium::tags::KeyValueFilter().add(true, "highway", "primary").add(true, "name")));
-}
+        REQUIRE(osmium::tags::match_any_of(tag_list,
+                                           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"));
-
-    osmium::memory::Buffer buffer(10240);
-    const osmium::TagList& tag_list1 = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary_link" },
-        { "source", "GPS" }
-    });
-    const osmium::TagList& tag_list2 = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },
-        { "source", "GPS" }
-    });
-
-    check_filter(tag_list1, filter, {true, false});
-    check_filter(tag_list2, filter, {false, false});
-}
+    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 = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary_link" },
+            { "source", "GPS" }
+        });
+        const osmium::TagList& tag_list2 = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },
+            { "source", "GPS" }
+        });
+
+        check_filter(tag_list1, filter, {true, false});
+        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");
-    filter.add(true, "name", r);
+    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 = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },
-        { "name", "Hauptstraße" }
-    });
+        osmium::memory::Buffer buffer(10240);
+        const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },
+            { "name", "Hauptstraße" }
+        });
 
-    check_filter(tag_list, filter, {false, true});
-}
+        check_filter(tag_list, filter, {false, true});
+    }
 
-SECTION("KeyPrefixFilter_matches_some_tags") {
-    osmium::tags::KeyPrefixFilter filter(false);
-    filter.add(true, "name:");
+    SECTION("KeyPrefixFilter_matches_some_tags") {
+        osmium::tags::KeyPrefixFilter filter(false);
+        filter.add(true, "name:");
 
-    osmium::memory::Buffer buffer(10240);
-    const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },
-        { "name:de", "Hauptstraße" }
-    });
+        osmium::memory::Buffer buffer(10240);
+        const osmium::TagList& tag_list = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },
+            { "name:de", "Hauptstraße" }
+        });
 
-    check_filter(tag_list, filter, {false, true});
-}
+        check_filter(tag_list, filter, {false, true});
+    }
 
 }
diff --git a/test/t/tags/test_operators.cpp b/test/t/tags/test_operators.cpp
index e2c22a4..33a53c2 100644
--- a/test/t/tags/test_operators.cpp
+++ b/test/t/tags/test_operators.cpp
@@ -8,54 +8,54 @@
 
 TEST_CASE("Operators") {
 
-SECTION("Equal") {
-    osmium::memory::Buffer buffer1(10240);
-    {
-        osmium::builder::TagListBuilder tl_builder(buffer1);
-        tl_builder.add_tag("highway", "primary");
-        tl_builder.add_tag("name", "Main Street");
-        tl_builder.add_tag("source", "GPS");
+    SECTION("Equal") {
+        osmium::memory::Buffer buffer1(10240);
+        {
+            osmium::builder::TagListBuilder tl_builder(buffer1);
+            tl_builder.add_tag("highway", "primary");
+            tl_builder.add_tag("name", "Main Street");
+            tl_builder.add_tag("source", "GPS");
+        }
+        buffer1.commit();
+
+        osmium::memory::Buffer buffer2(10240);
+        {
+            osmium::builder::TagListBuilder tl_builder(buffer2);
+            tl_builder.add_tag("highway", "primary");
+        }
+        buffer2.commit();
+
+        const osmium::TagList& tl1 = buffer1.get<const osmium::TagList>(0);
+        const osmium::TagList& tl2 = buffer2.get<const osmium::TagList>(0);
+
+        auto tagit1 = tl1.begin();
+        auto tagit2 = tl2.begin();
+        REQUIRE(*tagit1 == *tagit2);
+        ++tagit1;
+        REQUIRE(!(*tagit1 == *tagit2));
     }
-    buffer1.commit();
 
-    osmium::memory::Buffer buffer2(10240);
-    {
-        osmium::builder::TagListBuilder tl_builder(buffer2);
-        tl_builder.add_tag("highway", "primary");
+    SECTION("Order") {
+        osmium::memory::Buffer buffer(10240);
+        {
+            osmium::builder::TagListBuilder tl_builder(buffer);
+            tl_builder.add_tag("highway", "residential");
+            tl_builder.add_tag("highway", "primary");
+            tl_builder.add_tag("name", "Main Street");
+            tl_builder.add_tag("amenity", "post_box");
+        }
+        buffer.commit();
+
+        const osmium::TagList& tl = buffer.get<const osmium::TagList>(0);
+        const osmium::Tag& t1 = *(tl.begin());
+        const osmium::Tag& t2 = *(std::next(tl.begin(), 1));
+        const osmium::Tag& t3 = *(std::next(tl.begin(), 2));
+        const osmium::Tag& t4 = *(std::next(tl.begin(), 3));
+
+        REQUIRE(t2 < t1);
+        REQUIRE(t1 < t3);
+        REQUIRE(t2 < t3);
+        REQUIRE(t4 < t1);
     }
-    buffer2.commit();
-
-    const osmium::TagList& tl1 = buffer1.get<const osmium::TagList>(0);
-    const osmium::TagList& tl2 = buffer2.get<const osmium::TagList>(0);
-
-    auto tagit1 = tl1.begin();
-    auto tagit2 = tl2.begin();
-    REQUIRE(*tagit1 == *tagit2);
-    ++tagit1;
-    REQUIRE(!(*tagit1 == *tagit2));
-}
-
-SECTION("Order") {
-    osmium::memory::Buffer buffer(10240);
-    {
-        osmium::builder::TagListBuilder tl_builder(buffer);
-        tl_builder.add_tag("highway", "residential");
-        tl_builder.add_tag("highway", "primary");
-        tl_builder.add_tag("name", "Main Street");
-        tl_builder.add_tag("amenity", "post_box");
-    }
-    buffer.commit();
-
-    const osmium::TagList& tl = buffer.get<const osmium::TagList>(0);
-    const osmium::Tag& t1 = *(tl.begin());
-    const osmium::Tag& t2 = *(std::next(tl.begin(), 1));
-    const osmium::Tag& t3 = *(std::next(tl.begin(), 2));
-    const osmium::Tag& t4 = *(std::next(tl.begin(), 3));
-
-    REQUIRE(t2 < t1);
-    REQUIRE(t1 < t3);
-    REQUIRE(t2 < t3);
-    REQUIRE(t4 < t1);
-}
 
 }
diff --git a/test/t/tags/test_tag_list.cpp b/test/t/tags/test_tag_list.cpp
index b25d8ca..c2512d1 100644
--- a/test/t/tags/test_tag_list.cpp
+++ b/test/t/tags/test_tag_list.cpp
@@ -6,71 +6,71 @@
 
 TEST_CASE("tag_list") {
 
-SECTION("can_be_created_from_initializer_list") {
-    osmium::memory::Buffer buffer(10240);
-
-    const osmium::TagList& tl = osmium::builder::build_tag_list(buffer, {
-        { "highway", "primary" },
-        { "name", "Main Street" },
-        { "source", "GPS" }
-    });
-
-    REQUIRE(osmium::item_type::tag_list == tl.type());
-    REQUIRE(3 == tl.size());
-    REQUIRE(std::string("highway") == tl.begin()->key());
-    REQUIRE(std::string("primary") == tl.begin()->value());
-}
-
-SECTION("can_be_created_from_map") {
-    osmium::memory::Buffer buffer(10240);
-
-    const osmium::TagList& tl = osmium::builder::build_tag_list_from_map(buffer, std::map<const char*, const char*>({
-        { "highway", "primary" },
-        { "name", "Main Street" }
-    }));
-
-    REQUIRE(osmium::item_type::tag_list == tl.type());
-    REQUIRE(2 == tl.size());
-
-    if (std::string("highway") == tl.begin()->key()) {
+    SECTION("can_be_created_from_initializer_list") {
+        osmium::memory::Buffer buffer(10240);
+
+        const osmium::TagList& tl = osmium::builder::build_tag_list(buffer, {
+            { "highway", "primary" },
+            { "name", "Main Street" },
+            { "source", "GPS" }
+        });
+
+        REQUIRE(osmium::item_type::tag_list == tl.type());
+        REQUIRE(3 == tl.size());
+        REQUIRE(std::string("highway") == tl.begin()->key());
         REQUIRE(std::string("primary") == tl.begin()->value());
-        REQUIRE(std::string("name") == std::next(tl.begin(), 1)->key());
-        REQUIRE(std::string("Main Street") == std::next(tl.begin(), 1)->value());
-    } else {
-        REQUIRE(std::string("highway") == std::next(tl.begin(), 1)->key());
-        REQUIRE(std::string("primary") == std::next(tl.begin(), 1)->value());
-        REQUIRE(std::string("name") == tl.begin()->key());
-        REQUIRE(std::string("Main Street") == tl.begin()->value());
     }
-}
 
-SECTION("can_be_created_with_callback") {
-    osmium::memory::Buffer buffer(10240);
+    SECTION("can_be_created_from_map") {
+        osmium::memory::Buffer buffer(10240);
+
+        const osmium::TagList& tl = osmium::builder::build_tag_list_from_map(buffer, std::map<const char*, const char*>({
+            { "highway", "primary" },
+            { "name", "Main Street" }
+        }));
+
+        REQUIRE(osmium::item_type::tag_list == tl.type());
+        REQUIRE(2 == tl.size());
+
+        if (std::string("highway") == tl.begin()->key()) {
+            REQUIRE(std::string("primary") == tl.begin()->value());
+            REQUIRE(std::string("name") == std::next(tl.begin(), 1)->key());
+            REQUIRE(std::string("Main Street") == std::next(tl.begin(), 1)->value());
+        } else {
+            REQUIRE(std::string("highway") == std::next(tl.begin(), 1)->key());
+            REQUIRE(std::string("primary") == std::next(tl.begin(), 1)->value());
+            REQUIRE(std::string("name") == tl.begin()->key());
+            REQUIRE(std::string("Main Street") == tl.begin()->value());
+        }
+    }
 
-    const osmium::TagList& tl = osmium::builder::build_tag_list_from_func(buffer, [](osmium::builder::TagListBuilder& tlb) {
-        tlb.add_tag("highway", "primary");
-        tlb.add_tag("bridge", "true");
-    });
+    SECTION("can_be_created_with_callback") {
+        osmium::memory::Buffer buffer(10240);
 
-    REQUIRE(osmium::item_type::tag_list == tl.type());
-    REQUIRE(2 == tl.size());
-    REQUIRE(std::string("bridge") == std::next(tl.begin(), 1)->key());
-    REQUIRE(std::string("true") == std::next(tl.begin(), 1)->value());
-}
+        const osmium::TagList& tl = osmium::builder::build_tag_list_from_func(buffer, [](osmium::builder::TagListBuilder& tlb) {
+            tlb.add_tag("highway", "primary");
+            tlb.add_tag("bridge", "true");
+        });
+
+        REQUIRE(osmium::item_type::tag_list == tl.type());
+        REQUIRE(2 == tl.size());
+        REQUIRE(std::string("bridge") == std::next(tl.begin(), 1)->key());
+        REQUIRE(std::string("true") == std::next(tl.begin(), 1)->value());
+    }
 
-SECTION("returns_value_by_key") {
-    osmium::memory::Buffer buffer(10240);
+    SECTION("returns_value_by_key") {
+        osmium::memory::Buffer buffer(10240);
 
-    const osmium::TagList& tl = osmium::builder::build_tag_list_from_func(buffer, [](osmium::builder::TagListBuilder& tlb) {
-        tlb.add_tag("highway", "primary");
-        tlb.add_tag("bridge", "true");
-    });
+        const osmium::TagList& tl = osmium::builder::build_tag_list_from_func(buffer, [](osmium::builder::TagListBuilder& tlb) {
+            tlb.add_tag("highway", "primary");
+            tlb.add_tag("bridge", "true");
+        });
 
-    REQUIRE(std::string("primary") == tl.get_value_by_key("highway"));
-    REQUIRE(nullptr == tl.get_value_by_key("name"));
-    REQUIRE(std::string("foo") == tl.get_value_by_key("name", "foo"));
+        REQUIRE(std::string("primary") == tl.get_value_by_key("highway"));
+        REQUIRE(nullptr == tl.get_value_by_key("name"));
+        REQUIRE(std::string("foo") == tl.get_value_by_key("name", "foo"));
 
-    REQUIRE(std::string("true") == tl["bridge"]);
-}
+        REQUIRE(std::string("true") == tl["bridge"]);
+    }
 
 }
diff --git a/test/t/thread/test_pool.cpp b/test/t/thread/test_pool.cpp
index 4bbf9ee..5fa6bba 100644
--- a/test/t/thread/test_pool.cpp
+++ b/test/t/thread/test_pool.cpp
@@ -36,7 +36,7 @@ TEST_CASE("thread") {
     SECTION("can send job to thread pool") {
         auto& pool = osmium::thread::Pool::instance();
         result = 0;
-        auto future = pool.submit(test_job_ok{});
+        auto future = pool.submit(test_job_ok {});
 
         // wait a bit for the other thread to get a chance to run
         std::this_thread::sleep_for(std::chrono::milliseconds(1));
@@ -50,7 +50,7 @@ TEST_CASE("thread") {
 
     SECTION("can send job to thread pool") {
         auto& pool = osmium::thread::Pool::instance();
-        auto future = pool.submit(test_job_with_result{});
+        auto future = pool.submit(test_job_with_result {});
 
         REQUIRE(future.get() == 42);
     }
@@ -59,8 +59,7 @@ TEST_CASE("thread") {
         auto& pool = osmium::thread::Pool::instance();
         result = 0;
 
-        bool got_exception = false;
-        auto future = pool.submit(test_job_throw{});
+        auto future = pool.submit(test_job_throw {});
 
         REQUIRE_THROWS_AS(future.get(), std::runtime_error);
     }
diff --git a/test/t/util/test_data_file.cpp b/test/t/util/test_data_file.cpp
new file mode 100644
index 0000000..3f432f9
--- /dev/null
+++ b/test/t/util/test_data_file.cpp
@@ -0,0 +1,81 @@
+#include "catch.hpp"
+
+#include <cstring>
+
+#include <osmium/util/data_file.hpp>
+
+TEST_CASE("temporary file") {
+
+    SECTION("create/open") {
+        osmium::util::DataFile file;
+
+        REQUIRE(!!file);
+        int fd = file.fd();
+
+        REQUIRE(fd > 0);
+
+        const char buf[] = "foobar";
+        REQUIRE(::write(fd, buf, sizeof(buf)) == sizeof(buf));
+
+        file.close();
+
+        REQUIRE(!file);
+    }
+
+}
+
+TEST_CASE("named file") {
+
+    SECTION("create/open") {
+        {
+            osmium::util::DataFile file("test.data", true);
+
+            REQUIRE(!!file);
+            int fd = file.fd();
+
+            REQUIRE(fd > 0);
+
+            REQUIRE(file.size() == 0);
+
+            const char buf[] = "foobar";
+            REQUIRE(::write(fd, buf, sizeof(buf) - 1) == sizeof(buf) - 1);
+
+            file.close();
+
+            REQUIRE(!file);
+        }
+        {
+            osmium::util::DataFile file("test.data", false);
+
+            REQUIRE(!!file);
+            int fd = file.fd();
+
+            REQUIRE(fd > 0);
+
+            REQUIRE(file.size() == 6);
+
+            char buf[10];
+            int len = ::read(fd, buf, sizeof(buf));
+
+            REQUIRE(len == 6);
+            REQUIRE(!strncmp(buf, "foobar", 6));
+
+            file.close();
+
+            REQUIRE(!file);
+            REQUIRE(unlink("test.data") == 0);
+        }
+    }
+
+    SECTION("grow file") {
+        osmium::util::DataFile file("test.data", true);
+
+        REQUIRE(!!file);
+
+        REQUIRE(file.size() == 0);
+        file.grow(10);
+        REQUIRE(file.size() == 10);
+    }
+
+}
+
diff --git a/test/t/util/test_delta.cpp b/test/t/util/test_delta.cpp
new file mode 100644
index 0000000..a698204
--- /dev/null
+++ b/test/t/util/test_delta.cpp
@@ -0,0 +1,44 @@
+#include "catch.hpp"
+
+#include <osmium/util/delta.hpp>
+
+TEST_CASE("delta encode") {
+
+    osmium::util::DeltaEncode<int> x;
+
+    SECTION("int") {
+        REQUIRE(x.update(17) == 17);
+        REQUIRE(x.update(10) == -7);
+    }
+
+}
+
+TEST_CASE("delta decode") {
+
+    osmium::util::DeltaDecode<int> x;
+
+    SECTION("int") {
+        REQUIRE(x.update(17) == 17);
+        REQUIRE(x.update(10) == 27);
+    }
+
+}
+
+TEST_CASE("delta encode and decode") {
+
+    std::vector<int> a = { 5, -9, 22, 13, 0, 23 };
+
+    osmium::util::DeltaEncode<int> de;
+    std::vector<int> b;
+    for (int x : a) {
+        b.push_back(de.update(x));
+    }
+
+    osmium::util::DeltaDecode<int> dd;
+    std::vector<int> c;
+    for (int x : b) {
+        c.push_back(dd.update(x));
+    }
+
+}
+
diff --git a/test/t/util/test_double.cpp b/test/t/util/test_double.cpp
index 79e8509..6cc87a0 100644
--- a/test/t/util/test_double.cpp
+++ b/test/t/util/test_double.cpp
@@ -4,30 +4,30 @@
 
 TEST_CASE("Double") {
 
-SECTION("double2string") {
-    std::string s1;
-    osmium::util::double2string(s1, 1.123, 7);
-    REQUIRE(s1 == "1.123");
-
-    std::string s2;
-    osmium::util::double2string(s2, 1.000, 7);
-    REQUIRE(s2 == "1");
-
-    std::string s3;
-    osmium::util::double2string(s3, 0.0, 7);
-    REQUIRE(s3 == "0");
-
-    std::string s4;
-    osmium::util::double2string(s4, 0.020, 7);
-    REQUIRE(s4 == "0.02");
-
-    std::string s5;
-    osmium::util::double2string(s5, -0.020, 7);
-    REQUIRE(s5 == "-0.02");
-
-    std::string s6;
-    osmium::util::double2string(s6, -0.0, 7);
-    REQUIRE(s6 == "-0");
-}
+    SECTION("double2string") {
+        std::string s1;
+        osmium::util::double2string(s1, 1.123, 7);
+        REQUIRE(s1 == "1.123");
+
+        std::string s2;
+        osmium::util::double2string(s2, 1.000, 7);
+        REQUIRE(s2 == "1");
+
+        std::string s3;
+        osmium::util::double2string(s3, 0.0, 7);
+        REQUIRE(s3 == "0");
+
+        std::string s4;
+        osmium::util::double2string(s4, 0.020, 7);
+        REQUIRE(s4 == "0.02");
+
+        std::string s5;
+        osmium::util::double2string(s5, -0.020, 7);
+        REQUIRE(s5 == "-0.02");
+
+        std::string s6;
+        osmium::util::double2string(s6, -0.0, 7);
+        REQUIRE(s6 == "-0");
+    }
 }
 
diff --git a/test/t/util/test_file.cpp b/test/t/util/test_file.cpp
new file mode 100644
index 0000000..2787261
--- /dev/null
+++ b/test/t/util/test_file.cpp
@@ -0,0 +1,69 @@
+#include "catch.hpp"
+
+#include <osmium/util/file.hpp>
+
+#ifdef _WIN32
+// https://msdn.microsoft.com/en-us/library/ksazx244.aspx
+// https://msdn.microsoft.com/en-us/library/a9yf33zb.aspx
+class DoNothingInvalidParameterHandler {
+
+    static void invalid_parameter_handler(
+                    const wchar_t* expression,
+                    const wchar_t* function,
+                    const wchar_t* file,
+                    unsigned int line,
+                    uintptr_t pReserved
+                ) {
+        // do nothing
+    }
+
+    _invalid_parameter_handler old_handler;
+
+public:
+
+    DoNothingInvalidParameterHandler() :
+        old_handler(_set_invalid_parameter_handler(invalid_parameter_handler)) {
+    }
+
+    ~DoNothingInvalidParameterHandler() {
+        _set_invalid_parameter_handler(old_handler);
+    }
+
+}; // class InvalidParameterHandler
+#endif
+
+
+TEST_CASE("file_size") {
+
+#ifdef _WIN32
+    DoNothingInvalidParameterHandler handler;
+#endif
+
+    SECTION("illegal fd should throw") {
+        REQUIRE_THROWS_AS(osmium::util::file_size(-1), std::system_error);
+    }
+
+    SECTION("unused fd should throw") {
+        // its unlikely that fd 1000 is open...
+        REQUIRE_THROWS_AS(osmium::util::file_size(1000), std::system_error);
+    }
+
+}
+
+TEST_CASE("resize_file") {
+
+#ifdef _WIN32
+    DoNothingInvalidParameterHandler handler;
+#endif
+
+    SECTION("illegal fd should throw") {
+        REQUIRE_THROWS_AS(osmium::util::resize_file(-1, 10), std::system_error);
+    }
+
+    SECTION("unused fd should throw") {
+        // its unlikely that fd 1000 is open...
+        REQUIRE_THROWS_AS(osmium::util::resize_file(1000, 10), std::system_error);
+    }
+
+}
+
diff --git a/test/t/util/test_memory_mapping.cpp b/test/t/util/test_memory_mapping.cpp
new file mode 100644
index 0000000..7f95405
--- /dev/null
+++ b/test/t/util/test_memory_mapping.cpp
@@ -0,0 +1,393 @@
+#include "catch.hpp"
+
+#include <sys/types.h>
+#include <limits>
+
+#include <osmium/util/file.hpp>
+#include <osmium/util/memory_mapping.hpp>
+
+#if defined(_MSC_VER) || (defined(__GNUC__) && defined(_WIN32))
+#include "win_mkstemp.hpp"
+#endif
+
+static const size_t huge = std::numeric_limits<size_t>::max();
+
+TEST_CASE("anonymous mapping") {
+
+    SECTION("simple memory mapping should work") {
+        osmium::util::MemoryMapping mapping(1000);
+        REQUIRE(mapping.get_addr() != nullptr);
+
+        REQUIRE(mapping.size() >= 1000);
+
+        volatile int* addr = mapping.get_addr<int>();
+
+        REQUIRE(mapping.writable());
+
+        *addr = 42;
+        REQUIRE(*addr == 42);
+
+        REQUIRE(!!mapping);
+        mapping.unmap();
+        REQUIRE(!mapping);
+        mapping.unmap(); // second unmap is okay
+    }
+
+    SECTION("memory mapping of zero length should work") {
+        osmium::util::MemoryMapping mapping(0);
+        REQUIRE(mapping.get_addr() != nullptr);
+
+        REQUIRE(mapping.size() == osmium::util::get_pagesize());
+
+        REQUIRE(!!mapping);
+        mapping.unmap();
+        REQUIRE(!mapping);
+    }
+
+    SECTION("memory mapping a huge area should fail") {
+        REQUIRE_THROWS_AS(osmium::util::MemoryMapping mapping(huge),
+            std::system_error);
+    }
+
+    SECTION("moving a memory mapping should work") {
+        osmium::util::MemoryMapping mapping1(1000);
+        int* addr1 = mapping1.get_addr<int>();
+        *addr1 = 42;
+
+        REQUIRE(!!mapping1);
+        osmium::util::MemoryMapping mapping2(std::move(mapping1));
+        REQUIRE(!!mapping2);
+        REQUIRE(!mapping1);
+        mapping1.unmap();
+
+        int* addr2 = mapping2.get_addr<int>();
+        REQUIRE(*addr2 == 42);
+
+        mapping2.unmap();
+        REQUIRE(!mapping2);
+    }
+
+    SECTION("move assignment should work") {
+        osmium::util::MemoryMapping mapping1(1000);
+        osmium::util::MemoryMapping mapping2(1000);
+
+        REQUIRE(!!mapping1);
+        REQUIRE(!!mapping2);
+
+        int* addr1 = mapping1.get_addr<int>();
+        *addr1 = 42;
+
+        mapping2 = std::move(mapping1);
+        REQUIRE(!!mapping2);
+        REQUIRE(!mapping1);
+
+        int* addr2 = mapping2.get_addr<int>();
+        REQUIRE(*addr2 == 42);
+
+        mapping2.unmap();
+        REQUIRE(!mapping2);
+    }
+
+#ifdef __linux__
+    SECTION("remapping to larger size should work") {
+        osmium::util::MemoryMapping mapping(1000);
+        REQUIRE(mapping.size() >= 1000);
+
+        size_t size1 = mapping.size();
+
+        int* addr1 = mapping.get_addr<int>();
+        *addr1 = 42;
+
+        mapping.resize(8000);
+        REQUIRE(mapping.size() > size1);
+
+        int* addr2 = mapping.get_addr<int>();
+        REQUIRE(*addr2 == 42);
+    }
+
+    SECTION("remapping to smaller size should work") {
+        osmium::util::MemoryMapping mapping(8000);
+        REQUIRE(mapping.size() >= 1000);
+
+        size_t size1 = mapping.size();
+
+        int* addr1 = mapping.get_addr<int>();
+        *addr1 = 42;
+
+        mapping.resize(500);
+        REQUIRE(mapping.size() < size1);
+
+        int* addr2 = mapping.get_addr<int>();
+        REQUIRE(*addr2 == 42);
+    }
+#endif
+
+}
+
+TEST_CASE("file-based mapping") {
+
+    SECTION("writing to a mapped file should work") {
+        char filename[] = "test_mmap_write_XXXXXX";
+        const int fd = mkstemp(filename);
+        REQUIRE(fd > 0);
+
+        osmium::util::resize_file(fd, 100);
+
+        {
+            osmium::util::MemoryMapping mapping(100, true, fd);
+            REQUIRE(mapping.writable());
+
+            REQUIRE(!!mapping);
+            REQUIRE(mapping.size() >= 100);
+
+            *mapping.get_addr<int>() = 1234;
+
+            mapping.unmap();
+        }
+
+        REQUIRE(osmium::util::file_size(fd) == 100);
+
+        {
+            osmium::util::MemoryMapping mapping(100, false, fd);
+            REQUIRE(!mapping.writable());
+
+            REQUIRE(!!mapping);
+            REQUIRE(mapping.size() >= 100);
+            REQUIRE(*mapping.get_addr<int>() == 1234);
+
+            mapping.unmap();
+        }
+
+        REQUIRE(0 == close(fd));
+        REQUIRE(0 == unlink(filename));
+    }
+
+    SECTION("remapping to larger size should work") {
+        char filename[] = "test_mmap_grow_XXXXXX";
+        const int fd = mkstemp(filename);
+        REQUIRE(fd > 0);
+
+        osmium::util::MemoryMapping mapping(100, true, fd);
+        REQUIRE(mapping.size() >= 100);
+        size_t size1 = mapping.size();
+
+        int* addr1 = mapping.get_addr<int>();
+        *addr1 = 42;
+
+        mapping.resize(8000);
+        REQUIRE(mapping.size() >= 8000);
+        REQUIRE(mapping.size() > size1);
+
+        int* addr2 = mapping.get_addr<int>();
+        REQUIRE(*addr2 == 42);
+
+        mapping.unmap();
+
+        REQUIRE(0 == close(fd));
+        REQUIRE(0 == unlink(filename));
+    }
+
+    SECTION("remapping to smaller size should work") {
+        char filename[] = "test_mmap_shrink_XXXXXX";
+        const int fd = mkstemp(filename);
+        REQUIRE(fd > 0);
+
+        {
+            osmium::util::MemoryMapping mapping(8000, true, fd);
+            REQUIRE(mapping.size() >= 8000);
+            size_t size1 = mapping.size();
+
+            int* addr1 = mapping.get_addr<int>();
+            *addr1 = 42;
+
+            mapping.resize(50);
+            REQUIRE(mapping.size() >= 50);
+            REQUIRE(mapping.size() < size1);
+
+            int* addr2 = mapping.get_addr<int>();
+            REQUIRE(*addr2 == 42);
+        }
+
+        REQUIRE(0 == close(fd));
+        REQUIRE(0 == unlink(filename));
+    }
+}
+
+TEST_CASE("typed anonymous mapping") {
+
+    SECTION("simple memory mapping should work") {
+        osmium::util::TypedMemoryMapping<uint32_t> mapping(1000);
+        volatile uint32_t* addr = mapping.begin();
+
+        REQUIRE(mapping.writable());
+
+        *addr = 42;
+        REQUIRE(*addr == 42);
+
+        REQUIRE(!!mapping);
+        mapping.unmap();
+        REQUIRE(!mapping);
+        mapping.unmap(); // second unmap is okay
+    }
+
+    SECTION("memory mapping a huge area should fail") {
+        REQUIRE_THROWS_AS(osmium::util::TypedMemoryMapping<uint32_t> mapping(huge),
+            std::system_error);
+    }
+
+    SECTION("moving a memory mapping should work") {
+        osmium::util::TypedMemoryMapping<uint32_t> mapping1(1000);
+        uint32_t* addr1 = mapping1.begin();
+        *addr1 = 42;
+
+        REQUIRE(!!mapping1);
+        osmium::util::TypedMemoryMapping<uint32_t> mapping2(std::move(mapping1));
+        REQUIRE(!!mapping2);
+        REQUIRE(!mapping1);
+        mapping1.unmap();
+
+        auto addr2 = mapping2.begin();
+        REQUIRE(*addr2 == 42);
+
+        mapping2.unmap();
+        REQUIRE(!mapping2);
+    }
+
+    SECTION("move assignment should work") {
+        osmium::util::TypedMemoryMapping<uint32_t> mapping1(1000);
+        osmium::util::TypedMemoryMapping<uint32_t> mapping2(1000);
+
+        REQUIRE(!!mapping1);
+        REQUIRE(!!mapping2);
+
+        auto addr1 = mapping1.begin();
+        *addr1 = 42;
+
+        mapping2 = std::move(mapping1);
+        REQUIRE(!!mapping2);
+        REQUIRE(!mapping1);
+
+        auto addr2 = mapping2.begin();
+        REQUIRE(*addr2 == 42);
+
+        mapping2.unmap();
+        REQUIRE(!mapping2);
+    }
+
+#ifdef __linux__
+    SECTION("remapping to larger size should work") {
+        osmium::util::TypedMemoryMapping<uint32_t> mapping(1000);
+        REQUIRE(mapping.size() >= 1000);
+
+        auto addr1 = mapping.begin();
+        *addr1 = 42;
+
+        mapping.resize(8000);
+
+        auto addr2 = mapping.begin();
+        REQUIRE(*addr2 == 42);
+    }
+
+    SECTION("remapping to smaller size should work") {
+        osmium::util::TypedMemoryMapping<uint32_t> mapping(8000);
+        REQUIRE(mapping.size() >= 8000);
+
+        auto addr1 = mapping.begin();
+        *addr1 = 42;
+
+        mapping.resize(500);
+
+        auto addr2 = mapping.begin();
+        REQUIRE(*addr2 == 42);
+    }
+#endif
+
+}
+
+TEST_CASE("typed file-based mapping") {
+
+    SECTION("writing to a mapped file should work") {
+        char filename[] = "test_mmap_file_size_XXXXXX";
+        const int fd = mkstemp(filename);
+        REQUIRE(fd > 0);
+
+        osmium::util::resize_file(fd, 100);
+
+        {
+            osmium::util::TypedMemoryMapping<uint32_t> mapping(100, true, fd);
+            REQUIRE(mapping.writable());
+
+            REQUIRE(!!mapping);
+            REQUIRE(mapping.size() >= 100);
+
+            *mapping.begin() = 1234;
+
+            mapping.unmap();
+        }
+
+        {
+            osmium::util::TypedMemoryMapping<uint32_t> mapping(100, false, fd);
+            REQUIRE(!mapping.writable());
+
+            REQUIRE(!!mapping);
+            REQUIRE(mapping.size() >= 100);
+            REQUIRE(*mapping.begin() == 1234);
+
+            mapping.unmap();
+        }
+
+        REQUIRE(0 == close(fd));
+        REQUIRE(0 == unlink(filename));
+    }
+
+}
+
+TEST_CASE("anonymous memory mapping class") {
+
+    SECTION("simple memory mapping should work") {
+        osmium::util::AnonymousMemoryMapping mapping(1000);
+        REQUIRE(mapping.get_addr() != nullptr);
+
+        volatile int* addr = mapping.get_addr<int>();
+
+        REQUIRE(mapping.writable());
+
+        *addr = 42;
+        REQUIRE(*addr == 42);
+
+        REQUIRE(!!mapping);
+        mapping.unmap();
+        REQUIRE(!mapping);
+        mapping.unmap(); // second unmap is okay
+    }
+
+#ifdef __linux__
+    SECTION("remapping to larger size should work") {
+        osmium::util::AnonymousMemoryMapping mapping(1000);
+        REQUIRE(mapping.size() >= 1000);
+
+        int* addr1 = mapping.get_addr<int>();
+        *addr1 = 42;
+
+        mapping.resize(2000);
+
+        int* addr2 = mapping.get_addr<int>();
+        REQUIRE(*addr2 == 42);
+    }
+
+    SECTION("remapping to smaller size should work") {
+        osmium::util::AnonymousMemoryMapping mapping(2000);
+        REQUIRE(mapping.size() >= 2000);
+
+        int* addr1 = mapping.get_addr<int>();
+        *addr1 = 42;
+
+        mapping.resize(500);
+
+        int* addr2 = mapping.get_addr<int>();
+        REQUIRE(*addr2 == 42);
+    }
+#endif
+
+}
+
diff --git a/test/t/util/test_minmax.cpp b/test/t/util/test_minmax.cpp
new file mode 100644
index 0000000..8b40f85
--- /dev/null
+++ b/test/t/util/test_minmax.cpp
@@ -0,0 +1,68 @@
+#include "catch.hpp"
+
+#include <osmium/util/minmax.hpp>
+#include <osmium/osm/timestamp.hpp>
+
+TEST_CASE("minmax numeric") {
+
+    SECTION("min") {
+        osmium::min_op<int> x;
+        REQUIRE(x() == std::numeric_limits<int>::max());
+
+        x.update(17);
+        REQUIRE(x() == 17);
+
+        x.update(10);
+        REQUIRE(x() == 10);
+
+        x.update(22);
+        REQUIRE(x() == 10);
+    }
+
+    SECTION("max") {
+        osmium::max_op<uint32_t> x;
+        REQUIRE(x() == 0);
+
+        x.update(17);
+        REQUIRE(x() == 17);
+
+        x.update(10);
+        REQUIRE(x() == 17);
+
+        x.update(22);
+        REQUIRE(x() == 22);
+    }
+
+}
+
+TEST_CASE("minmax timestamp") {
+
+    SECTION("min") {
+        osmium::min_op<osmium::Timestamp> x;
+
+        x.update(osmium::Timestamp("2010-01-01T00:00:00Z"));
+        REQUIRE(x().to_iso() == "2010-01-01T00:00:00Z");
+
+        x.update(osmium::Timestamp("2015-01-01T00:00:00Z"));
+        REQUIRE(x().to_iso() == "2010-01-01T00:00:00Z");
+
+        x.update(osmium::Timestamp("2000-01-01T00:00:00Z"));
+        REQUIRE(x().to_iso() == "2000-01-01T00:00:00Z");
+    }
+
+    SECTION("max") {
+        osmium::max_op<osmium::Timestamp> x;
+
+        x.update(osmium::Timestamp("2010-01-01T00:00:00Z"));
+        REQUIRE(x().to_iso() == "2010-01-01T00:00:00Z");
+
+        x.update(osmium::Timestamp("2015-01-01T00:00:00Z"));
+        REQUIRE(x().to_iso() == "2015-01-01T00:00:00Z");
+
+        x.update(osmium::Timestamp("2000-01-01T00:00:00Z"));
+        REQUIRE(x().to_iso() == "2015-01-01T00:00:00Z");
+
+    }
+
+}
+
diff --git a/test/t/util/test_options.cpp b/test/t/util/test_options.cpp
index 203b9db..969f201 100644
--- a/test/t/util/test_options.cpp
+++ b/test/t/util/test_options.cpp
@@ -6,43 +6,43 @@
 
 TEST_CASE("Options") {
 
-SECTION("set_simple") {
-    osmium::util::Options o;
-    o.set("foo", "bar");
-    REQUIRE("bar" == o.get("foo"));
-    REQUIRE("" == o.get("empty"));
-    REQUIRE("default" == o.get("empty", "default"));
-    REQUIRE(!o.is_true("foo"));
-    REQUIRE(!o.is_true("empty"));
-    REQUIRE(1 == o.size());
-}
-
-SECTION("set_from_bool") {
-    osmium::util::Options o;
-    o.set("t", true);
-    o.set("f", false);
-    REQUIRE("true" == o.get("t"));
-    REQUIRE("false" == o.get("f"));
-    REQUIRE("" == o.get("empty"));
-    REQUIRE(o.is_true("t"));
-    REQUIRE(!o.is_true("f"));
-    REQUIRE(2 == o.size());
-}
-
-SECTION("set_from_single_string_with_equals") {
-    osmium::util::Options o;
-    o.set("foo=bar");
-    REQUIRE("bar" == o.get("foo"));
-    REQUIRE(1 == o.size());
-}
-
-SECTION("set_from_single_string_without_equals") {
-    osmium::util::Options o;
-    o.set("foo");
-    REQUIRE("true" == o.get("foo"));
-    REQUIRE(o.is_true("foo"));
-    REQUIRE(1 == o.size());
-}
+    SECTION("set_simple") {
+        osmium::util::Options o;
+        o.set("foo", "bar");
+        REQUIRE("bar" == o.get("foo"));
+        REQUIRE("" == o.get("empty"));
+        REQUIRE("default" == o.get("empty", "default"));
+        REQUIRE(!o.is_true("foo"));
+        REQUIRE(!o.is_true("empty"));
+        REQUIRE(1 == o.size());
+    }
+
+    SECTION("set_from_bool") {
+        osmium::util::Options o;
+        o.set("t", true);
+        o.set("f", false);
+        REQUIRE("true" == o.get("t"));
+        REQUIRE("false" == o.get("f"));
+        REQUIRE("" == o.get("empty"));
+        REQUIRE(o.is_true("t"));
+        REQUIRE(!o.is_true("f"));
+        REQUIRE(2 == o.size());
+    }
+
+    SECTION("set_from_single_string_with_equals") {
+        osmium::util::Options o;
+        o.set("foo=bar");
+        REQUIRE("bar" == o.get("foo"));
+        REQUIRE(1 == o.size());
+    }
+
+    SECTION("set_from_single_string_without_equals") {
+        osmium::util::Options o;
+        o.set("foo");
+        REQUIRE("true" == o.get("foo"));
+        REQUIRE(o.is_true("foo"));
+        REQUIRE(1 == o.size());
+    }
 
 }
 
diff --git a/test/t/util/test_string.cpp b/test/t/util/test_string.cpp
index c7b75cf..0960afe 100644
--- a/test/t/util/test_string.cpp
+++ b/test/t/util/test_string.cpp
@@ -4,54 +4,65 @@
 
 TEST_CASE("split_string") {
 
-SECTION("split_string string") {
-    std::string str { "foo,baramba,baz" };
-    std::vector<std::string> result = {"foo", "baramba", "baz"};
-
-    REQUIRE(result == osmium::split_string(str, ','));
-}
-
-SECTION("split_string string without sep") {
-    std::string str { "foo" };
-    std::vector<std::string> result = {"foo"};
-
-    REQUIRE(result == osmium::split_string(str, ','));
-}
-
-SECTION("split_string string with empty at end") {
-    std::string str { "foo,bar," };
-    std::vector<std::string> result = {"foo", "bar", ""};
-
-    REQUIRE(result == osmium::split_string(str, ','));
-}
-
-SECTION("split_string string with empty in middle") {
-    std::string str { "foo,,bar" };
-    std::vector<std::string> result = {"foo", "", "bar"};
-
-    REQUIRE(result == osmium::split_string(str, ','));
-}
-
-SECTION("split_string string with empty at start") {
-    std::string str { ",bar,baz" };
-    std::vector<std::string> result = {"", "bar", "baz"};
-
-    REQUIRE(result == osmium::split_string(str, ','));
-}
-
-SECTION("split_string sep") {
-    std::string str { "," };
-    std::vector<std::string> result = {"", ""};
-
-    REQUIRE(result == osmium::split_string(str, ','));
-}
-
-SECTION("split_string empty string") {
-    std::string str { "" };
-    std::vector<std::string> result;
-
-    REQUIRE(result == osmium::split_string(str, ','));
-}
+    SECTION("split_string string") {
+        std::string str { "foo,baramba,baz" };
+        std::vector<std::string> result = {"foo", "baramba", "baz"};
+
+        REQUIRE(result == osmium::split_string(str, ','));
+        REQUIRE(result == osmium::split_string(str, ',', true));
+    }
+
+    SECTION("split_string string without sep") {
+        std::string str { "foo" };
+        std::vector<std::string> result = {"foo"};
+
+        REQUIRE(result == osmium::split_string(str, ','));
+        REQUIRE(result == osmium::split_string(str, ',', true));
+    }
+
+    SECTION("split_string string with empty at end") {
+        std::string str { "foo,bar," };
+        std::vector<std::string> result = {"foo", "bar", ""};
+        std::vector<std::string> resultc = {"foo", "bar"};
+
+        REQUIRE(result == osmium::split_string(str, ','));
+        REQUIRE(resultc == osmium::split_string(str, ',', true));
+    }
+
+    SECTION("split_string string with empty in middle") {
+        std::string str { "foo,,bar" };
+        std::vector<std::string> result = {"foo", "", "bar"};
+        std::vector<std::string> resultc = {"foo", "bar"};
+
+        REQUIRE(result == osmium::split_string(str, ','));
+        REQUIRE(resultc == osmium::split_string(str, ',', true));
+    }
+
+    SECTION("split_string string with empty at start") {
+        std::string str { ",bar,baz" };
+        std::vector<std::string> result = {"", "bar", "baz"};
+        std::vector<std::string> resultc = {"bar", "baz"};
+
+        REQUIRE(result == osmium::split_string(str, ','));
+        REQUIRE(resultc == osmium::split_string(str, ',', true));
+    }
+
+    SECTION("split_string sep") {
+        std::string str { "," };
+        std::vector<std::string> result = {"", ""};
+        std::vector<std::string> resultc;
+
+        REQUIRE(result == osmium::split_string(str, ','));
+        REQUIRE(resultc == osmium::split_string(str, ',', true));
+    }
+
+    SECTION("split_string empty string") {
+        std::string str { "" };
+        std::vector<std::string> result;
+
+        REQUIRE(result == osmium::split_string(str, ','));
+        REQUIRE(result == osmium::split_string(str, ',', true));
+    }
 
 }
 

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