[osmium-tool] 01/07: Imported Upstream version 1.5.0

Bas Couwenberg sebastic at debian.org
Wed Jan 18 19:56:25 UTC 2017


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

sebastic pushed a commit to branch master
in repository osmium-tool.

commit 2c7d8bb1fdf43918fff6ffb31908dd914015ef8e
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Wed Jan 18 19:57:16 2017 +0100

    Imported Upstream version 1.5.0
---
 CHANGELOG.md                                       |  11 +-
 CMakeLists.txt                                     |   9 +-
 README.md                                          |  15 +-
 doc/manual.md                                      | 495 ---------------------
 doc/osmium-show.png                                | Bin 51357 -> 0 bytes
 extract-example-config/README.md                   |  17 +
 extract-example-config/berlin.geojson              | 122 +++++
 extract-example-config/berlin.poly                 | 115 +++++
 extract-example-config/brandenburg.poly            | 275 ++++++++++++
 extract-example-config/extracts.json               | 135 ++++++
 extract-example-config/hamburg.poly                |  71 +++
 extract-example-config/karlsruhe.osm.bz2           | Bin 0 -> 16630 bytes
 man/manpage.template                               |   2 +-
 man/osmium-add-locations-to-ways.md                |   2 +-
 man/osmium-apply-changes.md                        |   2 +-
 man/osmium-cat.md                                  |   2 +-
 man/osmium-changeset-filter.md                     |   2 +-
 man/osmium-check-refs.md                           |   2 +-
 man/osmium-derive-changes.md                       |   2 +-
 man/osmium-diff.md                                 |   6 +-
 man/osmium-extract.md                              | 361 +++++++++++++++
 man/osmium-file-formats.md                         |  25 +-
 man/osmium-fileinfo.md                             |   2 +-
 man/osmium-getid.md                                |   2 +-
 man/osmium-merge-changes.md                        |   2 +-
 man/osmium-merge.md                                |   2 +-
 man/osmium-renumber.md                             |   4 +-
 man/osmium-show.md                                 |   2 +-
 man/osmium-sort.md                                 |   2 +-
 man/osmium-time-filter.md                          |   2 +-
 man/osmium.md                                      |   5 +-
 .../osm-history-splitter2osmium-extract-config.sh  |  32 ++
 src/CMakeLists.txt                                 |   2 +-
 src/cmd.cpp                                        |  12 +-
 src/cmd.hpp                                        |  15 +-
 src/cmd_factory.cpp                                |   4 +-
 src/command_add_locations_to_ways.cpp              |   5 +-
 src/command_add_locations_to_ways.hpp              |   4 +-
 src/command_apply_changes.cpp                      |  51 ++-
 src/command_apply_changes.hpp                      |   4 +-
 src/command_cat.cpp                                |   5 +-
 src/command_cat.hpp                                |   4 +-
 src/command_changeset_filter.cpp                   |   4 +-
 src/command_changeset_filter.hpp                   |   4 +-
 src/command_check_refs.cpp                         |   5 +-
 src/command_check_refs.hpp                         |   4 +-
 src/command_derive_changes.cpp                     |   5 +-
 src/command_derive_changes.hpp                     |   4 +-
 src/command_diff.cpp                               |   7 +-
 src/command_diff.hpp                               |   4 +-
 src/command_extract.cpp                            | 486 ++++++++++++++++++++
 src/command_extract.hpp                            |  79 ++++
 src/command_fileinfo.cpp                           |   5 +-
 src/command_fileinfo.hpp                           |   4 +-
 src/command_getid.cpp                              |   5 +-
 src/command_getid.hpp                              |   4 +-
 src/command_help.cpp                               |   8 +-
 src/command_help.hpp                               |   4 +-
 src/command_merge.cpp                              |  96 ++--
 src/command_merge.hpp                              |   4 +-
 src/command_merge_changes.cpp                      |   4 +-
 src/command_merge_changes.hpp                      |   4 +-
 src/command_renumber.cpp                           |   4 +-
 src/command_renumber.hpp                           |   4 +-
 src/command_show.cpp                               |   4 +-
 src/command_show.hpp                               |   4 +-
 src/command_sort.cpp                               |   4 +-
 src/command_sort.hpp                               |   4 +-
 src/command_time_filter.cpp                        |   5 +-
 src/command_time_filter.hpp                        |   4 +-
 src/exception.hpp                                  |   4 +-
 src/{exception.hpp => extract/error.hpp}           |  31 +-
 src/extract/extract.cpp                            |  58 +++
 src/extract/extract.hpp                            | 101 +++++
 src/{command_help.hpp => extract/extract_bbox.cpp} |  46 +-
 src/{exception.hpp => extract/extract_bbox.hpp}    |  31 +-
 src/extract/extract_polygon.cpp                    | 161 +++++++
 src/extract/extract_polygon.hpp                    |  65 +++
 src/extract/geojson_file_parser.cpp                | 191 ++++++++
 src/extract/geojson_file_parser.hpp                |  58 +++
 src/extract/osm_file_parser.cpp                    |  85 ++++
 src/{exception.hpp => extract/osm_file_parser.hpp} |  34 +-
 src/extract/poly_file_parser.cpp                   | 133 ++++++
 src/extract/poly_file_parser.hpp                   |  78 ++++
 src/extract/strategy.hpp                           | 173 +++++++
 src/extract/strategy_complete_ways.cpp             | 176 ++++++++
 src/extract/strategy_complete_ways.hpp             |  65 +++
 .../strategy_complete_ways_with_history.cpp        | 186 ++++++++
 .../strategy_complete_ways_with_history.hpp        |  65 +++
 src/extract/strategy_simple.cpp                    | 113 +++++
 src/extract/strategy_simple.hpp                    |  60 +++
 src/extract/strategy_smart.cpp                     | 255 +++++++++++
 src/extract/strategy_smart.hpp                     |  75 ++++
 src/io.cpp                                         |  27 +-
 src/main.cpp                                       |   6 +-
 src/util.cpp                                       |  69 +++
 src/{exception.hpp => util.hpp}                    |  34 +-
 test/CMakeLists.txt                                |   9 +-
 test/extract/empty-root.geojson                    |   1 +
 test/extract/empty.geojson                         |   0
 test/extract/empty.osm.opl                         |   0
 test/extract/empty.poly                            |   0
 test/extract/invalid-root.geojson                  |   1 +
 test/extract/invalid.geojson                       |   1 +
 test/extract/missing-end-polygon.poly              |   8 +
 test/extract/missing-end-ring.poly                 |   7 +
 test/extract/multipolygon.osm.opl                  |  17 +
 test/extract/no-polygon.osm.opl                    |   5 +
 test/extract/one-line.poly                         |   1 +
 test/extract/polygon-crlf.poly                     |   9 +
 test/extract/polygon-one-outer.poly                |   9 +
 test/extract/polygon-outer-inner.poly              |  16 +
 test/extract/polygon-two-outer.poly                |  16 +
 test/extract/polygon-two-ways.osm.opl              |  10 +
 test/extract/polygon-way.osm.opl                   |   5 +
 test/extract/test_unit.cpp                         | 277 ++++++++++++
 test/extract/two-line.poly                         |   2 +
 test/extract/two-polygons-empty-line.poly          |  26 ++
 test/extract/two-polygons.poly                     |  25 ++
 test/extract/wrong-geometry-type.geojson           |   6 +
 test/include/catch.hpp                             |  14 +-
 test/util/test_unit.cpp                            |  25 ++
 zsh_completion/_osmium                             |  45 +-
 123 files changed, 4709 insertions(+), 811 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index aca591f..57498a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,14 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 ### Fixed
 
 
+## [1.5.0] - 2017-01-18
+
+### Added
+
+- New `extract` command to cut out geographical regions from an OSM file
+  using a bounding box or (multi)polygon.
+
+
 ## [1.4.1] - 2016-11-20
 
 ### Added
@@ -190,7 +198,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
 - Minor updates to documentation and build system
 
 
-[unreleased]: https://github.com/osmcode/osmium-tool/compare/v1.4.1...HEAD
+[unreleased]: https://github.com/osmcode/osmium-tool/compare/v1.5.0...HEAD
+[1.5.0]: https://github.com/osmcode/osmium-tool/compare/v1.4.1...v1.5.0
 [1.4.1]: https://github.com/osmcode/osmium-tool/compare/v1.4.0...v1.4.1
 [1.4.0]: https://github.com/osmcode/osmium-tool/compare/v1.3.1...v1.4.0
 [1.3.1]: https://github.com/osmcode/osmium-tool/compare/v1.3.0...v1.3.1
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 37da7e6..bce2dbf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,8 +25,8 @@ set(CMAKE_CONFIGURATION_TYPES "Debug;Release;RelWithDebInfo;MinSizeRel;Dev"
 project(osmium)
 
 set(OSMIUM_VERSION_MAJOR 1)
-set(OSMIUM_VERSION_MINOR 4)
-set(OSMIUM_VERSION_PATCH 1)
+set(OSMIUM_VERSION_MINOR 5)
+set(OSMIUM_VERSION_PATCH 0)
 
 set(OSMIUM_VERSION ${OSMIUM_VERSION_MAJOR}.${OSMIUM_VERSION_MINOR}.${OSMIUM_VERSION_PATCH})
 
@@ -44,7 +44,7 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 find_package(Boost 1.55.0 REQUIRED COMPONENTS program_options)
 include_directories(SYSTEM ${Boost_INCLUDE_DIRS})
 
-find_package(Osmium 2.10.2 REQUIRED COMPONENTS io)
+find_package(Osmium 2.11.0 REQUIRED COMPONENTS io)
 include_directories(SYSTEM ${OSMIUM_INCLUDE_DIRS})
 
 
@@ -76,7 +76,7 @@ endif()
 #  http://include-what-you-use.org/
 #
 #-----------------------------------------------------------------------------
-find_program(IWYU_TOOL iwyu_tool.py)
+find_program(IWYU_TOOL NAMES iwyu_tool iwyu_tool.py)
 
 if(IWYU_TOOL)
     message(STATUS "Looking for iwyu_tool.py - found")
@@ -142,6 +142,7 @@ if(PANDOC)
     add_man_page(1 osmium-check-refs)
     add_man_page(1 osmium-derive-changes)
     add_man_page(1 osmium-diff)
+    add_man_page(1 osmium-extract)
     add_man_page(1 osmium-fileinfo)
     add_man_page(1 osmium-getid)
     add_man_page(1 osmium-merge)
diff --git a/README.md b/README.md
index 564a201..0bac60b 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@
 A multipurpose command line tool for working with OpenStreetMap data based on
 the Osmium library.
 
+Official web site: http://osmcode.org/osmium-tool/
+
 [![Build Status](https://secure.travis-ci.org/osmcode/osmium-tool.svg)](http://travis-ci.org/osmcode/osmium-tool)
 [![Build Status](https://ci.appveyor.com/api/projects/status/github/osmcode/osmium-tool?svg=true)](https://ci.appveyor.com/project/Mapbox/osmium-tool)
 
@@ -14,7 +16,7 @@ later are known to work. It also works on modern Visual Studio C++ compilers.
 
 You also need the following libraries:
 
-    Libosmium (>= 2.10.2)
+    Libosmium (>= 2.11.0)
         http://osmcode.org/libosmium
         Debian/Ubuntu: libosmium2-dev
 
@@ -33,6 +35,12 @@ You also need the following libraries:
         openSUSE: utfcpp
         Fedora: utf8cpp-devel
 
+    RapidJSON (>= 1.1)
+        This is included in the osmium-tool repository, so you usually do
+        not need to install this.
+        http://rapidjson.org/
+        Debian/Ubuntu: rapidjson-dev
+
     boost-program-options (>= 1.55)
         http://www.boost.org/doc/libs/1_55_0/doc/html/program_options.html
         Debian/Ubuntu: libboost-program-options-dev
@@ -95,7 +103,8 @@ default is RelWithDebInfo.
 
 ## Documentation
 
-See the [manual](doc/manual.md).
+See the [Osmium Tool website](http://osmcode.org/osmium-tool/)
+and [Osmium Tool Manual](http://osmcode.org/osmium-tool/manual.html).
 
 There are man pages in the 'man' directory. To build them you need 'pandoc'.
 If the `pandoc` command was found during the CMake config step, the manpages
@@ -112,7 +121,7 @@ More extensive tests of the libosmium I/O system can also be run. See
 
 ## License
 
-Copyright (C) 2013-2016  Jochen Topf (jochen at topf.org)
+Copyright (C) 2013-2017  Jochen Topf (jochen at topf.org)
 
 This program is available under the GNU GENERAL PUBLIC LICENSE Version 3.
 See the file LICENSE.txt for the complete text of the license.
diff --git a/doc/manual.md b/doc/manual.md
deleted file mode 100644
index fc7b1a9..0000000
--- a/doc/manual.md
+++ /dev/null
@@ -1,495 +0,0 @@
-
-# Osmium Tool Manual
-
-## Introduction
-
-[Osmium](http://osmcode.org/osmium) is a versatile command line tool for
-working with OpenStreetMap data. It includes many useful functions for
-manipulating OSM data and often outperforms similar tools. This manual will
-introduce the tool, give a general overview and describe some use cases. For
-the nitty gritty detail consult the [manual
-pages](http://docs.osmcode.org/osmium/latest/).
-
-Osmium is Open Source and available under the GNU General Public License.
-It works and is regularly tested on Linux, Mac OSX, and Windows.
-
-Osmium is based on the C++ library [libosmium](http://osmcode.org/libosmium)
-and it gets most of its functionality from it. If the command line tool doesn't
-do something you need, have a look at the library. Maybe you can use it to
-create a program that does what you want.
-
-*Osmium is not complete. Of course most software will never be complete, but I
-mention this here, because there are some glaring gaps in Osmium's
-functionality, missing functions that would be useful for many people and that
-would fit Osmium's mission as a general OSM data manging tool. They might be
-missing because nobody thought of writing them, but more likely nobody did have
-the time yet. Osmium, like most Open Source Software, is built piece by piece
-based on what the author and others need. I some cases we haven't implemented
-some functionality, because it is available somewhere else and while it would
-be nice to have it in the same package, available alternatives make adding it a
-lower priority. In any case, [tell
-us](https://github.com/osmcode/osmium-tool/issues) if you need something.*
-
-
-## Installation
-
-### From source
-
-You can download the
-[latest release](https://github.com/osmcode/osmium-tool/releases/latest) or
-install the current master from GitHub:
-
-    git clone https://github.com/osmcode/osmium-tool
-
-Follow the build and installation instructions in the
-[README](https://github.com/osmcode/osmium-tool/blob/master/README.md).
-
-
-### Debian and Ubuntu
-
-Packages called `osmium-tool` are available for Debian and Ubuntu systems.
-(Do not install the `osmium` package, it is a very old version of the Osmium
-library.) Depending on your system, the packages might be rather old. Debian
-unstable and testing are tracking the development of Osmium closely. For
-Jessie those new packages are available in the
-[backports repository](http://backports.debian.org/Instructions/).
-
-
-### Fedora
-
-Packages for several Fedora versions
-[are available](https://admin.fedoraproject.org/pkgdb/package/rpms/osmium-tool/).
-
-
-## The Osmium command
-
-The starting point for everything is the `osmium` command. All the
-functionality of Osmium is available via this one command and its
-"subcommands", similar to how the `git` command works for instance. Just
-calling
-
-    osmium
-
-will give you a list of subcommands. Use
-
-    osmium COMMAND -h
-
-to get a short description about a subcommand's options. Use
-
-    osmium help COMMAND
-
-to display the manual page explaining all the details. If Osmium is installed
-correctly on your system, you can also get those manpages using the usual `man`
-command:
-
-    man osmium
-    man osmium-fileinfo
-
-etc.
-
-The manpages are also available [on the web](http://docs.osmcode.org/osmium/latest/).
-
-
-## Showing OSM files
-
-Before you can do anything with an OSM file you'll often want to know what's in
-that file. To find out run
-
-    osmium fileinfo OSMFILE
-
-Say you downloaded an extract of the tiny country of
-[Liechtenstein](https://www.openstreetmap.org/relation/1155955)
-(it is always good to start your experiments with a small extract)
-from [Geofabrik](http://download.geofabrik.de/)
-and want to know what's in it:
-
-    osmium fileinfo liechtenstein-latest.osm.pbf
-
-This will show you something like the following:
-
-    File:
-      Name: liechtenstein-latest.osm.pbf
-      Format: PBF
-      Compression: none
-      Size: 1532846
-    Header:
-      Bounding boxes:
-        (9.47108,47.0477,9.63622,47.2713)
-      With history: no
-      Options:
-        generator=Osmium (http://wiki.openstreetmap.org/wiki/Osmium)
-        osmosis_replication_timestamp=2016-04-12T19:14:02Z
-        pbf_dense_nodes=true
-        timestamp=2016-04-12T19:14:02Z
-
-The `File` section contains the information gleaned from the file system,
-the `Header` section contains, as the name implies, the information from the
-file header. To get some more detail use the `-e` or `--extended` flag:
-
-    osmium fileinfo -e liechtenstein-latest.osm.pbf
-
-In this case the whole file will be read, which will take more time. But it
-gives you a lot more information:
-
-    File:
-      Name: liechtenstein-latest.osm.pbf
-      Format: PBF
-      Compression: none
-      Size: 1532846
-    Header:
-      Bounding boxes:
-        (9.47108,47.0477,9.63622,47.2713)
-      With history: no
-      Options:
-        generator=Osmium (http://wiki.openstreetmap.org/wiki/Osmium)
-        osmosis_replication_timestamp=2016-04-12T19:14:02Z
-        pbf_dense_nodes=true
-        timestamp=2016-04-12T19:14:02Z
-    Data:
-      Bounding box: (9.39778,46.9688,9.67146,47.5258)
-      Timestamps:
-        First: 2007-06-19T06:25:12Z
-        Last: 2016-04-11T07:49:32Z
-      Objects ordered (by type and id): yes
-      Multiple versions of same object: no
-      CRC32: 727f1abf
-      Number of changesets: 0
-      Number of nodes: 168683
-      Number of ways: 18784
-      Number of relations: 374
-      Largest changeset ID: 0
-      Largest node ID: 4112691184
-      Largest way ID: 409348773
-      Largest relation ID: 6122095
-
-Sometimes your are only interested in a specific piece of information from this
-list. Use the `-g`, `--get` option to ask for it. For instance to get the last
-timestamp used:
-
-    osmium fileinfo -e -g data.timestamp.last liechtenstein-latest.osm.pbf
-
-This will result in just the timestamp:
-
-    2016-04-11T07:49:32Z
-
-This format makes it easy to use the output in other shell commands. To put the
-CRC32 checkum into the `checksum` shell variable you can use the following
-command, for instance:
-
-    checksum=$(osmium fileinfo -e -g data.crc32 $filename)
-
-Of course you might also want to look at the contents of the file. For a quick
-look, use the `show` command:
-
-    osmium show liechtenstein-latest.osm.pbf
-
-This will show the contents of the file in the *debug* format (more on the
-different formats below) using your favourite pager (`less` by default):
-
-![osmium show](osmium-show.png)
-
-This *debug* format is intended to be easy to read, on terminals that support
-it, colors make it even easier. But you can change the output format to any
-format supported by Osmium.
-
-
-## OSM file formats and converting between them
-
-Osmium supports all popular OSM file formats (and some more): XML, PBF,
-O5M/O5C, OPL, and the already mentioned *debug* format. Some formats can
-only be read or only be written. See the
-[osmium-file-formats](http://docs.osmcode.org/osmium/latest/osmium-file-formats.html)
-man page for details. All the text-based formats can be automatically
-(de)compressed if they use the `gzip` or `bzip2` compression.
-
-Often you need to convert OSM data files from one format to another. This
-is easily done with the `osmium cat` command. To convert the Liechtenstein
-PBF file into XML format compressed with `bzip2`, use this command:
-
-    osmium cat liechtenstein-latest.osm.pbf -o liechtenstein.osm.bz2
-
-Osmium will automatically detect the file format based on the file name.
-Usually it will do what you want, but you can force the file format with
-the command line options `-f` (for the output file) and `-F` for the input
-file. This is useful when reading from STDIN or writing to STDOUT:
-
-    osmium cat input.osm.pbf -f osm | some_program_reading_osm_xml
-
-The format for the `-f` and `-F` options is simply the suffix you would usually
-use for an OSM file (here `osm`). It also works with `pbf` (or `osm.pbf`),
-`osm.gz` etc.
-
-Most file formats have special options to change the details of the file
-format. You can set those special options with the same command line options.
-For instance, if you want a bz2ip-compressed XML file without metadata use
-this:
-
-    osmium cat input.osm.pbf -o output.osm.bz2 -f osm.bz2,add_metadata=false
-
-The output file will not have the `version`, `timestamp`, etc. fields and so
-it is much more compact than the usual XML format.
-
-As you can see you can add the options to the format separated by commas,
-multiple options are possible. Some options are available for several or all
-formats (`add_metadata` for instance), others are specific to one of the
-formats, `pbf_dense_nodes` for instance only works on the PBF format. Note
-that unknown options are silently ignored, so if you mistype an option, you
-will not get an error message.
-
-See the
-[osmium-file-formats](http://docs.osmcode.org/osmium/latest/osmium-file-formats.html)
-man page for all the details. Btw: all these file formats and options are
-implemented in the [libosmium library](http://osmcode.org/libosmium/), so most
-libosmium-based programs will understand them in the same fashion.
-
-While playing around with the command you might have noticed an error
-message like `Open failed for 'output.osm.bz2': File exists`. By default
-Osmium will not overwrite an existing file. This is a safety measure to
-keep you from accidentally deleting that 30GB planet file, that took you
-all day to download. With the `-O` or `--overwrite` option you can disable
-this check.
-
-The `osmium cat` command can do more: Just like the shell `cat` command, it
-can take several input files and it will concatenate them to generate the
-output. In fact that's where the name (con"cat"enate) comes from. Its ability
-to convert from one file format to another is only a side-effect really, most
-Osmium subcommands can read and write OSM files in any format and support the
-same `-f` and `-F` options. Note that this command will really just
-concatenate its inputs and not sort them in any way. This might or might not
-be what you want.
-
-There is one additional option of the `osmium cat` command that often comes
-handy. With `-t TYPE` it can be instructed to only copy objects of the given
-type. So
-
-    osmium input.osm.pbf -t node -o output.osm.pbf
-
-will only copy the nodes, not the ways, or relations.
-
-
-## Verbose mode
-
-Most Osmium commands support the `-v` or `--verbose` command line option to
-enable *verbose* mode. Osmium will then display information about its command
-line parameters and about what it is doing to STDERR.
-
-Each line of the output is started with the elapsed time in minutes and
-seconds. This is especially useful when Osmium is run in scripts and the
-output is logged, you can immediately see where the time went.
-
-In verbose mode, most Osmium commands will also display the memory used. This
-is handy because Osmium command often need a lot of memory to efficiently do
-their job. There is also a `MEMORY` section in each of the man pages that
-tells you about memory use of this particular command.
-
-
-## Getting OSM objects by ID
-
-Sometimes you have the ID of a node, way or relation (or a bunch of IDs) and
-want to get the objects with those IDs from a file. That's what the `getid`
-command is for. The following command will get the nodes 17 and 18, the way
-42, and the relation 3 out of the file:
-
-    osmium getid input.osm.pbf n17 n18 w42 r3 -o output.osm.pbf
-
-As you can see the IDs are written with a one-letter prefix denoting the type.
-IDs without this prefix are understood to be node IDs (but this can be changed
-with the `--default-type` option).
-
-If you have more than a few IDs you can put them into a file, one ID per line
-and use the `-i` option to read them from that file:
-
-    osmium getid input.osm.pbf -i ids -o output.osm.pbf
-
-Empty lines and everything after a space or hash (#) sign is ignored. And you
-can read from STDIN. So this command
-
-    echo "r123 foo bar" | osmium getid input.osm.pbf -i - -f debug
-
-will find the relation with the ID 123 in `input.osm.pbf` and output it to
-STDOUT in debug format.
-
-Often you need not only the OSM objects with the given IDs but also all
-objects referenced by those objects. So for ways you need the nodes in it
-and for relations the members. Use the `-r` or `--add-referenced` option to
-add them to the output:
-
-    osmium getid -r input.osm.pbf w222 -o output.osm.pbf
-
-The output will contain first all nodes references by way 222 and then the
-way. The `getid` command will read the input file up to three times to
-follow all the references. Output will be sorted in the usual fashion: first
-nodes ordered by ID, then ways, then relations.
-
-
-## Checking references
-
-A full planet dump is referentially complete. All objects referenced by other
-objects are in the file. So if a relation has a member way 123, then this way
-will also be in the file. This is not always true for geographical extracts or
-other partial OSM files.
-
-This often leads to confusion. Many programs that use OSM data will fail
-in unexpected ways when something is missing in its input data. If you have
-a situation like this you can use the `check-refs` command to find out whether
-your OSM file is referentially complete:
-
-    osmium check-refs input.osm.pbf
-
-This command will check that all nodes referenced in the ways are in the input
-file. If you want to check relation members, too, use the `-r` or
-`--check-relations` option.
-
-
-## Working with History Files
-
-Osmium can not only work with normal OSM data files, but also with history
-files. History files look more or less like normal OSM data files, but they
-can contain several versions of the same object. The [full history planet
-dump](http://planet.osm.org/planet/full-history/) contains all versions of all
-objects that ever existed in the OSM data.
-
-Most programs using OSM data expect object IDs to be unique, so they can not
-work with history data. The same is true for some Osmium subcommands. But
-wherever it is possible and makes sense, Osmium also supports history files.
-Often they will just work, sometimes you need special command line options.
-
-Commands that "just work" are `cat`, `fileinfo`, `show`, `sort`. The will do
-what you expect.
-
-One command is only useful for history files: `time-filter`. It is used to
-filter objects from a history file based on time. To get all objects visible
-at a point in time use a command line like this:
-
-    osmium time-filter history.osm.pbf 2015-01-01T00:00:00Z -o 2015.osm.pbf
-
-The output is a normal OSM data file containing the data as it would have
-looked at the start of the year 2015 (UTC time as always with OSM).
-
-
-## Working with change files
-
-While OSM data files contain the OSM data of a specific point in time and
-OSM history files contain all the data that is now and ever was in the data,
-an OSM change file contains the data that changed between two points in
-time. Those change files (usually with the suffix `.osc` or `.osc.gz` or
-`.osc.bz2`) can be used to update an OSM file or OSM database from an earlier
-state to a later state.
-
-Osmium has commands to deal with those change files. Most often you will use
-the `apply-changes` command which applies one or more change files to a data
-file:
-
-    osmium apply-changes planet.osm.pbf change-123.osc.gz change-124.osc.gz \
-        -o planet-new.osm.pbf
-
-You can give as many change files as you want on the command line and in any
-order. All changes will be read into memory and sorted before they are applied
-to the data file. If you a lot of change files, it might be better to apply
-them one bunch at a time so this will not use too much memory.
-
-The same command also works with history files:
-
-    osmium apply-changes australia.osh.pbf australia-changes.osc.bz2 \
-        -o australia-new.osh.pbf
-
-Note the `.osh` file suffix which tells Osmium that you want your history file
-updated. Sometimes history files are named `.osm`. In this case you can force
-history mode with a command line option:
-
-    osmium apply-changes australia.osh.pbf australia-changes.osc.bz2 \
-        --with-history -o australia-new.osh.pbf
-
-Sometimes you don't want to apply changes to an OSM file, but to a database
-or so. For instance when you are upgrading a `osm2pgsql` rendering database.
-If you have a lot of change files, it is often better to merge them into one
-larger change file and then apply that one using `osm2pgsql`. Merging is done
-with
-
-    osmium merge-changes --simplify ch456.osc.gz ch457.osc.gz \
-        -o merged-changes.osc.gz
-
-The `--simplify` option usually makes sense: If an object changed multiple
-times, all intermediate versions will be removed and only the last version
-remains.
-
-Osmium can also do the reverse: Create a change file from two OSM data files.
-This is done with `derive-changes`:
-
-    osmium derive-changes yesterday.osm.pbf today.osm.pbf \
-        -o changes-since-yesterday.osc
-
-
-## Working with Changesets
-
-Osmium can not only handle OSM objects (nodes, ways, and relations), but also
-OSM changesets. Those changesets are also stored in files with the suffix
-`.osm`, which can make things somewhat confusing. Some Osmium commands will
-just work on changeset files: `osmium fileinfo`, `osmium show`, and
-`osmium cat`. Others don't make any sense with changeset files, like
-`apply-changes`.
-
-Unlike OSM data files for which several formats are available (XML, PBF,
-O5M, ...) changesets always come in XML format which is much slower to work
-with than the binary formats.
-
-There is one command specifically for changeset files: `changeset-filter`.
-You can use it to select changesets from a file. If you want to get all
-changesets by user "Einstein", for instance, you'd use this command:
-
-    osmium changeset-filter -u Einstein changesets.osm.bz2 -o einstein.osm.bz2
-
-Have a look in the
-[man page](http://docs.osmcode.org/osmium/latest/osmium-changeset-filter.html)
-to see the other options available. You can also combine several of them to
-only get those changesets matching all criteria.
-
-Changesets can also be written out into the OPL format which can be processed
-easily with standard unix command line tools. Say you have a list of user names
-and want to find all changesets in January 2015 by any of those users. You can
-first use `changeset-filter` to filter out the time window and then use `grep`
-to check for those users:
-
-    osmium changeset-filter -a 2015-01-01T00:00:00Z -b 2015-01-31T23:59:59Z \
-        -f opl | grep ' u\(foo\|bar\|baz\) '
-
-This way you can use the fast, but not very flexible filtering options of the
-`changeset-filter` command together with slower, but more flexible filtering
-using unix command line tools or scripting languages.
-
-
-## Sorting OSM files
-
-OSM files are usually sorted in a specific way: First the nodes ordered by
-ID, then ways ordered by ID, then relations ordered by ID. But this is not
-necessarily so. `osmium fileinfo` will tell you if a file is sorted or not:
-
-    > osmium fileinfo -e input.osm.pbf
-    ...
-    Objects ordered (by type and id): yes
-    ...
-
-
-Many commands (Osmium or otherwise) only work correctly if the file is sorted.
-Sorting a file is easy:
-
-    osmium sort input.osm.pbf -o output.osm.pbf
-
-Note that `osmium sort` reads the contents of the input file into main memory.
-This will take roughly 10 times as much memory as the files take on disk in
-.osm.bz2 or osm.pbf format. So this command is only useful for smaller OSM
-files.
-
-Osmium sort will work correctly on history and change files.
-
-
-### Command Line Completion
-
-There is a command line completion configuration available for Zsh users. This
-allows comfortable command line completion of all Osmium commands including
-command line options. If you have installed Osmium as a Debian package, it
-will be installed for you. If you built and installed from sources, you need
-to install the file `zsh_completion/_osmium` yourself, for instance in
-`/usr/local/share/zsh/site-functions/`.
-
-
diff --git a/doc/osmium-show.png b/doc/osmium-show.png
deleted file mode 100644
index eee9697..0000000
Binary files a/doc/osmium-show.png and /dev/null differ
diff --git a/extract-example-config/README.md b/extract-example-config/README.md
new file mode 100644
index 0000000..f388389
--- /dev/null
+++ b/extract-example-config/README.md
@@ -0,0 +1,17 @@
+
+# Example config for creating extracts
+
+These example files show the different ways the extent of extracts to be
+created with the `osmium extract` command can be specified.
+
+The config file `extracts.json` contains a list of all extracts that should be
+created. In this case several different parts of Germany. You can [download a
+Germany extract](http://download.geofabrik.de/europe/germany.html) and then try
+this out:
+
+    osmium extract -v -c examples-extract.json germany-latest.osm.pbf
+
+All resulting files will be written to the `/tmp/` directory because of the
+`directory` setting in the config file. You can override this on the command
+line with the `-d DIR` or `--directory=DIR` option.
+
diff --git a/extract-example-config/berlin.geojson b/extract-example-config/berlin.geojson
new file mode 100644
index 0000000..7c69013
--- /dev/null
+++ b/extract-example-config/berlin.geojson
@@ -0,0 +1,122 @@
+{
+    "type": "Feature",
+    "properties": {
+        "name": "Berlin"
+    },
+    "geometry": {
+        "type": "Polygon",
+        "coordinates": [[
+            [1.373096E+01, 5.239455E+01],
+            [1.371644E+01, 5.239650E+01],
+            [1.369940E+01, 5.238719E+01],
+            [1.370683E+01, 5.237793E+01],
+            [1.369538E+01, 5.236414E+01],
+            [1.367929E+01, 5.236490E+01],
+            [1.365020E+01, 5.233446E+01],
+            [1.363535E+01, 5.233825E+01],
+            [1.362978E+01, 5.234713E+01],
+            [1.363782E+01, 5.236773E+01],
+            [1.362606E+01, 5.237642E+01],
+            [1.360378E+01, 5.236905E+01],
+            [1.358738E+01, 5.238813E+01],
+            [1.356448E+01, 5.238530E+01],
+            [1.353044E+01, 5.238605E+01],
+            [1.352982E+01, 5.239436E+01],
+            [1.351776E+01, 5.239852E+01],
+            [1.347892E+01, 5.239210E+01],
+            [1.346283E+01, 5.241758E+01],
+            [1.342229E+01, 5.240796E+01],
+            [1.343343E+01, 5.238530E+01],
+            [1.342198E+01, 5.237340E+01],
+            [1.338392E+01, 5.237548E+01],
+            [1.338268E+01, 5.238530E+01],
+            [1.336721E+01, 5.238605E+01],
+            [1.336566E+01, 5.239266E+01],
+            [1.333750E+01, 5.240560E+01],
+            [1.331089E+01, 5.239502E+01],
+            [1.329325E+01, 5.240956E+01],
+            [1.327066E+01, 5.240069E+01],
+            [1.324560E+01, 5.240239E+01],
+            [1.323941E+01, 5.241749E+01],
+            [1.317412E+01, 5.240465E+01],
+            [1.317350E+01, 5.239125E+01],
+            [1.315833E+01, 5.238728E+01],
+            [1.312832E+01, 5.238407E+01],
+            [1.308469E+01, 5.240824E+01],
+            [1.308283E+01, 5.242202E+01],
+            [1.311284E+01, 5.244032E+01],
+            [1.309861E+01, 5.245126E+01],
+            [1.311037E+01, 5.248048E+01],
+            [1.315493E+01, 5.250799E+01],
+            [1.313977E+01, 5.251514E+01],
+            [1.311161E+01, 5.251213E+01],
+            [1.311192E+01, 5.253689E+01],
+            [1.312553E+01, 5.255872E+01],
+            [1.313915E+01, 5.255778E+01],
+            [1.314627E+01, 5.257771E+01],
+            [1.312986E+01, 5.257527E+01],
+            [1.312027E+01, 5.258204E+01],
+            [1.312213E+01, 5.259031E+01],
+            [1.314379E+01, 5.259520E+01],
+            [1.315098E+01, 5.260013E+01],
+            [1.316667E+01, 5.260209E+01],
+            [1.320798E+01, 5.259137E+01],
+            [1.319296E+01, 5.260658E+01],
+            [1.321549E+01, 5.263134E+01],
+            [1.325823E+01, 5.263177E+01],
+            [1.325609E+01, 5.264306E+01],
+            [1.327826E+01, 5.264458E+01],
+            [1.327683E+01, 5.266345E+01],
+            [1.330938E+01, 5.266237E+01],
+            [1.331511E+01, 5.265694E+01],
+            [1.330688E+01, 5.265261E+01],
+            [1.331439E+01, 5.264306E+01],
+            [1.331403E+01, 5.263090E+01],
+            [1.333871E+01, 5.262591E+01],
+            [1.336840E+01, 5.262960E+01],
+            [1.338217E+01, 5.264002E+01],
+            [1.339219E+01, 5.264978E+01],
+            [1.339970E+01, 5.264978E+01],
+            [1.341570E+01, 5.264475E+01],
+            [1.342690E+01, 5.263962E+01],
+            [1.343611E+01, 5.265155E+01],
+            [1.346162E+01, 5.265223E+01],
+            [1.346262E+01, 5.265491E+01],
+            [1.344520E+01, 5.266200E+01],
+            [1.345739E+01, 5.267211E+01],
+            [1.346461E+01, 5.267083E+01],
+            [1.347319E+01, 5.267830E+01],
+            [1.349261E+01, 5.267166E+01],
+            [1.348999E+01, 5.265921E+01],
+            [1.352813E+01, 5.264623E+01],
+            [1.352223E+01, 5.262719E+01],
+            [1.351005E+01, 5.262382E+01],
+            [1.350216E+01, 5.260630E+01],
+            [1.351041E+01, 5.259455E+01],
+            [1.352850E+01, 5.259542E+01],
+            [1.355305E+01, 5.259025E+01],
+            [1.358531E+01, 5.257272E+01],
+            [1.359176E+01, 5.255148E+01],
+            [1.364139E+01, 5.254418E+01],
+            [1.363888E+01, 5.253306E+01],
+            [1.366003E+01, 5.253284E+01],
+            [1.366343E+01, 5.252499E+01],
+            [1.364256E+01, 5.251431E+01],
+            [1.363378E+01, 5.249315E+01],
+            [1.361711E+01, 5.247853E+01],
+            [1.362715E+01, 5.247591E+01],
+            [1.364399E+01, 5.248202E+01],
+            [1.366818E+01, 5.247634E+01],
+            [1.369273E+01, 5.246728E+01],
+            [1.370007E+01, 5.247274E+01],
+            [1.372014E+01, 5.246499E+01],
+            [1.373179E+01, 5.245440E+01],
+            [1.376136E+01, 5.244883E+01],
+            [1.376100E+01, 5.243868E+01],
+            [1.375096E+01, 5.243835E+01],
+            [1.373699E+01, 5.241464E+01],
+            [1.374415E+01, 5.240912E+01],
+            [1.373096E+01, 5.239455E+01]
+        ]]
+    }
+}
diff --git a/extract-example-config/berlin.poly b/extract-example-config/berlin.poly
new file mode 100644
index 0000000..b957559
--- /dev/null
+++ b/extract-example-config/berlin.poly
@@ -0,0 +1,115 @@
+berlin
+1
+   1.373096E+01   5.239455E+01
+   1.371644E+01   5.239650E+01
+   1.369940E+01   5.238719E+01
+   1.370683E+01   5.237793E+01
+   1.369538E+01   5.236414E+01
+   1.367929E+01   5.236490E+01
+   1.365020E+01   5.233446E+01
+   1.363535E+01   5.233825E+01
+   1.362978E+01   5.234713E+01
+   1.363782E+01   5.236773E+01
+   1.362606E+01   5.237642E+01
+   1.360378E+01   5.236905E+01
+   1.358738E+01   5.238813E+01
+   1.356448E+01   5.238530E+01
+   1.353044E+01   5.238605E+01
+   1.352982E+01   5.239436E+01
+   1.351776E+01   5.239852E+01
+   1.347892E+01   5.239210E+01
+   1.346283E+01   5.241758E+01
+   1.342229E+01   5.240796E+01
+   1.343343E+01   5.238530E+01
+   1.342198E+01   5.237340E+01
+   1.338392E+01   5.237548E+01
+   1.338268E+01   5.238530E+01
+   1.336721E+01   5.238605E+01
+   1.336566E+01   5.239266E+01
+   1.333750E+01   5.240560E+01
+   1.331089E+01   5.239502E+01
+   1.329325E+01   5.240956E+01
+   1.327066E+01   5.240069E+01
+   1.324560E+01   5.240239E+01
+   1.323941E+01   5.241749E+01
+   1.317412E+01   5.240465E+01
+   1.317350E+01   5.239125E+01
+   1.315833E+01   5.238728E+01
+   1.312832E+01   5.238407E+01
+   1.308469E+01   5.240824E+01
+   1.308283E+01   5.242202E+01
+   1.311284E+01   5.244032E+01
+   1.309861E+01   5.245126E+01
+   1.311037E+01   5.248048E+01
+   1.315493E+01   5.250799E+01
+   1.313977E+01   5.251514E+01
+   1.311161E+01   5.251213E+01
+   1.311192E+01   5.253689E+01
+   1.312553E+01   5.255872E+01
+   1.313915E+01   5.255778E+01
+   1.314627E+01   5.257771E+01
+   1.312986E+01   5.257527E+01
+   1.312027E+01   5.258204E+01
+   1.312213E+01   5.259031E+01
+   1.314379E+01   5.259520E+01
+   1.315098E+01   5.260013E+01
+   1.316667E+01   5.260209E+01
+   1.320798E+01   5.259137E+01
+   1.319296E+01   5.260658E+01
+   1.321549E+01   5.263134E+01
+   1.325823E+01   5.263177E+01
+   1.325609E+01   5.264306E+01
+   1.327826E+01   5.264458E+01
+   1.327683E+01   5.266345E+01
+   1.330938E+01   5.266237E+01
+   1.331511E+01   5.265694E+01
+   1.330688E+01   5.265261E+01
+   1.331439E+01   5.264306E+01
+   1.331403E+01   5.263090E+01
+   1.333871E+01   5.262591E+01
+   1.336840E+01   5.262960E+01
+   1.338217E+01   5.264002E+01
+   1.339219E+01   5.264978E+01
+   1.339970E+01   5.264978E+01
+   1.341570E+01   5.264475E+01
+   1.342690E+01   5.263962E+01
+   1.343611E+01   5.265155E+01
+   1.346162E+01   5.265223E+01
+   1.346262E+01   5.265491E+01
+   1.344520E+01   5.266200E+01
+   1.345739E+01   5.267211E+01
+   1.346461E+01   5.267083E+01
+   1.347319E+01   5.267830E+01
+   1.349261E+01   5.267166E+01
+   1.348999E+01   5.265921E+01
+   1.352813E+01   5.264623E+01
+   1.352223E+01   5.262719E+01
+   1.351005E+01   5.262382E+01
+   1.350216E+01   5.260630E+01
+   1.351041E+01   5.259455E+01
+   1.352850E+01   5.259542E+01
+   1.355305E+01   5.259025E+01
+   1.358531E+01   5.257272E+01
+   1.359176E+01   5.255148E+01
+   1.364139E+01   5.254418E+01
+   1.363888E+01   5.253306E+01
+   1.366003E+01   5.253284E+01
+   1.366343E+01   5.252499E+01
+   1.364256E+01   5.251431E+01
+   1.363378E+01   5.249315E+01
+   1.361711E+01   5.247853E+01
+   1.362715E+01   5.247591E+01
+   1.364399E+01   5.248202E+01
+   1.366818E+01   5.247634E+01
+   1.369273E+01   5.246728E+01
+   1.370007E+01   5.247274E+01
+   1.372014E+01   5.246499E+01
+   1.373179E+01   5.245440E+01
+   1.376136E+01   5.244883E+01
+   1.376100E+01   5.243868E+01
+   1.375096E+01   5.243835E+01
+   1.373699E+01   5.241464E+01
+   1.374415E+01   5.240912E+01
+   1.373096E+01   5.239455E+01
+END
+END
diff --git a/extract-example-config/brandenburg.poly b/extract-example-config/brandenburg.poly
new file mode 100644
index 0000000..9d20eeb
--- /dev/null
+++ b/extract-example-config/brandenburg.poly
@@ -0,0 +1,275 @@
+brandenburg
+1
+   1.441545E+01   5.335626E+01
+   1.442177E+01   5.334259E+01
+   1.442438E+01   5.330853E+01
+   1.443556E+01   5.328224E+01
+   1.445028E+01   5.327896E+01
+   1.445852E+01   5.326018E+01
+   1.445867E+01   5.325950E+01
+   1.441916E+01   5.321890E+01
+   1.441332E+01   5.320919E+01
+   1.440992E+01   5.320568E+01
+   1.438822E+01   5.319557E+01
+   1.438151E+01   5.316430E+01
+   1.439742E+01   5.314588E+01
+   1.439749E+01   5.314562E+01
+   1.435989E+01   5.304827E+01
+   1.432616E+01   5.303146E+01
+   1.422813E+01   5.298549E+01
+   1.417930E+01   5.297135E+01
+   1.415209E+01   5.295518E+01
+   1.415917E+01   5.294373E+01
+   1.415730E+01   5.293272E+01
+   1.417021E+01   5.290097E+01
+   1.417420E+01   5.288214E+01
+   1.416930E+01   5.286883E+01
+   1.414617E+01   5.285209E+01
+   1.413270E+01   5.284332E+01
+   1.414575E+01   5.283296E+01
+   1.416212E+01   5.283086E+01
+   1.416317E+01   5.283033E+01
+   1.419051E+01   5.282722E+01
+   1.421769E+01   5.282373E+01
+   1.431225E+01   5.276656E+01
+   1.436309E+01   5.275281E+01
+   1.444839E+01   5.268275E+01
+   1.446853E+01   5.267235E+01
+   1.447787E+01   5.265942E+01
+   1.454210E+01   5.263904E+01
+   1.461005E+01   5.260747E+01
+   1.464806E+01   5.257506E+01
+   1.465434E+01   5.256837E+01
+   1.462299E+01   5.252690E+01
+   1.461805E+01   5.252054E+01
+   1.461815E+01   5.252049E+01
+   1.461727E+01   5.251933E+01
+   1.465200E+01   5.249693E+01
+   1.458605E+01   5.243059E+01
+   1.455463E+01   5.242273E+01
+   1.455299E+01   5.241025E+01
+   1.455144E+01   5.240951E+01
+   1.455257E+01   5.240699E+01
+   1.455099E+01   5.239495E+01
+   1.457981E+01   5.234623E+01
+   1.459682E+01   5.230829E+01
+   1.460039E+01   5.228483E+01
+   1.464619E+01   5.227555E+01
+   1.470427E+01   5.226275E+01
+   1.472857E+01   5.222906E+01
+   1.470984E+01   5.210710E+01
+   1.474099E+01   5.209762E+01
+   1.477307E+01   5.206171E+01
+   1.477481E+01   5.205944E+01
+   1.472869E+01   5.200424E+01
+   1.472844E+01   5.199795E+01
+   1.472809E+01   5.199750E+01
+   1.472681E+01   5.195713E+01
+   1.472507E+01   5.191376E+01
+   1.460713E+01   5.183235E+01
+   1.461182E+01   5.183035E+01
+   1.460812E+01   5.182766E+01
+   1.465952E+01   5.180085E+01
+   1.467236E+01   5.174483E+01
+   1.468006E+01   5.173953E+01
+   1.468176E+01   5.173411E+01
+   1.474087E+01   5.169769E+01
+   1.475505E+01   5.168793E+01
+   1.477871E+01   5.161530E+01
+   1.477770E+01   5.161427E+01
+   1.474431E+01   5.158065E+01
+   1.470138E+01   5.157814E+01
+   1.467680E+01   5.154666E+01
+   1.460047E+01   5.153708E+01
+   1.457992E+01   5.155990E+01
+   1.452708E+01   5.154210E+01
+   1.445223E+01   5.153069E+01
+   1.436783E+01   5.151288E+01
+   1.436013E+01   5.149552E+01
+   1.432343E+01   5.149552E+01
+   1.432196E+01   5.151060E+01
+   1.417189E+01   5.153297E+01
+   1.416491E+01   5.151311E+01
+   1.413446E+01   5.151356E+01
+   1.411960E+01   5.147313E+01
+   1.402236E+01   5.136810E+01
+   1.397466E+01   5.137199E+01
+   1.397392E+01   5.138940E+01
+   1.392108E+01   5.137153E+01
+   1.377577E+01   5.135252E+01
+   1.354919E+01   5.136237E+01
+   1.342957E+01   5.142808E+01
+   1.328609E+01   5.137520E+01
+   1.322353E+01   5.138768E+01
+   1.317436E+01   5.142773E+01
+   1.317820E+01   5.154040E+01
+   1.307596E+01   5.159534E+01
+   1.302394E+01   5.165302E+01
+   1.314162E+01   5.171829E+01
+   1.310569E+01   5.185316E+01
+   1.302518E+01   5.186272E+01
+   1.266842E+01   5.198572E+01
+   1.262506E+01   5.196741E+01
+   1.250800E+01   5.197198E+01
+   1.233086E+01   5.202918E+01
+   1.216859E+01   5.216730E+01
+   1.225778E+01   5.234398E+01
+   1.225034E+01   5.242790E+01
+   1.228131E+01   5.246263E+01
+   1.210789E+01   5.250488E+01
+   1.214133E+01   5.264039E+01
+   1.219088E+01   5.265354E+01
+   1.215124E+01   5.272562E+01
+   1.219584E+01   5.281032E+01
+   1.217850E+01   5.284325E+01
+   1.209426E+01   5.282753E+01
+   1.206453E+01   5.285971E+01
+   1.196667E+01   5.285522E+01
+   1.177590E+01   5.290008E+01
+   1.177900E+01   5.293145E+01
+   1.143711E+01   5.304107E+01
+   1.133677E+01   5.301350E+01
+   1.122404E+01   5.309466E+01
+   1.125006E+01   5.315041E+01
+   1.150772E+01   5.316304E+01
+   1.150524E+01   5.322909E+01
+   1.170096E+01   5.327282E+01
+   1.175547E+01   5.325503E+01
+   1.197348E+01   5.332612E+01
+   1.201065E+01   5.338859E+01
+   1.224972E+01   5.337972E+01
+   1.251110E+01   5.328578E+01
+   1.269939E+01   5.327319E+01
+   1.280220E+01   5.323205E+01
+   1.301279E+01   5.321500E+01
+   1.308835E+01   5.326170E+01
+   1.344759E+01   5.332168E+01
+   1.350952E+01   5.341850E+01
+   1.372259E+01   5.352541E+01
+   1.375851E+01   5.357840E+01
+   1.382788E+01   5.357546E+01
+   1.390964E+01   5.351731E+01
+   1.394680E+01   5.346056E+01
+   1.407191E+01   5.344876E+01
+   1.411899E+01   5.346498E+01
+   1.424782E+01   5.345392E+01
+   1.426144E+01   5.337640E+01
+   1.414624E+01   5.329059E+01
+   1.423791E+01   5.328689E+01
+   1.429860E+01   5.333056E+01
+   1.441545E+01   5.335626E+01
+END
+!berlin
+   1.373096E+01   5.239455E+01
+   1.371644E+01   5.239650E+01
+   1.369940E+01   5.238719E+01
+   1.370683E+01   5.237793E+01
+   1.369538E+01   5.236414E+01
+   1.367929E+01   5.236490E+01
+   1.365020E+01   5.233446E+01
+   1.363535E+01   5.233825E+01
+   1.362978E+01   5.234713E+01
+   1.363782E+01   5.236773E+01
+   1.362606E+01   5.237642E+01
+   1.360378E+01   5.236905E+01
+   1.358738E+01   5.238813E+01
+   1.356448E+01   5.238530E+01
+   1.353044E+01   5.238605E+01
+   1.352982E+01   5.239436E+01
+   1.351776E+01   5.239852E+01
+   1.347892E+01   5.239210E+01
+   1.346283E+01   5.241758E+01
+   1.342229E+01   5.240796E+01
+   1.343343E+01   5.238530E+01
+   1.342198E+01   5.237340E+01
+   1.338392E+01   5.237548E+01
+   1.338268E+01   5.238530E+01
+   1.336721E+01   5.238605E+01
+   1.336566E+01   5.239266E+01
+   1.333750E+01   5.240560E+01
+   1.331089E+01   5.239502E+01
+   1.329325E+01   5.240956E+01
+   1.327066E+01   5.240069E+01
+   1.324560E+01   5.240239E+01
+   1.323941E+01   5.241749E+01
+   1.317412E+01   5.240465E+01
+   1.317350E+01   5.239125E+01
+   1.315833E+01   5.238728E+01
+   1.312832E+01   5.238407E+01
+   1.308469E+01   5.240824E+01
+   1.308283E+01   5.242202E+01
+   1.311284E+01   5.244032E+01
+   1.309861E+01   5.245126E+01
+   1.311037E+01   5.248048E+01
+   1.315493E+01   5.250799E+01
+   1.313977E+01   5.251514E+01
+   1.311161E+01   5.251213E+01
+   1.311192E+01   5.253689E+01
+   1.312553E+01   5.255872E+01
+   1.313915E+01   5.255778E+01
+   1.314627E+01   5.257771E+01
+   1.312986E+01   5.257527E+01
+   1.312027E+01   5.258204E+01
+   1.312213E+01   5.259031E+01
+   1.314379E+01   5.259520E+01
+   1.315098E+01   5.260013E+01
+   1.316667E+01   5.260209E+01
+   1.320798E+01   5.259137E+01
+   1.319296E+01   5.260658E+01
+   1.321549E+01   5.263134E+01
+   1.325823E+01   5.263177E+01
+   1.325609E+01   5.264306E+01
+   1.327826E+01   5.264458E+01
+   1.327683E+01   5.266345E+01
+   1.330938E+01   5.266237E+01
+   1.331511E+01   5.265694E+01
+   1.330688E+01   5.265261E+01
+   1.331439E+01   5.264306E+01
+   1.331403E+01   5.263090E+01
+   1.333871E+01   5.262591E+01
+   1.336840E+01   5.262960E+01
+   1.338217E+01   5.264002E+01
+   1.339219E+01   5.264978E+01
+   1.339970E+01   5.264978E+01
+   1.341570E+01   5.264475E+01
+   1.342690E+01   5.263962E+01
+   1.343611E+01   5.265155E+01
+   1.346162E+01   5.265223E+01
+   1.346262E+01   5.265491E+01
+   1.344520E+01   5.266200E+01
+   1.345739E+01   5.267211E+01
+   1.346461E+01   5.267083E+01
+   1.347319E+01   5.267830E+01
+   1.349261E+01   5.267166E+01
+   1.348999E+01   5.265921E+01
+   1.352813E+01   5.264623E+01
+   1.352223E+01   5.262719E+01
+   1.351005E+01   5.262382E+01
+   1.350216E+01   5.260630E+01
+   1.351041E+01   5.259455E+01
+   1.352850E+01   5.259542E+01
+   1.355305E+01   5.259025E+01
+   1.358531E+01   5.257272E+01
+   1.359176E+01   5.255148E+01
+   1.364139E+01   5.254418E+01
+   1.363888E+01   5.253306E+01
+   1.366003E+01   5.253284E+01
+   1.366343E+01   5.252499E+01
+   1.364256E+01   5.251431E+01
+   1.363378E+01   5.249315E+01
+   1.361711E+01   5.247853E+01
+   1.362715E+01   5.247591E+01
+   1.364399E+01   5.248202E+01
+   1.366818E+01   5.247634E+01
+   1.369273E+01   5.246728E+01
+   1.370007E+01   5.247274E+01
+   1.372014E+01   5.246499E+01
+   1.373179E+01   5.245440E+01
+   1.376136E+01   5.244883E+01
+   1.376100E+01   5.243868E+01
+   1.375096E+01   5.243835E+01
+   1.373699E+01   5.241464E+01
+   1.374415E+01   5.240912E+01
+   1.373096E+01   5.239455E+01
+END
+END
diff --git a/extract-example-config/extracts.json b/extract-example-config/extracts.json
new file mode 100644
index 0000000..f256c6a
--- /dev/null
+++ b/extract-example-config/extracts.json
@@ -0,0 +1,135 @@
+{
+    "directory": "/tmp/",
+    "extracts": [
+        {
+            "output": "dresden.osm.pbf",
+            "output_format": "pbf",
+            "description": "City of Dresden specified using a bounding box (object format)",
+            "bbox": {
+                "left": 13.57,
+                "right": 13.97,
+                "top": 51.18,
+                "bottom": 50.97
+            }
+        },
+        {
+            "output": "munich.osm.pbf",
+            "description": "City of Munich specified using a bounding box (array format)",
+            "bbox": [11.35, 48.05, 11.73, 48.25]
+        },
+        {
+            "output": "berlin.osm.pbf",
+            "description": "Berlin area specified using a GeoJSON file",
+            "polygon": {
+                "file_name": "berlin.geojson",
+                "file_type": "geojson"
+            }
+        },
+        {
+            "output": "brandenburg.osm.pbf",
+            "description": "Brandenburg area without Berlin specified using a poly file with hole",
+            "multipolygon": {
+                "file_name": "brandenburg.poly"
+            }
+        },
+        {
+            "output": "karlsruhe.osm.pbf",
+            "description": "City of Karlsruhe specified using an OSM file",
+            "polygon": {
+                "file_name": "karlsruhe.osm.bz2",
+                "file_type": "osm"
+            }
+        },
+        {
+            "output": "hamburg.osm.pbf",
+            "description": "City of Hamburg specified using an inline polygon description",
+            "polygon": [[
+                [9.613465, 53.58071],
+                [9.647599, 53.59655],
+                [9.649288, 53.61059],
+                [9.710458, 53.62282],
+                [9.748647, 53.62482],
+                [9.755407, 53.63184],
+                [9.779739, 53.63144],
+                [9.780077, 53.62041],
+                [9.803734, 53.61340],
+                [9.810156, 53.60498],
+                [9.802720, 53.60237],
+                [9.821984, 53.59114],
+                [9.836178, 53.60137],
+                [9.850034, 53.60578],
+                [9.868622, 53.62402],
+                [9.886112, 53.63481],
+                [9.900712, 53.66354],
+                [9.976046, 53.65593],
+                [9.983638, 53.68637],
+                [10.04788, 53.68845],
+                [10.05897, 53.71541],
+                [10.07883, 53.72889],
+                [10.11328, 53.72232],
+                [10.16292, 53.74650],
+                [10.20263, 53.73511],
+                [10.19037, 53.70193],
+                [10.17343, 53.70193],
+                [10.16117, 53.67980],
+                [10.19621, 53.66665],
+                [10.20964, 53.64485],
+                [10.23417, 53.63793],
+                [10.22483, 53.61957],
+                [10.21431, 53.61957],
+                [10.20322, 53.61091],
+                [10.21023, 53.57902],
+                [10.16467, 53.57625],
+                [10.15825, 53.56827],
+                [10.16876, 53.56030],
+                [10.16818, 53.55197],
+                [10.16000, 53.54573],
+                [10.18803, 53.51830],
+                [10.21373, 53.52733],
+                [10.24643, 53.49364],
+                [10.30542, 53.45715],
+                [10.33637, 53.45298],
+                [10.32381, 53.42863],
+                [10.26775, 53.41297],
+                [10.24672, 53.38581],
+                [10.16789, 53.38929],
+                [10.10073, 53.42167],
+                [10.06452, 53.44776],
+                [9.982178, 53.40496],
+                [9.945679, 53.41784],
+                [9.915895, 53.40879],
+                [9.892536, 53.41645],
+                [9.907135, 53.44046],
+                [9.896624, 53.44672],
+                [9.871512, 53.43524],
+                [9.872680, 53.42584],
+                [9.855160, 53.42306],
+                [9.788586, 53.46723],
+                [9.787418, 53.48704],
+                [9.773986, 53.48739],
+                [9.750627, 53.50893],
+                [9.754714, 53.54850],
+                [9.693980, 53.55267],
+                [9.613465, 53.58071]
+            ]]
+        },
+        {
+            "output": "cologne.osm.pbf",
+            "description": "City of Cologne suburbs without city center specified using an inline multipolygon description",
+            "multipolygon": [[[
+                [6.847, 50.987],
+                [6.910, 51.007],
+                [7.037, 50.953],
+                [6.967, 50.880],
+                [6.842, 50.925],
+                [6.847, 50.987]
+            ],[
+                [6.967, 50.954],
+                [6.969, 50.920],
+                [6.932, 50.928],
+                [6.934, 50.950],
+                [6.967, 50.954]
+            ]]]
+        }
+    ]
+}
diff --git a/extract-example-config/hamburg.poly b/extract-example-config/hamburg.poly
new file mode 100644
index 0000000..afa6ac9
--- /dev/null
+++ b/extract-example-config/hamburg.poly
@@ -0,0 +1,71 @@
+hamburg
+1
+   9.613465E+00   5.358071E+01
+   9.647599E+00   5.359655E+01
+   9.649288E+00   5.361059E+01
+   9.710458E+00   5.362282E+01
+   9.748647E+00   5.362482E+01
+   9.755407E+00   5.363184E+01
+   9.779739E+00   5.363144E+01
+   9.780077E+00   5.362041E+01
+   9.803734E+00   5.361340E+01
+   9.810156E+00   5.360498E+01
+   9.802720E+00   5.360237E+01
+   9.821984E+00   5.359114E+01
+   9.836178E+00   5.360137E+01
+   9.850034E+00   5.360578E+01
+   9.868622E+00   5.362402E+01
+   9.886112E+00   5.363481E+01
+   9.900712E+00   5.366354E+01
+   9.976046E+00   5.365593E+01
+   9.983638E+00   5.368637E+01
+   1.004788E+01   5.368845E+01
+   1.005897E+01   5.371541E+01
+   1.007883E+01   5.372889E+01
+   1.011328E+01   5.372232E+01
+   1.016292E+01   5.374650E+01
+   1.020263E+01   5.373511E+01
+   1.019037E+01   5.370193E+01
+   1.017343E+01   5.370193E+01
+   1.016117E+01   5.367980E+01
+   1.019621E+01   5.366665E+01
+   1.020964E+01   5.364485E+01
+   1.023417E+01   5.363793E+01
+   1.022483E+01   5.361957E+01
+   1.021431E+01   5.361957E+01
+   1.020322E+01   5.361091E+01
+   1.021023E+01   5.357902E+01
+   1.016467E+01   5.357625E+01
+   1.015825E+01   5.356827E+01
+   1.016876E+01   5.356030E+01
+   1.016818E+01   5.355197E+01
+   1.016000E+01   5.354573E+01
+   1.018803E+01   5.351830E+01
+   1.021373E+01   5.352733E+01
+   1.024643E+01   5.349364E+01
+   1.030542E+01   5.345715E+01
+   1.033637E+01   5.345298E+01
+   1.032381E+01   5.342863E+01
+   1.026775E+01   5.341297E+01
+   1.024672E+01   5.338581E+01
+   1.016789E+01   5.338929E+01
+   1.010073E+01   5.342167E+01
+   1.006452E+01   5.344776E+01
+   9.982178E+00   5.340496E+01
+   9.945679E+00   5.341784E+01
+   9.915895E+00   5.340879E+01
+   9.892536E+00   5.341645E+01
+   9.907135E+00   5.344046E+01
+   9.896624E+00   5.344672E+01
+   9.871512E+00   5.343524E+01
+   9.872680E+00   5.342584E+01
+   9.855160E+00   5.342306E+01
+   9.788586E+00   5.346723E+01
+   9.787418E+00   5.348704E+01
+   9.773986E+00   5.348739E+01
+   9.750627E+00   5.350893E+01
+   9.754714E+00   5.354850E+01
+   9.693980E+00   5.355267E+01
+   9.613465E+00   5.358071E+01
+END
+END
diff --git a/extract-example-config/karlsruhe.osm.bz2 b/extract-example-config/karlsruhe.osm.bz2
new file mode 100644
index 0000000..f63440d
Binary files /dev/null and b/extract-example-config/karlsruhe.osm.bz2 differ
diff --git a/man/manpage.template b/man/manpage.template
index ab7ea69..749bf2b 100644
--- a/man/manpage.template
+++ b/man/manpage.template
@@ -15,7 +15,7 @@ $endfor$
 $if(author)$
 .SH COPYRIGHT
 .PP
-Copyright (C) 2013\-2016 Jochen Topf <jochen at topf.org>.
+Copyright (C) 2013\-2017 Jochen Topf <jochen at topf.org>.
 
 License GPLv3+: GNU GPL version 3 or later
 <https://gnu.org/licenses/gpl.html>.
diff --git a/man/osmium-add-locations-to-ways.md b/man/osmium-add-locations-to-ways.md
index 0b6faac..84829b7 100644
--- a/man/osmium-add-locations-to-ways.md
+++ b/man/osmium-add-locations-to-ways.md
@@ -100,5 +100,5 @@ Add node locations to a planet file (without untagged nodes):
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-apply-changes.md b/man/osmium-apply-changes.md
index d2d24c2..0f7a883 100644
--- a/man/osmium-apply-changes.md
+++ b/man/osmium-apply-changes.md
@@ -78,5 +78,5 @@ Apply changes in `362.osc.gz` to planet file and write result to `new.osm.pbf`:
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5), **osmium-merge-changes**(1), **osmium-derive-changes**(1)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-cat.md b/man/osmium-cat.md
index 31bffa7..fca08a5 100644
--- a/man/osmium-cat.md
+++ b/man/osmium-cat.md
@@ -67,5 +67,5 @@ Copy nodes and ways from source to destination file:
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-changeset-filter.md b/man/osmium-changeset-filter.md
index 0675645..e270408 100644
--- a/man/osmium-changeset-filter.md
+++ b/man/osmium-changeset-filter.md
@@ -88,5 +88,5 @@ To create an OPL file containing only open changesets:
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-check-refs.md b/man/osmium-check-refs.md
index 7850cef..0b2a4cb 100644
--- a/man/osmium-check-refs.md
+++ b/man/osmium-check-refs.md
@@ -72,6 +72,6 @@ that's roughly 500 MB these days (Summer 2015). With the **-r**,
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
 
diff --git a/man/osmium-derive-changes.md b/man/osmium-derive-changes.md
index a228812..192d803 100644
--- a/man/osmium-derive-changes.md
+++ b/man/osmium-derive-changes.md
@@ -86,5 +86,5 @@ Find changes in Nepal extract in January 2016:
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5), **osmium-apply-changes**(1), **osmium-diff**(1)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-diff.md b/man/osmium-diff.md
index 85dbb73..cb75293 100644
--- a/man/osmium-diff.md
+++ b/man/osmium-diff.md
@@ -18,7 +18,7 @@ displayed. Headers are ignored.
 
 Objects in both input files must be sorted in the same order.
 
-Several output formats are supported, see the **OUTPUT FORMAT** section.
+Several output formats are supported, see the **OUTPUT FORMATS** section.
 
 This command is intended for displaying the differences between files to
 humans. It can not be used to create an OSM change file (`.osc`), use
@@ -58,7 +58,7 @@ None of the output formats print the headers of the input files.
 :   Do not output objects that are the same in both files.
 
 -f, --output-format=FORMAT
-:   See section OUTPUT FORMATS.
+:   See the **OUTPUT FORMATS** section.
 
 -o, --output=FILE
 :   Name of the output file. Default is '-' (*stdout*).
@@ -116,5 +116,5 @@ Show in color debug format only those objects that are different:
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5), **osmium-derive-changes**(1)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-extract.md b/man/osmium-extract.md
new file mode 100644
index 0000000..6da2ac1
--- /dev/null
+++ b/man/osmium-extract.md
@@ -0,0 +1,361 @@
+
+# NAME
+
+osmium-extract - create geographical extracts from an OSM file
+
+
+# SYNOPSIS
+
+**osmium extract** --config *CONFIG-FILE* \[*OPTIONS*\] *OSM-FILE*\
+**osmium extract** --bbox *LEFT*,*BOTTOM*,*RIGHT*,*TOP* \[*OPTIONS*\] *OSM-FILE*\
+**osmium extract** --polygon *POLYGON-FILE* \[*OPTIONS*\] *OSM-FILE*
+
+
+# DESCRIPTION
+
+Create geographical extracts from an OSM data file or an OSM history file.
+The region (geographical extent) can be given as a bounding box or as a
+(multi)polygon.
+
+There are three ways of calling this command:
+
+* Specify a config file with the --config/-c option. It can define any number
+  of regions you want to cut out. See the **CONFIG FILE** section for details.
+
+* Specify a bounding box to cut out with the --bbox/-b option.
+
+* Specify a (multi)polygon to cut out with the --polygon/-p option.
+
+The input file is assumed to be ordered in the usual order: nodes first, then
+ways, then relations.
+
+If the `--with-history` option is used, the command will work correctly for
+history files. This currently works for the **complete_ways** strategy only.
+The **simple** or **smart** strategies do not work with history files. A
+history extract will contain every version of all objects with at least one
+version in the region. Generating a history extract is somewhat slower than
+a normal data extract.
+
+Osmium will make sure that all nodes on the vertices of the boundary of the
+region will be in the extract, but nodes that happen to be directly on the
+boundary, but between those vertices, might end up in the extract or not. In
+almost all cases this will be good enough, but if you want to make really sure
+you got everything, use a small buffer around your region.
+
+No **bounds** will be set in the header of the output file. Which bounds would
+be correct is unclear and setting it correctly might need an extra pass through
+the input file.
+
+Note that **osmium extract** will never clip any OSM objects, ie. it will not
+remove node references outside the region from ways or unused relation members
+from relations. This means you might get objects that are not
+reference-complete. It has the advantage that you can use **osmium merge**
+to merge several extracts without problems.
+
+
+# OPTIONS
+
+-b, --bbox=LEFT,BOTTOM,RIGHT,TOP
+:   Set the bounding box to cut out. Can not be used with --polygon/-p,
+    --config/-c, or --directory/-d.
+
+-c, --config=FILE
+:   Set the name of the config file. Can not be used with the --bbox/-b or
+    --polygon/-p option. If this is set, the --output/-o and --output-format/-f
+    options are ignored, because they are set in the config file.
+
+-d, --directory=DIRECTORY
+:   Output directory. Output file names in the config file are relative to
+    this directory. Overwrites the setting of the same name in the config
+    file. This option is ignored when the --bbox/-b or --polygon/-p options
+    are used, set the output directory and name with the --output/-o option
+    in that case.
+
+-p, --polygon=POLYGON_FILE
+:   Set the polygon to cut out based on the contents of the file. The file
+    has to be a GeoJSON, poly, or OSM file as described in the
+    **(MULTI)POLYGON FILE FORMATS** section. It has to have the right suffix
+    to be detected correctly. Can not be used with --bbox/-b, --config/-c,
+    or --directory/-d.
+
+-s, --strategy=STRATEGY
+:   Use the given strategy to extract the region. For possible values and
+    details see the **STRATEGIES** section. Default is "complete_ways".
+
+-S, --option=OPTION=VALUE
+:   Set a named option for the strategy. If needed you can specify this
+    option multiple times to set several options.
+
+--with-history
+:   Specify that the input file is a history file. The output file(s) will also
+    be history file(s).
+
+
+ at MAN_COMMON_OPTIONS@
+ at MAN_INPUT_OPTIONS@
+ at MAN_OUTPUT_OPTIONS@
+
+
+# CONFIG FILE
+
+The config file mainly specifies the file names and the regions of the extracts
+that should be created.
+
+The config file is in JSON format. The top-level is an object which contains at
+least an "extracts" array. It can also contain a "directory" entry which names
+the directory where all the output files will be created:
+
+    {
+        "extracts": [...],
+        "directory": "/tmp/"
+    }
+
+
+The extracts array specifies the extracts that should be created. Each item in
+the array is an object with at least a name "output" naming the output file and
+a region defined in a "bbox", "polygon" or "multipolygon" name. An optional
+"description" can be added, it will not be used by the program but can help
+with documenting the file contents. You can add an optional "output_format"
+if the format can not be detected from the "output" file name. Run "osmium
+help file-formats" to get a description of allowed formats.
+
+    "extracts": [
+        {
+            "output": "hamburg.osm.pbf",
+            "output_format": "pbf",
+            "description": "optional description",
+            "box": ...
+        },
+        {
+            "output": "berlin.osm.pbf",
+            "description": "optional description",
+            "polygon": ...
+        },
+        {
+            "output": "munich.osm.pbf",
+            "description": "optional description",
+            "multipolygon": ...
+        }
+    ]
+
+There are several formats for specifying the regions:
+
+**box**:
+
+A bounding box in one of two formats. The first is a simple array with
+four real numbers specifying the left, bottom, right, and top boundary
+(in that order):
+
+    {
+        "output": "munich.osm.pbf",
+        "description": "Bounding box specified in array format",
+        "bbox": [11.35, 48.05, 11.73, 48.25]
+    }
+
+The second format uses an object instead of an array:
+
+    {
+        "output": "dresden.osm.pbf",
+        "description": "Bounding box specified in object format",
+        "bbox": {
+            "left": 13.57,
+            "right": 13.97,
+            "top": 51.18,
+            "bottom": 50.97
+        }
+    }
+
+**polygon**:
+
+A polygon, either specified inline in the config file or read from an external
+file. See the **(MULTI)POLYGON FILE FORMATS** section for external files. If
+specified inline this is a nested array, the outer array defining the polygon,
+the next array the rings and the innermost arrays the coordinates. This format
+is the same as in GeoJSON files.
+
+In this example there is only one outer ring:
+
+    "polygon": [[
+        [9.613465, 53.58071],
+        [9.647599, 53.59655],
+        [9.649288, 53.61059],
+        [9.613465, 53.58071]
+    ]]
+
+In each ring, the last set of coordinates should be the same as the first set,
+closing the ring.
+
+**multipolygon**:
+
+A multipolygon, either specified inline in the config file or read from an
+external file. See the **(MULTI)POLYGON FILE FORMATS** section for external
+files. If specified inline this is a nested array, the outer array defining the
+multipolygon, the next array the polygons, the next the rings and the innermost
+arrays the coordinates. This format is the same as in GeoJSON files.
+
+In this example there is one outer and one inner ring:
+
+    "multipolygon": [[[
+        [6.847, 50.987],
+        [6.910, 51.007],
+        [7.037, 50.953],
+        [6.967, 50.880],
+        [6.842, 50.925],
+        [6.847, 50.987]
+    ],[
+        [6.967, 50.954],
+        [6.969, 50.920],
+        [6.932, 50.928],
+        [6.934, 50.950],
+        [6.967, 50.954]
+    ]]]
+
+In each ring, the last set of coordinates should be the same as the first set,
+closing the ring.
+
+Osmium must check each and every node in the input data and find out in which
+bounding boxes or (multi)polygons this node is. This is very cheap for bounding
+boxes, but more expensive for (multi)polygons. And it becomes more expensive
+the more vertices the (multi)polyon has. Use bounding boxes or simplified
+polygons where possible.
+
+
+# (MULTI)POLYGON FILE FORMATS
+
+External files describing a (multi)polygon are specified in the config file
+using the "file_name" and "file_type" properties on the "polygon" or
+"multipolygon" object:
+
+    "polygon": {
+        "file_name": "berlin.geojson",
+        "file_type": "geojson"
+    }
+
+If file names don't start with a slash (/), they are interpreted relative to
+the directory where the config file is. If the "file_type" is missing, Osmium
+will try to autodetect it from the suffix of the "file_name".
+
+The following file types are supported:
+
+geojson
+:   GeoJSON file containing exactly one polygon or multipolygon. Everything
+    except the actual geometry is ignored.
+
+poly
+:   A poly file as described in
+    https://wiki.openstreetmap.org/wiki/Osmosis/Polygon_Filter_File_Format .
+    This wiki page also mentions several sources for such poly files.
+
+osm
+:   An OSM file containing one or more multipolygon or boundary relation
+    together with all the nodes and ways needed. Any OSM file format (XML,
+    PBF, ...) supported by Osmium can be used here, but the correct suffix
+    must be used, so the file format is detected correctly. Files for this can
+    easily be obtained by searching for the area on OSM and then downloading
+    the full relation using a URL like
+    http://www.openstreetmap.org/api/0.6/relation/RELATION-ID/full . Or
+    you can use **osmium getid -r** to get a specific relation from an OSM
+    file. Note that both these approaches can get you very detailed boundaries
+    which can take quite a while to cut out. Consider simplifying the boundary
+    before use.
+
+If there are several (multi)polygons in a poly file or OSM file, they will
+be merged. The (multi)polygons must not overlap, otherwise the result is
+undefined.
+
+
+# STRATEGIES
+
+**osmium extract** can use different strategies for creating the extracts.
+Depending on the strategy different objects will end up in the extracts. The
+strategies differ in how much memory they need and how often they need to read
+the input file. The choice of strategy depends on how you want to use the
+generated extracts and how much memory and time you have.
+
+The default strategy is **complete_ways**.
+
+Strategy **simple**
+:   Runs in a single pass. The extract will contain all nodes inside the
+    region and all ways referencing those nodes as well as all relations
+    referencing any nodes or ways already included. Ways crossing the region
+    boundary will not be reference-complete. Relations will not be
+    reference-complete. This strategy is fast, because it reads the input only
+    once, but the result is not enough for most use cases. It is the only
+    strategy that will work when reading from a socket or pipe. This strategy
+    will not work for history files.
+
+Strategy **complete_ways**
+:   Runs in two passes. The extract will contain all nodes inside the region
+    and all ways referencing those nodes as well as all nodes referenced by
+    those ways. The extract will also contain all relations referenced by
+    nodes inside the region or ways already included and, recursively, their
+    parent relations. The ways are reference-complete, but the relations are
+    not.
+
+Strategy **smart**
+:   Runs in three passes. The extract will contain all nodes inside the region
+    and all ways referencing those nodes as well as all nodes referenced by
+    those ways. The extract will also contain all relations referenced by
+    nodes inside the region or ways already included and, recursively, their
+    parent relations. The extract will also contain all nodes and ways (and
+    the nodes they reference) referenced by relations tagged
+    "type=multipolygon" directly referencing any nodes in the region or ways
+    referencing nodes in the region. The ways are reference-complete, and
+    all multipolygon relations referencing nodes in the regions or ways that
+    have nodes in the region are reference-complete. Other relations are not
+    reference-complete.
+
+For the **smart** strategy you can change the types of relations that should be
+reference-complete. Instead of just relations tagged "type=multipolygon", you
+can either get all relations (use "-S all") or give a list of types to the
+-S option: "-S multipolygon,route". Note that especially boundary relations
+can be huge, so if you include them, be aware your result might be huge.
+
+
+# DIAGNOSTICS
+
+**osmium extract** exits with exit code
+
+0
+  ~ if everything went alright,
+
+1
+  ~ if there was an error processing the data, or
+
+2
+  ~ if there was a problem with the command line arguments, config file or
+    polygon files.
+
+
+# MEMORY USAGE
+
+Memory usage of **osmium extract** depends on the number of extracts and on the
+strategy used. For the *simple* strategy it will at least be the number of
+extracts times the highest node ID used devided by 8. For the *complete_ways*
+twice that and for the *smart* strategy a bit more.
+
+
+# EXAMPLES
+
+See the example config files in the *extract-example-config* directory. To
+try it:
+
+    osmium extract -v -c extract-example-config/extracts.json \
+        germany-latest.osm.pbf
+
+Extract the city of Karlsruhe using a boundary polygon:
+
+    osmium -p karlsruhe-boundary.osm.bz2 germany-latest.osm.pbf \
+        -o karlsruhe.osm.pbf
+
+Extract the city of Munich using a bounding box:
+
+    osmium -b 11.35,48.05,11.73,48.25 germany-latest.osm.pbf \
+        -o munich.osm.pbf
+
+
+# SEE ALSO
+
+* **osmium**(1), **osmium-file-formats**(5), **osmium-getid**(1), **osmium-merge**(1)
+* [Osmium website](http://osmcode.org/osmium-tool/)
+
diff --git a/man/osmium-file-formats.md b/man/osmium-file-formats.md
index ef8dd2e..4023d3a 100644
--- a/man/osmium-file-formats.md
+++ b/man/osmium-file-formats.md
@@ -11,7 +11,7 @@ Osmium library. These are:
 * The classical XML format in the variants *.osm* (for data files),
   *.osh* (for data files with history) and *.osc* (for change files).
 * The PBF binary format (usually with suffix *.osm.pbf*).
-* The OPL format (usually with suffix *.osm.opl*) (writing only).
+* The OPL format (usually with suffix *.osm.opl*).
 * The O5M/O5C format (usually with suffix *.o5m* or *.o5c*) (reading only).
 * The "debug" format (usually with suffix *.osm.debug*) (writing only).
 
@@ -36,20 +36,20 @@ set additional options.
 
 The following options can be added when writing OSM files:
 
-xml_change_format=true/false
+`xml_change_format`=`true`/`false`
 :   Enable/disable XML change format. Same as *.osc*.
 
-force_visible_flag=true/false (*default: false*)
+`force_visible_flag`=`true`/`false` (*default: false*)
 :   Force writing of visible flag, even for normal OSM XML files.
 
-pbf_dense_nodes=true/false (*default: true*)
+`pbf_dense_nodes`=`true`/`false` (*default: true*)
 :   Enable/disable DenseNodes format for PBF files.
 
-pbf_compression=true/false (*default: true*)
+`pbf_compression`=`true`/`false` (*default: true*)
 :   Enable/disable compression in PBF files. Disabling this will make writing
     files a bit faster, but the resulting files are 2 to 3 times bigger.
 
-add_metadata=true/false (*default: true*)
+`add_metadata`=`true`/`false` (*default: true*)
 :   Enable/disable writing of object metadata such as changeset id, username,
     etc. Disabling this will make files a bit smaller.
 
@@ -58,24 +58,25 @@ add_metadata=true/false (*default: true*)
 
 Here are some examples:
 
-pbf
+`pbf`
 :   PBF format.
 
-pbf,add_metadata=false
+`pbf,add_metadata=false`
 :   PBF format, dont' write metadata
 
-osm.bz2
+`osm.bz2`
 :   XML format, compressed with bzip2.
 
-osc.gz
+`osc.gz`
 :   OSM change file, compressed with gzip.
 
-osm.gz,xml_change_format=true
+`osm.gz,xml_change_format=true`
 :   OSM change file, compressed with gzip.
 
 
 # SEE ALSO
 
 * **osmium**(1)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
+* [OSM File Formats Manual](http://osmcode.org/file-formats-manual/)
 
diff --git a/man/osmium-fileinfo.md b/man/osmium-fileinfo.md
index 8e88013..5d2bcec 100644
--- a/man/osmium-fileinfo.md
+++ b/man/osmium-fileinfo.md
@@ -126,5 +126,5 @@ main memory.
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-getid.md b/man/osmium-getid.md
index 1b248b7..4d3c280 100644
--- a/man/osmium-getid.md
+++ b/man/osmium-getid.md
@@ -130,5 +130,5 @@ Output nodes 17 and 1234, way 42, and relation 111 to *stdout* in OPL format:
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-merge-changes.md b/man/osmium-merge-changes.md
index c687f6c..180573e 100644
--- a/man/osmium-merge-changes.md
+++ b/man/osmium-merge-changes.md
@@ -64,5 +64,5 @@ just a single change file:
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5), **osmium-merge**(1)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-merge.md b/man/osmium-merge.md
index 44c5bc1..2646633 100644
--- a/man/osmium-merge.md
+++ b/man/osmium-merge.md
@@ -59,5 +59,5 @@ Merge several extracts into one:
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5), **osmium-merge-changes**(1)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-renumber.md b/man/osmium-renumber.md
index 9bcab4c..5077cef 100644
--- a/man/osmium-renumber.md
+++ b/man/osmium-renumber.md
@@ -37,7 +37,7 @@ IDs.
     in several OSM files. Without this option, the indexes are not read from
     or written to disk. The directory must exist. Use '.' for the current
     directory. The files written will be named `nodes.idx`, `ways.idx`, and
-    `relations.idx`. See also the section INDEX FILES below.
+    `relations.idx`. See also the **INDEX FILES** section below.
 
 -t, --object-type=TYPE
 :   Renumber only objects of given type (*node*, *way*, or *relation*). By
@@ -109,5 +109,5 @@ then rewrite a change file, too:
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-show.md b/man/osmium-show.md
index 5da5c35..4ac8b40 100644
--- a/man/osmium-show.md
+++ b/man/osmium-show.md
@@ -91,5 +91,5 @@ Show using XML format:
 # SEE ALSO
 
 * **osmium**(1), **osmium-cat**(1), **osmium-file-formats**(5)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-sort.md b/man/osmium-sort.md
index 9861893..5fe40ea 100644
--- a/man/osmium-sort.md
+++ b/man/osmium-sort.md
@@ -51,5 +51,5 @@ Sort *in.osm.bz2* and write out to *sorted.osm.pbf*:
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium-time-filter.md b/man/osmium-time-filter.md
index b0b3cdc..8a89e4e 100644
--- a/man/osmium-time-filter.md
+++ b/man/osmium-time-filter.md
@@ -63,5 +63,5 @@ Extract planet data how it appeared on January 1 2008 from history planet:
 # SEE ALSO
 
 * **osmium**(1), **osmium-file-formats**(5)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/man/osmium.md b/man/osmium.md
index 3b752d5..d724baa 100644
--- a/man/osmium.md
+++ b/man/osmium.md
@@ -48,6 +48,9 @@ derive-changes
 diff
 :   display differences between OSM files
 
+extract
+:   create geographical extracts from an OSM file
+
 fileinfo
 :   show information about an OSM file
 
@@ -121,5 +124,5 @@ If an osmium command exits with an "Out of memory" error, try running it with
   **osmium-sort**(1),
   **osmium-time-filter**(1),
   **osmium-file-formats**(5)
-* [Osmium website](http://osmcode.org/osmium)
+* [Osmium website](http://osmcode.org/osmium-tool/)
 
diff --git a/scripts/osm-history-splitter2osmium-extract-config.sh b/scripts/osm-history-splitter2osmium-extract-config.sh
new file mode 100755
index 0000000..4dec839
--- /dev/null
+++ b/scripts/osm-history-splitter2osmium-extract-config.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+#
+#  Convert a config file in the format for the osm-history-splitter into a
+#  config file for the "osmium extract" command.
+#
+
+if [ -n "$1" ]; then
+    exec <$1
+fi
+
+echo '{'
+echo '    "extracts": ['
+while read output type region; do
+    echo '        {'
+    echo "            \"output\": \"$output\","
+    if [ "$type" = "BBOX" ]; then
+        echo "            \"bbox\": [$region]"
+    elif [ "$type" = "OSM" -o "$type" = "POLY" ]; then
+        lctype=`echo $type | tr 'A-Z' 'a-z'`
+        cat << __END__
+            "polygon": {
+                "file_name": "$region",
+                "file_type": "$lctype"
+            }
+__END__
+    fi
+    echo '        },'
+done
+
+echo '    ]'
+echo '}'
+
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 5b034e5..8c84e6e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -6,7 +6,7 @@
 #
 #-----------------------------------------------------------------------------
 
-file(GLOB OSMIUM_SOURCE_FILES *.cpp ${PROJECT_BINARY_DIR}/src/version.cpp)
+file(GLOB OSMIUM_SOURCE_FILES *.cpp extract/*.cpp ${PROJECT_BINARY_DIR}/src/version.cpp)
 
 add_executable(osmium ${OSMIUM_SOURCE_FILES})
 
diff --git a/src/cmd.cpp b/src/cmd.cpp
index 859d970..b51def0 100644
--- a/src/cmd.cpp
+++ b/src/cmd.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -30,14 +30,6 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 #include "cmd.hpp"
 #include "exception.hpp"
 
-const char* yes_no(bool choice) noexcept {
-    return choice ? "yes\n" : "no\n";
-}
-
-void warning(const char* text) {
-    std::cerr << "WARNING: " << text;
-}
-
 po::options_description Command::add_common_options() {
     po::options_description options("COMMON OPTIONS");
 
diff --git a/src/cmd.hpp b/src/cmd.hpp
index 0e16a56..5548df3 100644
--- a/src/cmd.hpp
+++ b/src/cmd.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -43,10 +43,6 @@ const char* get_osmium_version() noexcept;
 const char* get_osmium_long_version() noexcept;
 const char* get_libosmium_version() noexcept;
 
-const char* yes_no(bool choice) noexcept;
-void warning(const char* text);
-std::size_t file_size_sum(std::vector<osmium::io::File> files);
-
 namespace osmium { namespace io {
     class Header;
 }}
@@ -178,11 +174,10 @@ public:
 
 class with_osm_output {
 
-    std::string m_generator;
-    std::vector<std::string> m_output_headers;
-
 protected:
 
+    std::string m_generator;
+    std::vector<std::string> m_output_headers;
     std::string m_output_filename = "-"; // default: stdout
     std::string m_output_format;
     osmium::io::File m_output_file;
@@ -196,6 +191,8 @@ public:
         m_generator.append(get_osmium_version());
     }
 
+    void init_output_file(const po::variables_map& vm);
+    void check_output_file();
     void setup_output_file(const po::variables_map& vm);
 
     po::options_description add_output_options();
diff --git a/src/cmd_factory.cpp b/src/cmd_factory.cpp
index 9220396..d0d386e 100644
--- a/src/cmd_factory.cpp
+++ b/src/cmd_factory.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_add_locations_to_ways.cpp b/src/command_add_locations_to_ways.cpp
index e96a517..4e8be62 100644
--- a/src/command_add_locations_to_ways.cpp
+++ b/src/command_add_locations_to_ways.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -44,6 +44,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include "command_add_locations_to_ways.hpp"
 #include "exception.hpp"
+#include "util.hpp"
 
 bool CommandAddLocationsToWays::setup(const std::vector<std::string>& arguments) {
     const auto& map_factory = osmium::index::MapFactory<osmium::unsigned_object_id_type, osmium::Location>::instance();
diff --git a/src/command_add_locations_to_ways.hpp b/src/command_add_locations_to_ways.hpp
index 716f63a..f3033c6 100644
--- a/src/command_add_locations_to_ways.hpp
+++ b/src/command_add_locations_to_ways.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_apply_changes.cpp b/src/command_apply_changes.cpp
index 4c85a64..5afe39c 100644
--- a/src/command_apply_changes.cpp
+++ b/src/command_apply_changes.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -45,6 +45,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include "command_apply_changes.hpp"
 #include "exception.hpp"
+#include "util.hpp"
 
 bool CommandApplyChanges::setup(const std::vector<std::string>& arguments) {
     po::options_description opts_cmd{"COMMAND OPTIONS"};
@@ -121,34 +122,38 @@ void CommandApplyChanges::show_arguments() {
     m_vout << "  reading and writing history file: " << yes_no(m_with_history);
 }
 
-/**
- *  Copy the first OSM object with a given Id to the output. Keep
- *  track of the Id of each object to do this.
- *
- *  We are using this functor class instead of a simple lambda, because the
- *  lambda doesn't build on MSVC.
- */
-class copy_first_with_id {
+namespace {
 
-    osmium::io::Writer* writer;
-    osmium::object_id_type id = 0;
+    /**
+     *  Copy the first OSM object with a given Id to the output. Keep
+     *  track of the Id of each object to do this.
+     *
+     *  We are using this functor class instead of a simple lambda, because the
+     *  lambda doesn't build on MSVC.
+     */
+    class copy_first_with_id {
 
-public:
+        osmium::io::Writer* writer;
+        osmium::object_id_type id = 0;
 
-    explicit copy_first_with_id(osmium::io::Writer& w) :
-        writer(&w) {
-    }
+    public:
+
+        explicit copy_first_with_id(osmium::io::Writer& w) :
+            writer(&w) {
+        }
 
-    void operator()(const osmium::OSMObject& obj) {
-        if (obj.id() != id) {
-            if (obj.visible()) {
-                (*writer)(obj);
+        void operator()(const osmium::OSMObject& obj) {
+            if (obj.id() != id) {
+                if (obj.visible()) {
+                    (*writer)(obj);
+                }
+                id = obj.id();
             }
-            id = obj.id();
         }
-    }
 
-}; // class copy_first_with_id
+    }; // class copy_first_with_id
+
+} // anonymous namespace
 
 bool CommandApplyChanges::run() {
     std::vector<osmium::memory::Buffer> changes;
diff --git a/src/command_apply_changes.hpp b/src/command_apply_changes.hpp
index 267a7c7..f38cac7 100644
--- a/src/command_apply_changes.hpp
+++ b/src/command_apply_changes.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_cat.cpp b/src/command_cat.cpp
index f9017af..94125e4 100644
--- a/src/command_cat.cpp
+++ b/src/command_cat.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -35,6 +35,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 #include <osmium/util/verbose_output.hpp>
 
 #include "command_cat.hpp"
+#include "util.hpp"
 
 bool CommandCat::setup(const std::vector<std::string>& arguments) {
     po::options_description opts_cmd{"COMMAND OPTIONS"};
diff --git a/src/command_cat.hpp b/src/command_cat.hpp
index 1356857..caa3e37 100644
--- a/src/command_cat.hpp
+++ b/src/command_cat.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_changeset_filter.cpp b/src/command_changeset_filter.cpp
index 8f81f4d..06bc9bc 100644
--- a/src/command_changeset_filter.cpp
+++ b/src/command_changeset_filter.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_changeset_filter.hpp b/src/command_changeset_filter.hpp
index d198d8d..3f52b66 100644
--- a/src/command_changeset_filter.hpp
+++ b/src/command_changeset_filter.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_check_refs.cpp b/src/command_check_refs.cpp
index 326b993..8fc8838 100644
--- a/src/command_check_refs.cpp
+++ b/src/command_check_refs.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -39,6 +39,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 #include <osmium/visitor.hpp>
 
 #include "command_check_refs.hpp"
+#include "util.hpp"
 
 bool CommandCheckRefs::setup(const std::vector<std::string>& arguments) {
     po::options_description opts_cmd{"COMMAND OPTIONS"};
diff --git a/src/command_check_refs.hpp b/src/command_check_refs.hpp
index 0e91863..7e94af1 100644
--- a/src/command_check_refs.hpp
+++ b/src/command_check_refs.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_derive_changes.cpp b/src/command_derive_changes.cpp
index 947e7f0..9aab407 100644
--- a/src/command_derive_changes.cpp
+++ b/src/command_derive_changes.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -39,6 +39,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include "command_derive_changes.hpp"
 #include "exception.hpp"
+#include "util.hpp"
 
 bool CommandDeriveChanges::setup(const std::vector<std::string>& arguments) {
     po::options_description opts_cmd{"COMMAND OPTIONS"};
diff --git a/src/command_derive_changes.hpp b/src/command_derive_changes.hpp
index 2d51ed8..d4923ba 100644
--- a/src/command_derive_changes.hpp
+++ b/src/command_derive_changes.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_diff.cpp b/src/command_diff.cpp
index aead57f..3920b83 100644
--- a/src/command_diff.cpp
+++ b/src/command_diff.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -45,6 +45,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include "command_diff.hpp"
 #include "exception.hpp"
+#include "util.hpp"
 
 bool CommandDiff::setup(const std::vector<std::string>& arguments) {
     po::options_description opts_cmd{"COMMAND OPTIONS"};
@@ -184,7 +185,7 @@ class OutputActionCompact : public OutputAction {
 
 public:
 
-    OutputActionCompact(int fd) noexcept :
+    explicit OutputActionCompact(int fd) noexcept :
         m_fd(fd) {
     }
 
diff --git a/src/command_diff.hpp b/src/command_diff.hpp
index 5734b88..0bbae7e 100644
--- a/src/command_diff.hpp
+++ b/src/command_diff.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_extract.cpp b/src/command_extract.cpp
new file mode 100644
index 0000000..b7170aa
--- /dev/null
+++ b/src/command_extract.cpp
@@ -0,0 +1,486 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <cassert>
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+#include <boost/program_options.hpp>
+
+#include <rapidjson/document.h>
+#include <rapidjson/error/en.h>
+#include <rapidjson/istreamwrapper.h>
+
+#include <osmium/geom/coordinates.hpp>
+#include <osmium/io/any_input.hpp>
+#include <osmium/io/header.hpp>
+#include <osmium/io/writer_options.hpp>
+#include <osmium/osm.hpp>
+#include <osmium/osm/box.hpp>
+#include <osmium/util/progress_bar.hpp>
+#include <osmium/util/string.hpp>
+#include <osmium/util/verbose_output.hpp>
+
+#include "command_extract.hpp"
+#include "exception.hpp"
+
+#include "extract/error.hpp"
+#include "extract/extract_bbox.hpp"
+#include "extract/extract_polygon.hpp"
+#include "extract/geojson_file_parser.hpp"
+#include "extract/osm_file_parser.hpp"
+#include "extract/poly_file_parser.hpp"
+#include "extract/strategy_complete_ways.hpp"
+#include "extract/strategy_complete_ways_with_history.hpp"
+#include "extract/strategy_simple.hpp"
+#include "extract/strategy_smart.hpp"
+#include "util.hpp"
+
+namespace {
+
+    osmium::Box parse_bbox(const rapidjson::Value& value) {
+        if (value.IsArray()) {
+            if (value.Size() == 4) {
+                if (value[0].IsNumber() && value[1].IsNumber() && value[2].IsNumber() && value[3].IsNumber()) {
+                    const osmium::Location bottom_left{value[0].GetDouble(), value[1].GetDouble()};
+                    const osmium::Location top_right{value[2].GetDouble(), value[3].GetDouble()};
+
+                    if (bottom_left < top_right) {
+                        return osmium::Box{bottom_left, top_right};
+                    }
+                    throw config_error{"'bbox' array elements must be in order: left, bottom, right, top."};
+                }
+                throw config_error{"'bbox' array elements must be numbers."};
+            } else {
+                throw config_error{"'bbox' must be an array with exactly four elements."};
+            }
+        } else if (value.IsObject()) {
+            const auto left   = value.FindMember("left");
+            const auto right  = value.FindMember("right");
+            const auto top    = value.FindMember("top");
+            const auto bottom = value.FindMember("bottom");
+
+            if (left != value.MemberEnd() && right  != value.MemberEnd() &&
+                top  != value.MemberEnd() && bottom != value.MemberEnd()) {
+                if (left->value.IsNumber() && right->value.IsNumber() &&
+                    top->value.IsNumber()  && bottom->value.IsNumber()) {
+
+                    const osmium::Location bottom_left{left->value.GetDouble(), bottom->value.GetDouble()};
+                    const osmium::Location top_right{right->value.GetDouble(), top->value.GetDouble()};
+
+                    if (bottom_left.x() < top_right.x() &&
+                        bottom_left.y() < top_right.y()) {
+                        return osmium::Box{bottom_left, top_right};
+                    }
+
+                    throw config_error{"Need 'left' < 'right' and 'bottom' < 'top' in 'bbox' object."};
+                }
+                throw config_error{"Members in 'bbox' object must be numbers."};
+            }
+
+            throw config_error{"Need 'left', 'right', 'top', and 'bottom' members in 'bbox' object."};
+        }
+
+        throw config_error{"'bbox' member is not an array or object."};
+    }
+
+    osmium::Box parse_bbox(const std::string& str) {
+        const auto coordinates = osmium::split_string(str, ',');
+
+        if (coordinates.size() != 4) {
+            throw argument_error{"Need exactly four coordinates in --bbox/-b option."};
+        }
+
+        const osmium::Location bottom_left{std::atof(coordinates[0].c_str()), std::atof(coordinates[1].c_str())};
+        const osmium::Location top_right{std::atof(coordinates[2].c_str()), std::atof(coordinates[3].c_str())};
+
+        if (bottom_left.x() < top_right.x() &&
+            bottom_left.y() < top_right.y()) {
+            return osmium::Box{bottom_left, top_right};
+        }
+
+        throw argument_error{"Need LEFT < RIGHT and BOTTOM < TOP in --bbox/-b option."};
+    }
+
+    std::size_t parse_multipolygon_object(const std::string& directory, std::string file_name, std::string file_type, osmium::memory::Buffer& buffer) {
+        if (file_name.empty()) {
+            throw config_error{"Missing 'file_name' in '(multi)polygon' object."};
+        }
+
+        if (file_name[0] != '/') {
+            // relative file name
+            file_name = directory + file_name;
+        }
+
+        // If the file type is not set, try to deduce it from the file name
+        // suffix.
+        if (file_type.empty()) {
+            std::string suffix{get_filename_suffix(file_name)};
+            if (suffix == "poly") {
+                file_type = "poly";
+            } else if (suffix == "json" || suffix == "geojson") {
+                file_type = "geojson";
+            } else {
+                osmium::io::File osmfile{"", suffix};
+                if (osmfile.format() != osmium::io::file_format::unknown) {
+                    file_type = "osm";
+                }
+            }
+        }
+
+        if (file_type == "osm") {
+            try {
+                OSMFileParser parser{buffer, file_name};
+                return parser();
+            } catch (const osmium::io_error& e) {
+                throw osmium::io_error{std::string{"While reading file '"} + file_name + "':\n" + e.what()};
+            }
+        } else if (file_type == "geojson") {
+            GeoJSONFileParser parser{buffer, file_name};
+            try {
+                return parser();
+            } catch (const config_error& e) {
+                throw geojson_error{e.what()};
+            }
+        } else if (file_type == "poly") {
+            PolyFileParser parser{buffer, file_name};
+            return parser();
+        } else if (file_type == "") {
+            throw config_error{"Could not autodetect file type in '(multi)polygon' object. Add a 'file_type'."};
+        }
+
+        throw config_error{std::string{"Unknown file type: '"} + file_type + "' in '(multi)polygon.file_type'"};
+    }
+
+    std::size_t parse_multipolygon_object(const std::string& directory, const rapidjson::Value& value, osmium::memory::Buffer& buffer) {
+        std::string file_name{get_value_as_string(value, "file_name")};
+        std::string file_type{get_value_as_string(value, "file_type")};
+        return parse_multipolygon_object(directory, file_name, file_type, buffer);
+    }
+
+    std::size_t parse_polygon(const std::string& directory, const rapidjson::Value& value, osmium::memory::Buffer& buffer) {
+        if (value.IsArray()) {
+            return parse_polygon_array(value, buffer);
+        } else if (value.IsObject()) {
+            return parse_multipolygon_object(directory, value, buffer);
+        }
+
+        throw config_error{"Polygon must be an object or array."};
+    }
+
+    std::size_t parse_multipolygon(const std::string& directory, const rapidjson::Value& value, osmium::memory::Buffer& buffer) {
+        if (value.IsArray()) {
+            return parse_multipolygon_array(value, buffer);
+        } else if (value.IsObject()) {
+            return parse_multipolygon_object(directory, value, buffer);
+        }
+
+        throw config_error{"Multipolygon must be an object or array."};
+    }
+
+} // anonymous namespace
+
+void CommandExtract::set_directory(const std::string& directory) {
+    m_output_directory = directory;
+    if (m_output_directory.empty() || m_output_directory.back() != '/') {
+        m_output_directory += '/';
+    }
+}
+
+void CommandExtract::parse_config_file() {
+    std::ifstream config_file{m_config_file_name};
+    rapidjson::IStreamWrapper stream_wrapper{config_file};
+
+    rapidjson::Document doc;
+    if (doc.ParseStream<(rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag)>(stream_wrapper).HasParseError()) {
+        throw config_error{std::string{"JSON error at offset "} +
+                           std::to_string(doc.GetErrorOffset()) +
+                           ": " +
+                           rapidjson::GetParseError_En(doc.GetParseError())
+                          };
+    }
+
+    if (!doc.IsObject()) {
+        throw config_error{"Top-level value must be an object."};
+    }
+
+    std::string directory{get_value_as_string(doc, "directory")};
+    if (!directory.empty() && m_output_directory.empty()) {
+        set_directory(directory);
+    }
+
+    const auto json_extracts = doc.FindMember("extracts");
+    if (json_extracts == doc.MemberEnd()) {
+        throw config_error{"Missing 'extracts' member in top-level object."};
+    }
+
+    if (!json_extracts->value.IsArray()) {
+        throw config_error{"'extracts' member in top-level object must be array."};
+    }
+
+    int extract_num = 1;
+    for (const auto& e : json_extracts->value.GetArray()) {
+        std::string output;
+        try {
+            if (!e.IsObject()) {
+                throw config_error{"Members in 'extracts' array must be objects."};
+            }
+
+            output = get_value_as_string(e, "output");
+            if (output.empty()) {
+                throw config_error{"Missing 'output' field for extract."};
+            }
+
+            std::string output_format{get_value_as_string(e, "output_format")};
+            std::string description{get_value_as_string(e, "description")};
+
+            const auto json_bbox         = e.FindMember("bbox");
+            const auto json_polygon      = e.FindMember("polygon");
+            const auto json_multipolygon = e.FindMember("multipolygon");
+
+            const osmium::io::File output_file{m_output_directory + output, output_format};
+
+            if (json_bbox != e.MemberEnd()) {
+                m_extracts.emplace_back(new ExtractBBox{output_file, description, parse_bbox(json_bbox->value)});
+            } else if (json_polygon != e.MemberEnd()) {
+                m_extracts.emplace_back(new ExtractPolygon{output_file, description, m_buffer, parse_polygon(m_config_directory, json_polygon->value, m_buffer)});
+            } else if (json_multipolygon != e.MemberEnd()) {
+                m_extracts.emplace_back(new ExtractPolygon{output_file, description, m_buffer, parse_multipolygon(m_config_directory, json_multipolygon->value, m_buffer)});
+            } else {
+                throw config_error{"Missing geometry for extract. Need 'bbox', 'polygon', or 'multipolygon'."};
+            }
+        } catch (const config_error& e) {
+            std::string message{"In extract "};
+            message += std::to_string(extract_num);
+            message += ": ";
+            message += e.what();
+            throw config_error{message};
+        } catch (const poly_error&) {
+            std::cerr << "Error while reading poly file for extract " << extract_num << " (" << output << "):\n";
+            throw;
+        } catch (const geojson_error&) {
+            std::cerr << "Error while reading GeoJSON file for extract " << extract_num << " (" << output << "):\n";
+            throw;
+        } catch (const std::system_error&) {
+            std::cerr << "Error while reading OSM file for extract " << extract_num << " (" << output << "):\n";
+            throw;
+        } catch (const osmium::io_error&) {
+            std::cerr << "Error while reading OSM file for extract " << extract_num << " (" << output << "):\n";
+            throw;
+        }
+
+        ++extract_num;
+    }
+}
+
+std::unique_ptr<ExtractStrategy> CommandExtract::make_strategy(const std::string& name) {
+    if (name == "simple") {
+        if (m_with_history) {
+            throw argument_error{"The 'simple' strategy is not supported for history files."};
+        } else {
+            return std::unique_ptr<ExtractStrategy>(new strategy_simple::Strategy{m_extracts, m_options});
+        }
+    } else if (name == "complete_ways") {
+        if (m_with_history) {
+            return std::unique_ptr<ExtractStrategy>(new strategy_complete_ways_with_history::Strategy{m_extracts, m_options});
+        } else {
+            return std::unique_ptr<ExtractStrategy>(new strategy_complete_ways::Strategy{m_extracts, m_options});
+        }
+    } else if (name == "smart") {
+        if (m_with_history) {
+            throw argument_error{"The 'smart' strategy is not supported for history files."};
+        } else {
+            return std::unique_ptr<ExtractStrategy>(new strategy_smart::Strategy{m_extracts, m_options});
+        }
+    }
+
+    throw argument_error{std::string{"Unknown extract strategy: '"} + name + "'."};
+}
+
+bool CommandExtract::setup(const std::vector<std::string>& arguments) {
+    po::options_description opts_cmd{"COMMAND OPTIONS"};
+    opts_cmd.add_options()
+    ("bbox,b", po::value<std::string>(), "Bounding box")
+    ("config,c", po::value<std::string>(), "Config file")
+    ("directory,d", po::value<std::string>(), "Output directory (default: from config)")
+    ("option,S", po::value<std::vector<std::string>>(), "Set strategy option")
+    ("polygon,p", po::value<std::string>(), "Polygon file")
+    ("strategy,s", po::value<std::string>()->default_value("complete_ways"), "Use named extract strategy")
+    ("with-history", "Input file and output files are history files")
+    ;
+
+    po::options_description opts_common{add_common_options()};
+    po::options_description opts_input{add_single_input_options()};
+    po::options_description opts_output{add_output_options()};
+
+    po::options_description hidden;
+    hidden.add_options()
+    ("input-filename", po::value<std::string>(), "OSM input file")
+    ;
+
+    po::options_description desc;
+    desc.add(opts_cmd).add(opts_common).add(opts_input).add(opts_output);
+
+    po::options_description parsed_options;
+    parsed_options.add(desc).add(hidden);
+
+    po::positional_options_description positional;
+    positional.add("input-filename", 1);
+
+    po::variables_map vm;
+    po::store(po::command_line_parser(arguments).options(parsed_options).positional(positional).run(), vm);
+    po::notify(vm);
+
+    setup_common(vm, desc);
+    setup_progress(vm);
+    setup_input_file(vm);
+    init_output_file(vm);
+
+    if (vm.count("config") + vm.count("bbox") + vm.count("polygon") > 1) {
+        throw argument_error{"Can only use one of --config/-c, --bbox/-b, or --polygon/-p."};
+    }
+
+    if (vm.count("config")) {
+        if (vm.count("directory")) {
+            set_directory(vm["directory"].as<std::string>());
+        }
+        if (vm.count("output")) {
+            warning("Ignoring --output/-o option\n");
+        }
+        if (vm.count("output-format")) {
+            warning("Ignoring --output-format/-f option\n");
+        }
+        m_config_file_name = vm["config"].as<std::string>();
+        auto slash = m_config_file_name.find_last_of('/');
+        if (slash != std::string::npos) {
+            m_config_directory = m_config_file_name;
+            m_config_directory.resize(slash + 1);
+        }
+
+        try {
+            parse_config_file();
+        } catch (const config_error&) {
+            std::cerr << "Error while reading config file '" << m_config_file_name << "':\n";
+            throw;
+        }
+    }
+
+    if (vm.count("bbox")) {
+        if (vm.count("directory")) {
+            warning("Ignoring --directory/-d option\n");
+        }
+        check_output_file();
+        m_extracts.emplace_back(new ExtractBBox{m_output_file, "", parse_bbox(vm["bbox"].as<std::string>())});
+    }
+
+    if (vm.count("polygon")) {
+        if (vm.count("directory")) {
+            warning("Ignoring --directory/-d option\n");
+        }
+        check_output_file();
+        m_extracts.emplace_back(new ExtractPolygon{m_output_file, "", m_buffer, parse_multipolygon_object("./", vm["polygon"].as<std::string>(), "", m_buffer)});
+    }
+
+    if (vm.count("option")) {
+        for (const auto& option : vm["option"].as<std::vector<std::string>>()) {
+            m_options.set(option);
+        }
+    }
+
+    if (vm.count("with-history")) {
+        m_with_history = true;
+    }
+
+    if (vm.count("strategy")) {
+        m_strategy = make_strategy(vm["strategy"].as<std::string>());
+    }
+
+    return true;
+}
+
+void CommandExtract::show_arguments() {
+    show_single_input_arguments(m_vout);
+    show_output_arguments(m_vout);
+
+    m_vout << "  strategy options:\n";
+    m_vout << "    strategy: " << m_strategy->name() << '\n';
+    m_vout << "    with history: " << yes_no(m_with_history);
+    m_strategy->show_arguments(m_vout);
+
+    m_vout << "  other options:\n";
+    m_vout << "    config file: " << m_config_file_name << '\n';
+    m_vout << "    output directory: " << m_output_directory << '\n';
+
+    m_vout << '\n';
+    m_vout << "Extracts:\n";
+
+    int n = 1;
+    for (const auto& e : m_extracts) {
+        const char old_fill = std::cerr.fill();
+        m_vout << "[" << std::setw(2) << std::setfill('0') << n
+               << "] Output:      " << e->output() << '\n';
+        std::cerr.fill(old_fill);
+        m_vout << "     Format:      " << e->output_format()    << '\n';
+        m_vout << "     Description: " << e->description()      << '\n';
+        m_vout << "     Envelope:    " << e->envelope_as_text() << '\n';
+        m_vout << "     Type:        " << e->geometry_type()    << '\n';
+        m_vout << "     Geometry:    " << e->geometry_as_text() << '\n';
+        ++n;
+    }
+
+    m_vout << '\n';
+}
+
+bool CommandExtract::run() {
+    osmium::io::Header header;
+    setup_header(header);
+
+    for (const auto& extract : m_extracts) {
+        extract->open_file(header, m_output_overwrite, m_fsync);
+    }
+
+    m_strategy->run(m_vout, display_progress(), m_input_file);
+
+    for (const auto& extract : m_extracts) {
+        extract->close_file();
+    }
+
+    show_memory_used();
+
+    m_vout << "Done.\n";
+
+    return true;
+}
+
+namespace {
+
+    const bool register_extract_command = CommandFactory::add("extract", "Create geographic extract", []() {
+        return new CommandExtract();
+    });
+
+}
+
diff --git a/src/command_extract.hpp b/src/command_extract.hpp
new file mode 100644
index 0000000..7665b97
--- /dev/null
+++ b/src/command_extract.hpp
@@ -0,0 +1,79 @@
+#ifndef COMMAND_EXTRACT_HPP
+#define COMMAND_EXTRACT_HPP
+
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <osmium/memory/buffer.hpp>
+#include <osmium/util/options.hpp>
+
+#include "cmd.hpp" // IWYU pragma: export
+#include "extract/extract.hpp"
+#include "extract/strategy.hpp"
+
+class CommandExtract : public Command, public with_single_osm_input, public with_osm_output {
+
+    static const size_t initial_buffer_size = 10 * 1024;
+
+    std::string m_config_file_name;
+    std::string m_config_directory;
+    std::string m_output_directory;
+    osmium::util::Options m_options;
+    std::unique_ptr<ExtractStrategy> m_strategy;
+    std::vector<std::unique_ptr<Extract>> m_extracts;
+    osmium::memory::Buffer m_buffer{initial_buffer_size, osmium::memory::Buffer::auto_grow::yes};
+    bool m_with_history = false;
+
+    void parse_config_file();
+
+    void set_directory(const std::string& directory);
+
+    std::unique_ptr<ExtractStrategy> make_strategy(const std::string& name);
+
+public:
+
+    CommandExtract() = default;
+
+    bool setup(const std::vector<std::string>& arguments) override final;
+
+    void show_arguments() override final;
+
+    bool run() override final;
+
+    const char* name() const noexcept override final {
+        return "extract";
+    }
+
+    const char* synopsis() const noexcept override final {
+        return "osmium extract --config CONFIG-FILE [OPTIONS] OSM-FILE\n"
+               "       osmium extract --bbox LEFT,BOTTOM,RIGHT,TOP [OPTIONS] OSM-FILE\n"
+               "       osmium extract --polygon POLYGON-FILE [OPTIONS] OSM-FILE";
+    }
+
+}; // class CommandExtract
+
+
+#endif // COMMAND_EXTRACT_HPP
diff --git a/src/command_fileinfo.cpp b/src/command_fileinfo.cpp
index d61615f..3c6aae4 100644
--- a/src/command_fileinfo.cpp
+++ b/src/command_fileinfo.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -61,6 +61,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include "command_fileinfo.hpp"
 #include "exception.hpp"
+#include "util.hpp"
 
 /*************************************************************************/
 
diff --git a/src/command_fileinfo.hpp b/src/command_fileinfo.hpp
index f6176da..52d4677 100644
--- a/src/command_fileinfo.hpp
+++ b/src/command_fileinfo.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_getid.cpp b/src/command_getid.cpp
index 2844e6b..68671fd 100644
--- a/src/command_getid.cpp
+++ b/src/command_getid.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -43,6 +43,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include "command_getid.hpp"
 #include "exception.hpp"
+#include "util.hpp"
 
 void CommandGetId::parse_and_add_id(const std::string& s) {
     auto p = osmium::string_to_object_id(s.c_str(), osmium::osm_entity_bits::nwr, m_default_item_type);
diff --git a/src/command_getid.hpp b/src/command_getid.hpp
index 7cfda20..44943c0 100644
--- a/src/command_getid.hpp
+++ b/src/command_getid.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_help.cpp b/src/command_help.cpp
index 9503f6e..08255bb 100644
--- a/src/command_help.cpp
+++ b/src/command_help.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -36,7 +36,7 @@ bool CommandHelp::setup(const std::vector<std::string>& arguments) {
     return true;
 }
 
-void show_help(const std::string& topic, const std::string& info) {
+static void show_help(const std::string& topic, const std::string& info) {
 #ifndef _MSC_VER
     // show man page on non-Windows systems
     std::string manpage{"osmium-"};
@@ -46,7 +46,7 @@ void show_help(const std::string& topic, const std::string& info) {
 #endif
     // show info string and link on Windows systems
     std::cout << info << "\n";
-    std::cout << "You'll find more documentation at http://osmcode.org/osmium/\n";
+    std::cout << "You'll find more documentation at http://osmcode.org/osmium-tool/\n";
 }
 
 bool CommandHelp::run() {
diff --git a/src/command_help.hpp b/src/command_help.hpp
index a9c460f..6afc0ab 100644
--- a/src/command_help.hpp
+++ b/src/command_help.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_merge.cpp b/src/command_merge.cpp
index 31d384e..b516e42 100644
--- a/src/command_merge.cpp
+++ b/src/command_merge.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -81,68 +81,72 @@ void CommandMerge::show_arguments() {
     show_output_arguments(m_vout);
 }
 
-class DataSource {
+namespace {
 
-    using it_type = osmium::io::InputIterator<osmium::io::Reader, osmium::OSMObject>;
+    class DataSource {
 
-    std::unique_ptr<osmium::io::Reader> reader;
-    it_type iterator;
+        using it_type = osmium::io::InputIterator<osmium::io::Reader, osmium::OSMObject>;
 
-public:
+        std::unique_ptr<osmium::io::Reader> reader;
+        it_type iterator;
 
-    explicit DataSource(const osmium::io::File& file) :
-        reader(new osmium::io::Reader{file}),
-        iterator(*reader) {
-    }
+    public:
 
-    bool empty() const noexcept {
-        return iterator == it_type{};
-    }
+        explicit DataSource(const osmium::io::File& file) :
+            reader(new osmium::io::Reader{file}),
+            iterator(*reader) {
+        }
 
-    bool next() noexcept {
-        ++iterator;
-        return iterator != it_type{};
-    }
+        bool empty() const noexcept {
+            return iterator == it_type{};
+        }
 
-    const osmium::OSMObject* get() noexcept {
-        return &*iterator;
-    }
+        bool next() noexcept {
+            ++iterator;
+            return iterator != it_type{};
+        }
 
-}; // DataSource
+        const osmium::OSMObject* get() noexcept {
+            return &*iterator;
+        }
 
-class QueueElement {
+    }; // DataSource
 
-    const osmium::OSMObject* m_object;
-    int m_data_source_index;
+    class QueueElement {
 
-public:
+        const osmium::OSMObject* m_object;
+        int m_data_source_index;
 
-    QueueElement(const osmium::OSMObject* object, int data_source_index) noexcept :
-        m_object(object),
-        m_data_source_index(data_source_index) {
-    }
+    public:
 
-    const osmium::OSMObject& object() const noexcept {
-        return *m_object;
-    }
+        QueueElement(const osmium::OSMObject* object, int data_source_index) noexcept :
+            m_object(object),
+            m_data_source_index(data_source_index) {
+        }
 
-    int data_source_index() const noexcept {
-        return m_data_source_index;
-    }
+        const osmium::OSMObject& object() const noexcept {
+            return *m_object;
+        }
 
-}; // QueueElement
+        int data_source_index() const noexcept {
+            return m_data_source_index;
+        }
 
-bool operator<(const QueueElement& lhs, const QueueElement& rhs) noexcept {
-    return lhs.object() > rhs.object();
-}
+    }; // QueueElement
 
-bool operator==(const QueueElement& lhs, const QueueElement& rhs) noexcept {
-    return lhs.object() == rhs.object();
-}
+    bool operator<(const QueueElement& lhs, const QueueElement& rhs) noexcept {
+        return lhs.object() > rhs.object();
+    }
 
-bool operator!=(const QueueElement& lhs, const QueueElement& rhs) noexcept {
-    return ! (lhs == rhs);
-}
+    bool operator==(const QueueElement& lhs, const QueueElement& rhs) noexcept {
+        return lhs.object() == rhs.object();
+    }
+
+    bool operator!=(const QueueElement& lhs, const QueueElement& rhs) noexcept {
+        return ! (lhs == rhs);
+    }
+
+} // anonymous namespace
 
 bool CommandMerge::run() {
     m_vout << "Opening output file...\n";
diff --git a/src/command_merge.hpp b/src/command_merge.hpp
index eb58ed6..28f0b9f 100644
--- a/src/command_merge.hpp
+++ b/src/command_merge.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_merge_changes.cpp b/src/command_merge_changes.cpp
index 56b477f..65c10b0 100644
--- a/src/command_merge_changes.cpp
+++ b/src/command_merge_changes.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_merge_changes.hpp b/src/command_merge_changes.hpp
index 699204b..6bd9fdb 100644
--- a/src/command_merge_changes.hpp
+++ b/src/command_merge_changes.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_renumber.cpp b/src/command_renumber.cpp
index 048676f..85797d1 100644
--- a/src/command_renumber.cpp
+++ b/src/command_renumber.cpp
@@ -1,8 +1,8 @@
 /*
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_renumber.hpp b/src/command_renumber.hpp
index 5b97a48..b2ab5a1 100644
--- a/src/command_renumber.hpp
+++ b/src/command_renumber.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_show.cpp b/src/command_show.cpp
index 4ec4ac0..9034ecf 100644
--- a/src/command_show.cpp
+++ b/src/command_show.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_show.hpp b/src/command_show.hpp
index b2d3d55..7061b71 100644
--- a/src/command_show.hpp
+++ b/src/command_show.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_sort.cpp b/src/command_sort.cpp
index 171d52e..6141504 100644
--- a/src/command_sort.cpp
+++ b/src/command_sort.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_sort.hpp b/src/command_sort.hpp
index 0d922f8..dc604e9 100644
--- a/src/command_sort.hpp
+++ b/src/command_sort.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/command_time_filter.cpp b/src/command_time_filter.cpp
index 90b789c..19b44ea 100644
--- a/src/command_time_filter.cpp
+++ b/src/command_time_filter.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -40,6 +40,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include "command_time_filter.hpp"
 #include "exception.hpp"
+#include "util.hpp"
 
 bool CommandTimeFilter::setup(const std::vector<std::string>& arguments) {
     po::options_description opts_common{add_common_options()};
diff --git a/src/command_time_filter.hpp b/src/command_time_filter.hpp
index 963ac19..82599d6 100644
--- a/src/command_time_filter.hpp
+++ b/src/command_time_filter.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/exception.hpp b/src/exception.hpp
index ebe8970..93131af 100644
--- a/src/exception.hpp
+++ b/src/exception.hpp
@@ -4,9 +4,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
diff --git a/src/exception.hpp b/src/extract/error.hpp
similarity index 53%
copy from src/exception.hpp
copy to src/extract/error.hpp
index ebe8970..6a3a0c0 100644
--- a/src/exception.hpp
+++ b/src/extract/error.hpp
@@ -1,12 +1,12 @@
-#ifndef EXCEPTION_HPP
-#define EXCEPTION_HPP
+#ifndef EXTRACT_ERROR_HPP
+#define EXTRACT_ERROR_HPP
 
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -24,21 +24,32 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
 #include <stdexcept>
+#include <string>
 
 /**
- *  Thrown when there is a problem with the command line arguments.
+ *  Thrown when there is a problem with parsing the JSON config file.
  */
-struct argument_error : std::runtime_error {
+struct config_error : public std::runtime_error {
 
-    explicit argument_error(const char* message) :
+    explicit config_error(const char* message) :
         std::runtime_error(message) {
     }
 
-    explicit argument_error(const std::string& message) :
+    explicit config_error(const std::string& message) :
         std::runtime_error(message) {
     }
 
-};
+}; // struct config_error
 
+/**
+ *  Thrown when there is a problem with parsing a GeoJSON file.
+ */
+struct geojson_error : public std::runtime_error {
+
+    explicit geojson_error(const std::string& message) :
+        std::runtime_error(message) {
+    }
+
+}; // struct geojson_error
 
-#endif // EXCEPTION_HPP
+#endif // EXTRACT_ERROR_HPP
diff --git a/src/extract/extract.cpp b/src/extract/extract.cpp
new file mode 100644
index 0000000..d842512
--- /dev/null
+++ b/src/extract/extract.cpp
@@ -0,0 +1,58 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <sstream>
+#include <string>
+
+#include <osmium/io/writer_options.hpp>
+
+#include "extract.hpp"
+
+namespace osmium {
+    namespace io {
+        class Header;
+    }
+    namespace memory {
+        class Item;
+    }
+}
+
+void Extract::open_file(const osmium::io::Header& header, osmium::io::overwrite output_overwrite, osmium::io::fsync sync) {
+    m_writer.reset(new osmium::io::Writer{m_output_file, header, output_overwrite, sync});
+}
+
+void Extract::close_file() {
+    if (m_writer) {
+        m_writer->close();
+    }
+}
+
+void Extract::write(const osmium::memory::Item& item) {
+    (*m_writer)(item);
+}
+
+std::string Extract::envelope_as_text() const {
+    std::stringstream ss;
+    ss << m_envelope;
+    return ss.str();
+}
+
diff --git a/src/extract/extract.hpp b/src/extract/extract.hpp
new file mode 100644
index 0000000..6a5bc10
--- /dev/null
+++ b/src/extract/extract.hpp
@@ -0,0 +1,101 @@
+#ifndef EXTRACT_EXTRACT_HPP
+#define EXTRACT_EXTRACT_HPP
+
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <memory>
+#include <string>
+
+#include <osmium/osm/box.hpp>
+#include <osmium/io/file.hpp>
+#include <osmium/io/writer_options.hpp>
+#include <osmium/io/writer.hpp>
+
+namespace osmium {
+    class Box;
+    class Location;
+
+    namespace io {
+        class Header;
+    }
+
+    namespace memory {
+        class Item;
+    }
+}
+
+class Extract {
+
+    osmium::io::File m_output_file;
+    std::string m_description;
+    osmium::Box m_envelope;
+    std::unique_ptr<osmium::io::Writer> m_writer;
+
+public:
+
+    Extract(const osmium::io::File& output_file, const std::string& description, const osmium::Box& envelope) :
+        m_output_file(output_file),
+        m_description(description),
+        m_envelope(envelope),
+        m_writer(nullptr) {
+    }
+
+    virtual ~Extract() = default;
+
+    const std::string& output() const noexcept {
+        return m_output_file.filename();
+    }
+
+    const char* output_format() const noexcept {
+        return osmium::io::as_string(m_output_file.format());
+    }
+
+    const std::string& description() const noexcept {
+        return m_description;
+    }
+
+    const osmium::Box& envelope() const noexcept {
+        return m_envelope;
+    }
+
+    osmium::io::Writer& writer() {
+        return *m_writer;
+    }
+
+    void open_file(const osmium::io::Header& header, osmium::io::overwrite output_overwrite, osmium::io::fsync sync);
+
+    void close_file();
+
+    void write(const osmium::memory::Item& item);
+
+    std::string envelope_as_text() const;
+
+    virtual bool contains(const osmium::Location& location) const noexcept = 0;
+
+    virtual const char* geometry_type() const noexcept = 0;
+
+    virtual std::string geometry_as_text() const = 0;
+
+}; // class Extract
+
+#endif // EXTRACT_EXTRACT_HPP
diff --git a/src/command_help.hpp b/src/extract/extract_bbox.cpp
similarity index 51%
copy from src/command_help.hpp
copy to src/extract/extract_bbox.cpp
index a9c460f..69e09bf 100644
--- a/src/command_help.hpp
+++ b/src/extract/extract_bbox.cpp
@@ -1,12 +1,9 @@
-#ifndef COMMAND_HELP_HPP
-#define COMMAND_HELP_HPP
-
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -24,30 +21,27 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
 
 #include <string>
-#include <vector>
-
-#include "cmd.hpp" // IWYU pragma: export
-
-class CommandHelp : public Command {
-
-    std::string m_topic;
-
-public:
-
-    bool setup(const std::vector<std::string>& arguments) override final;
 
-    bool run() override final;
+#include "extract_bbox.hpp"
 
-    const char* name() const noexcept override final {
-        return "help";
-    }
+namespace osmium {
+    class Location;
+}
 
-    const char* synopsis() const noexcept override final {
-        return "osmium COMMAND [ARG...]\n"
-               "       osmium --version";
-    }
+bool ExtractBBox::contains(const osmium::Location& location) const noexcept {
+    return location.valid() && envelope().contains(location);
+}
 
-}; // class CommandHelp
+const char* ExtractBBox::geometry_type() const noexcept {
+    return "bbox";
+}
 
+std::string ExtractBBox::geometry_as_text() const {
+    std::string s{"BOX("};
+    envelope().bottom_left().as_string(std::back_inserter(s), ' ');
+    s += ',';
+    envelope().top_right().as_string(std::back_inserter(s), ' ');
+    s += ')';
+    return s;
+}
 
-#endif // COMMAND_HELP_HPP
diff --git a/src/exception.hpp b/src/extract/extract_bbox.hpp
similarity index 51%
copy from src/exception.hpp
copy to src/extract/extract_bbox.hpp
index ebe8970..30eee7d 100644
--- a/src/exception.hpp
+++ b/src/extract/extract_bbox.hpp
@@ -1,12 +1,12 @@
-#ifndef EXCEPTION_HPP
-#define EXCEPTION_HPP
+#ifndef EXTRACT_EXTRACT_BBOX_HPP
+#define EXTRACT_EXTRACT_BBOX_HPP
 
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -23,22 +23,21 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 */
 
-#include <stdexcept>
+#include "extract.hpp"
 
-/**
- *  Thrown when there is a problem with the command line arguments.
- */
-struct argument_error : std::runtime_error {
+class ExtractBBox : public Extract {
 
-    explicit argument_error(const char* message) :
-        std::runtime_error(message) {
-    }
+public:
 
-    explicit argument_error(const std::string& message) :
-        std::runtime_error(message) {
+    ExtractBBox(const osmium::io::File& output_file, const std::string& description, const osmium::Box& box) :
+        Extract(output_file, description, box) {
     }
 
-};
+    bool contains(const osmium::Location& location) const noexcept override final;
+
+    const char* geometry_type() const noexcept override final;
 
+    std::string geometry_as_text() const override final;
 
-#endif // EXCEPTION_HPP
+}; // class ExtractBBox
+#endif // EXTRACT_EXTRACT_BBOX_HPP
diff --git a/src/extract/extract_polygon.cpp b/src/extract/extract_polygon.cpp
new file mode 100644
index 0000000..43d178b
--- /dev/null
+++ b/src/extract/extract_polygon.cpp
@@ -0,0 +1,161 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <cstdint>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <osmium/geom/wkt.hpp>
+#include <osmium/osm/location.hpp>
+#include <osmium/osm/segment.hpp>
+
+#include "extract_polygon.hpp"
+
+static void add_ring(std::vector<osmium::Segment>& segments, const osmium::NodeRefList& ring) {
+    auto it = ring.begin();
+    const auto end = ring.end();
+
+    assert(it != end);
+    auto last_it = it++;
+    while (it != end) {
+        segments.emplace_back(last_it->location(), it->location());
+        last_it = it++;
+    }
+}
+
+const osmium::Area& ExtractPolygon::area() const noexcept {
+    return m_buffer.get<osmium::Area>(m_offset);
+}
+
+ExtractPolygon::ExtractPolygon(const osmium::io::File& output_file, const std::string& description, const osmium::memory::Buffer& buffer, std::size_t offset) :
+    Extract(output_file, description, buffer.get<osmium::Area>(offset).envelope()),
+    m_buffer(buffer),
+    m_offset(offset),
+    m_bands(),
+    m_dy(0) {
+
+    // get segments from all rings
+    std::vector<osmium::Segment> segments;
+    for (const auto& outer_ring : area().outer_rings()) {
+        add_ring(segments, outer_ring);
+
+        for (const auto& inner_ring : area().inner_rings(outer_ring)) {
+            add_ring(segments, inner_ring);
+        }
+    }
+
+    // split y range into equal-sized bands
+    constexpr const int32_t segments_per_band = 10;
+    constexpr const int32_t max_bands = 10000;
+    int32_t num_bands = static_cast<int32_t>(segments.size()) / segments_per_band;
+    if (num_bands < 1) {
+        num_bands = 1;
+    } else if (num_bands > max_bands) {
+        num_bands = max_bands;
+    }
+
+    m_bands.resize(num_bands);
+
+    m_dy = (y_max() - y_min()) / num_bands;
+
+    // put segments into the bands they overlap
+    for (const auto& segment : segments) {
+        const std::pair<int32_t, int32_t> mm = std::minmax(segment.first().y(), segment.second().y());
+        const uint32_t band_min = (mm.first  - y_min()) / m_dy;
+        const uint32_t band_max = std::min(num_bands, ((mm.second - y_min()) / m_dy) + 1);
+
+        for (auto band = band_min; band < band_max; ++band) {
+            m_bands[band].push_back(segment);
+        }
+    }
+}
+
+/*
+
+  Simple point-in-polygon algorithm adapted from
+
+  https://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html
+
+    int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy)
+    {
+        int i, j, c = 0;
+        for (i = 0, j = nvert-1; i < nvert; j = i++) {
+            if ( ((verty[i]>testy) != (verty[j]>testy)) &&
+                (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
+            c = !c;
+        }
+        return c;
+    }
+
+  In our implementation we split the y-range into equal-sized subranges and
+  only have to test all segments in the subrange that contains the y coordinate
+  of the node.
+
+*/
+
+bool ExtractPolygon::contains(const osmium::Location& location) const noexcept {
+    if (!location.valid() || ! envelope().contains(location)) {
+        return false;
+    }
+
+    size_t band = (location.y() - y_min()) / m_dy;
+    if (band >= m_bands.size()) {
+        band = m_bands.size() - 1;
+    }
+
+    bool inside = false;
+
+    for (const auto& segment : m_bands[band]) {
+        if (segment.first() == location || segment.second() == location) {
+            return true;
+        }
+        if ((segment.second().y() > location.y()) != (segment.first().y() > location.y())) {
+            const int64_t ax = int64_t(segment.first().x()) - int64_t(segment.second().x());
+            const int64_t ay = int64_t(segment.first().y()) - int64_t(segment.second().y());
+            const int64_t tx = int64_t(location.x())        - int64_t(segment.second().x());
+            const int64_t ty = int64_t(location.y())        - int64_t(segment.second().y());
+
+            const bool comp = tx * ay < ax * ty;
+
+            if ((ay > 0) == comp) {
+                inside = !inside;
+            }
+        }
+
+    }
+
+    return inside;
+}
+
+const char* ExtractPolygon::geometry_type() const noexcept {
+    return "polygon";
+}
+
+std::string ExtractPolygon::geometry_as_text() const {
+    osmium::geom::WKTFactory<> factory;
+    return factory.create_multipolygon(area());
+}
+
diff --git a/src/extract/extract_polygon.hpp b/src/extract/extract_polygon.hpp
new file mode 100644
index 0000000..bda6855
--- /dev/null
+++ b/src/extract/extract_polygon.hpp
@@ -0,0 +1,65 @@
+#ifndef EXTRACT_EXTRACT_POLYGON_HPP
+#define EXTRACT_EXTRACT_POLYGON_HPP
+
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <vector>
+
+#include "extract.hpp"
+
+namespace osmium {
+    class Area;
+    class Segment;
+}
+
+class ExtractPolygon : public Extract {
+
+    const osmium::memory::Buffer& m_buffer;
+    std::size_t m_offset;
+
+    std::vector<std::vector<osmium::Segment>> m_bands;
+    int32_t m_dy;
+
+    const osmium::Area& area() const noexcept;
+
+    int32_t y_max() const noexcept {
+        return envelope().top_right().y();
+    }
+
+    int32_t y_min() const noexcept {
+        return envelope().bottom_left().y();
+    }
+
+public:
+
+    ExtractPolygon(const osmium::io::File& output_file, const std::string& description, const osmium::memory::Buffer& buffer, std::size_t offset);
+
+    bool contains(const osmium::Location& location) const noexcept override final;
+
+    const char* geometry_type() const noexcept override final;
+
+    std::string geometry_as_text() const override final;
+
+}; // class ExtractPolygon
+
+#endif // EXTRACT_EXTRACT_POLYGON_HPP
diff --git a/src/extract/geojson_file_parser.cpp b/src/extract/geojson_file_parser.cpp
new file mode 100644
index 0000000..7fed255
--- /dev/null
+++ b/src/extract/geojson_file_parser.cpp
@@ -0,0 +1,191 @@
+
+#include <cassert>
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <rapidjson/document.h>
+#include <rapidjson/error/en.h>
+#include <rapidjson/istreamwrapper.h>
+
+#include <osmium/builder/osm_object_builder.hpp>
+#include <osmium/geom/coordinates.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/osm/location.hpp>
+
+#include "error.hpp"
+#include "geojson_file_parser.hpp"
+
+std::string get_value_as_string(const rapidjson::Value& object, const char* key) {
+    assert(object.IsObject());
+
+    const auto it = object.FindMember(key);
+    if (it == object.MemberEnd()) {
+        return "";
+    }
+
+    if (it->value.IsString()) {
+        return it->value.GetString();
+    } else {
+        throw config_error{std::string{"Value for name '"} + key + "' must be a string."};
+    }
+}
+
+// parse coordinate pair from JSON array
+osmium::geom::Coordinates parse_coordinate(const rapidjson::Value& value) {
+    if (!value.IsArray()) {
+        throw config_error{"Coordinates must be an array."};
+    }
+
+    const auto array = value.GetArray();
+    if (array.Size() != 2) {
+        throw config_error{"Coordinates array must have exactly two elements."};
+    }
+
+    if (array[0].IsNumber() && array[1].IsNumber()) {
+        return osmium::geom::Coordinates{array[0].GetDouble(), array[1].GetDouble()};
+    }
+
+    throw config_error{"Coordinates array must contain numbers."};
+}
+
+std::vector<osmium::geom::Coordinates> parse_ring(const rapidjson::Value& value) {
+    if (!value.IsArray()) {
+        throw config_error{"Ring must be an array."};
+    }
+
+    const auto array = value.GetArray();
+    if (array.Size() < 3) {
+        throw config_error{"Ring must contain at least three coordinate pairs."};
+    }
+
+    std::vector<osmium::geom::Coordinates> coordinates;
+
+    for (const rapidjson::Value& item : array) {
+        coordinates.push_back(parse_coordinate(item));
+    }
+
+    return coordinates;
+}
+
+void parse_rings(const rapidjson::Value& value, osmium::builder::AreaBuilder& builder) {
+    assert(value.IsArray());
+    const auto array = value.GetArray();
+    if (array.Size() < 1) {
+        throw config_error{"Polygon must contain at least one ring."};
+    }
+
+    {
+        const auto outer_ring = parse_ring(array[0]);
+        osmium::builder::OuterRingBuilder ring_builder{builder};
+        for (const auto& c : outer_ring) {
+            ring_builder.add_node_ref(0, osmium::Location{c.x, c.y});
+        }
+    }
+
+    for (unsigned int i = 1; i < array.Size(); ++i) {
+        const auto inner_ring = parse_ring(array[i]);
+        osmium::builder::InnerRingBuilder ring_builder{builder};
+        for (const auto& c : inner_ring) {
+            ring_builder.add_node_ref(0, osmium::Location{c.x, c.y});
+        }
+    }
+}
+
+std::size_t parse_polygon_array(const rapidjson::Value& value, osmium::memory::Buffer& buffer) {
+    {
+        osmium::builder::AreaBuilder builder{buffer};
+        parse_rings(value, builder);
+    }
+
+    return buffer.commit();
+}
+
+std::size_t parse_multipolygon_array(const rapidjson::Value& value, osmium::memory::Buffer& buffer) {
+    assert(value.IsArray());
+    const auto array = value.GetArray();
+    if (array.Size() < 1) {
+        throw config_error{"Multipolygon must contain at least one polygon array."};
+    }
+
+    for (const auto& polygon : array) {
+        if (!polygon.IsArray()) {
+            throw config_error{"Polygon must be an array."};
+        }
+        osmium::builder::AreaBuilder builder{buffer};
+        parse_rings(polygon, builder);
+    }
+
+    return buffer.commit();
+}
+
+void GeoJSONFileParser::error(const std::string& message) {
+    throw geojson_error{std::string{"In file '"} + m_file_name + "':\n" + message};
+}
+
+GeoJSONFileParser::GeoJSONFileParser(osmium::memory::Buffer& buffer, const std::string& file_name) :
+    m_buffer(buffer),
+    m_file_name(file_name),
+    m_file(m_file_name) {
+    if (!m_file.is_open()) {
+        throw config_error{std::string{"Could not open file '"} + m_file_name + "'."};
+    }
+}
+
+std::size_t GeoJSONFileParser::operator()() {
+    rapidjson::IStreamWrapper stream_wrapper{m_file};
+
+    rapidjson::Document doc;
+    if (doc.ParseStream<rapidjson::kParseCommentsFlag | rapidjson::kParseTrailingCommasFlag>(stream_wrapper).HasParseError()) {
+        error(std::string{"JSON error at offset "} +
+              std::to_string(doc.GetErrorOffset()) +
+              " : " +
+              rapidjson::GetParseError_En(doc.GetParseError()));
+    }
+
+    if (!doc.IsObject()) {
+        error("Top-level value must be an object.");
+    }
+
+    const std::string type{get_value_as_string(doc, "type")};
+    if (type.empty()) {
+        error("Expected 'type' name with the value 'Feature'.");
+    }
+
+    if (type != "Feature") {
+        error("Expected 'type' value to be 'Feature'.");
+    }
+
+    const auto json_geometry = doc.FindMember("geometry");
+    if (json_geometry == doc.MemberEnd()) {
+        error("Missing 'geometry' name.");
+    }
+
+    if (!json_geometry->value.IsObject()) {
+        error("Expected 'geometry' value to be an object.");
+    }
+
+    std::string geometry_type{get_value_as_string(json_geometry->value, "type")};
+    if (geometry_type.empty()) {
+        error("Missing 'geometry.type'.");
+    }
+    if (geometry_type != "Polygon" && geometry_type != "Multipolygon") {
+        error("Expected 'geometry.type' value to be 'Polygon' or 'Multipolygon'.");
+    }
+
+    const auto json_coordinates = json_geometry->value.FindMember("coordinates");
+    if (json_coordinates == json_geometry->value.MemberEnd()) {
+        error("Missing 'coordinates' name in 'geometry' object.");
+    }
+
+    if (!json_coordinates->value.IsArray()) {
+        error("Expected 'geometry.coordinates' value to be an array.");
+    }
+
+    if (geometry_type == "Polygon") {
+        return parse_polygon_array(json_coordinates->value, m_buffer);
+    } else {
+        return parse_multipolygon_array(json_coordinates->value, m_buffer);
+    }
+}
+
diff --git a/src/extract/geojson_file_parser.hpp b/src/extract/geojson_file_parser.hpp
new file mode 100644
index 0000000..0a7c5e7
--- /dev/null
+++ b/src/extract/geojson_file_parser.hpp
@@ -0,0 +1,58 @@
+#ifndef EXTRACT_GEOJSON_FILE_PARSER_HPP
+#define EXTRACT_GEOJSON_FILE_PARSER_HPP
+
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <fstream>
+#include <string>
+
+#include <rapidjson/document.h>
+
+namespace osmium { namespace memory {
+    class Buffer;
+}}
+
+extern std::string get_value_as_string(const rapidjson::Value& object, const char* key);
+extern std::size_t parse_polygon_array(const rapidjson::Value& value, osmium::memory::Buffer& buffer);
+extern std::size_t parse_multipolygon_array(const rapidjson::Value& value, osmium::memory::Buffer& buffer);
+
+/**
+ * Gets areas from OSM files.
+ */
+class GeoJSONFileParser {
+
+    osmium::memory::Buffer& m_buffer;
+    std::string m_file_name;
+    std::ifstream m_file;
+
+    void error(const std::string& message);
+
+public:
+
+    GeoJSONFileParser(osmium::memory::Buffer& buffer, const std::string& file_name);
+
+    std::size_t operator()();
+
+}; // class GeoJSONFileParser
+
+#endif // EXTRACT_GEOJSON_FILE_PARSER_HPP
diff --git a/src/extract/osm_file_parser.cpp b/src/extract/osm_file_parser.cpp
new file mode 100644
index 0000000..c0d7c16
--- /dev/null
+++ b/src/extract/osm_file_parser.cpp
@@ -0,0 +1,85 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <cstddef>
+#include <string>
+
+#include <osmium/area/assembler.hpp>
+#include <osmium/area/multipolygon_collector.hpp>
+#include <osmium/builder/osm_object_builder.hpp>
+#include <osmium/handler/node_locations_for_ways.hpp>
+#include <osmium/index/map/sparse_mem_array.hpp>
+#include <osmium/io/any_input.hpp>
+#include <osmium/io/file.hpp>
+#include <osmium/memory/buffer.hpp>
+
+#include "osm_file_parser.hpp"
+
+using index_type = osmium::index::map::SparseMemArray<osmium::unsigned_object_id_type, osmium::Location>;
+using location_handler_type = osmium::handler::NodeLocationsForWays<index_type>;
+
+OSMFileParser::OSMFileParser(osmium::memory::Buffer& buffer, const std::string& file_name) :
+    m_buffer(buffer),
+    m_file_name(file_name) {
+}
+
+std::size_t OSMFileParser::operator()() {
+    osmium::io::File input_file{m_file_name};
+
+    osmium::area::Assembler::config_type assembler_config;
+    osmium::area::MultipolygonCollector<osmium::area::Assembler> collector{assembler_config};
+
+    {
+        osmium::io::Reader reader{input_file, osmium::osm_entity_bits::relation};
+        collector.read_relations(reader);
+        reader.close();
+    }
+
+    bool has_ring = false;
+    {
+        index_type index;
+        location_handler_type location_handler{index};
+        osmium::builder::AreaBuilder builder{m_buffer};
+
+        osmium::io::Reader reader{input_file};
+        osmium::apply(reader, location_handler, collector.handler([&](osmium::memory::Buffer&& buffer) {
+            for (const auto& area : buffer.select<osmium::Area>()) {
+                for (const auto& item : area) {
+                    if (item.type() == osmium::item_type::outer_ring ||
+                        item.type() == osmium::item_type::inner_ring) {
+                        builder.add_item(item);
+                        has_ring = true;
+                    }
+                }
+            }
+        }));
+        reader.close();
+    }
+
+    if (has_ring) {
+        return m_buffer.commit();
+    }
+
+    m_buffer.rollback();
+    throw osmium::io_error{"No areas found in the OSM file."};
+}
+
diff --git a/src/exception.hpp b/src/extract/osm_file_parser.hpp
similarity index 56%
copy from src/exception.hpp
copy to src/extract/osm_file_parser.hpp
index ebe8970..3884258 100644
--- a/src/exception.hpp
+++ b/src/extract/osm_file_parser.hpp
@@ -1,12 +1,12 @@
-#ifndef EXCEPTION_HPP
-#define EXCEPTION_HPP
+#ifndef EXTRACT_OSM_FILE_PARSER_HPP
+#define EXTRACT_OSM_FILE_PARSER_HPP
 
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -23,22 +23,26 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 */
 
-#include <stdexcept>
+#include <string>
+
+namespace osmium { namespace memory {
+    class Buffer;
+}}
 
 /**
- *  Thrown when there is a problem with the command line arguments.
+ * Gets areas from OSM files.
  */
-struct argument_error : std::runtime_error {
+class OSMFileParser {
+
+    osmium::memory::Buffer& m_buffer;
+    std::string m_file_name;
 
-    explicit argument_error(const char* message) :
-        std::runtime_error(message) {
-    }
+public:
 
-    explicit argument_error(const std::string& message) :
-        std::runtime_error(message) {
-    }
+    OSMFileParser(osmium::memory::Buffer& buffer, const std::string& file_name);
 
-};
+    std::size_t operator()();
 
+}; // class OSMFileParser
 
-#endif // EXCEPTION_HPP
+#endif // EXTRACT_OSM_FILE_PARSER_HPP
diff --git a/src/extract/poly_file_parser.cpp b/src/extract/poly_file_parser.cpp
new file mode 100644
index 0000000..0a03a01
--- /dev/null
+++ b/src/extract/poly_file_parser.cpp
@@ -0,0 +1,133 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <fstream>
+#include <sstream>
+#include <string>
+#include <vector>
+
+#include <osmium/geom/coordinates.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/util/string.hpp>
+
+#include "error.hpp"
+#include "poly_file_parser.hpp"
+
+void PolyFileParser::error(const std::string& message) {
+    throw poly_error{std::string{"In file '"} + m_file_name + "' on line " + std::to_string(m_line + 1) + ":\n" + message};
+}
+
+PolyFileParser::PolyFileParser(osmium::memory::Buffer& buffer, const std::string& file_name) :
+    m_buffer(buffer),
+    m_builder(nullptr),
+    m_file_name(file_name),
+    m_data() {
+    std::ifstream file{file_name};
+    if (!file.is_open()) {
+        throw config_error{std::string{"Could not open file '"} + file_name + "'"};
+    }
+    std::stringstream sstr;
+    sstr << file.rdbuf();
+    m_data = osmium::split_string(sstr.str(), '\n', true);
+
+    // remove CR at end of lines
+    for (auto& line : m_data) {
+        if (line.back() == '\r') {
+            line.resize(line.size() - 1);
+        }
+    }
+}
+
+void PolyFileParser::parse_ring() {
+    bool inner_ring = line()[0] == '!';
+    ++m_line;
+
+    std::vector<osmium::Location> coordinates;
+    while (m_line < m_data.size()) {
+        if (line() == "END") {
+            if (coordinates.size() < 3) {
+                error("Expected at least three lines with coordinates.");
+            }
+
+            if (coordinates.front() != coordinates.back()) {
+                coordinates.push_back(coordinates.front());
+            }
+
+            if (inner_ring) {
+                osmium::builder::InnerRingBuilder ring_builder{*m_builder};
+                for (const auto& location : coordinates) {
+                    ring_builder.add_node_ref(0, location);
+                }
+            } else {
+                osmium::builder::OuterRingBuilder ring_builder{*m_builder};
+                for (const auto& location : coordinates) {
+                    ring_builder.add_node_ref(0, location);
+                }
+            }
+
+            ++m_line;
+            return;
+        }
+
+        std::istringstream sstr{line()};
+        double lon, lat;
+        if (!(sstr >> lon >> lat)) {
+            error("Expected coordinates or 'END' to end the ring.");
+        }
+        coordinates.emplace_back(lon, lat);
+
+        ++m_line;
+    }
+}
+
+void PolyFileParser::parse_multipolygon() {
+    ++m_line; // ignore first line
+
+    while (m_line < m_data.size()) {
+        if (line() == "END") {
+            ++m_line;
+            if (m_line == 2) {
+                error("Need at least one ring in (multi)polygon.");
+            }
+            return;
+        }
+        parse_ring();
+    }
+
+    --m_line;
+    error("Expected 'END' for end of (multi)polygon.");
+}
+
+std::size_t PolyFileParser::operator()() {
+    if (m_data.empty()) {
+        throw poly_error{std::string{"File '"} + m_file_name + "' is empty."};
+    }
+
+    m_builder.reset(new osmium::builder::AreaBuilder{m_buffer});
+    while (m_line < m_data.size()) {
+        parse_multipolygon();
+    }
+    m_builder.reset();
+
+    return m_buffer.commit();
+}
+
diff --git a/src/extract/poly_file_parser.hpp b/src/extract/poly_file_parser.hpp
new file mode 100644
index 0000000..2558eeb
--- /dev/null
+++ b/src/extract/poly_file_parser.hpp
@@ -0,0 +1,78 @@
+#ifndef EXTRACT_POLY_FILE_PARSER_HPP
+#define EXTRACT_POLY_FILE_PARSER_HPP
+
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <osmium/builder/osm_object_builder.hpp>
+
+namespace osmium { namespace memory {
+    class Buffer;
+}}
+
+/**
+ *  Thrown when there is a problem with parsing a poly file.
+ */
+struct poly_error : public std::runtime_error {
+
+    explicit poly_error(const std::string& message) :
+        std::runtime_error(message) {
+    }
+
+}; // struct poly_error
+
+/**
+ * Gets areas from .poly files.
+ *
+ * Format description:
+ * http://wiki.openstreetmap.org/wiki/Osmosis/Polygon_Filter_File_Format
+ */
+class PolyFileParser {
+
+    osmium::memory::Buffer& m_buffer;
+    std::unique_ptr<osmium::builder::AreaBuilder> m_builder;
+    std::string m_file_name;
+    std::vector<std::string> m_data;
+    size_t m_line = 0;
+
+    void parse_ring();
+    void parse_multipolygon();
+
+    const std::string& line() const noexcept {
+        return m_data[m_line];
+    }
+
+    void error(const std::string& message);
+
+public:
+
+    PolyFileParser(osmium::memory::Buffer& buffer, const std::string& file_name);
+
+    std::size_t operator()();
+
+}; // class PolyFileParser
+
+#endif // EXTRACT_POLY_FILE_PARSER_HPP
diff --git a/src/extract/strategy.hpp b/src/extract/strategy.hpp
new file mode 100644
index 0000000..b4dc921
--- /dev/null
+++ b/src/extract/strategy.hpp
@@ -0,0 +1,173 @@
+#ifndef EXTRACT_STRATEGY_HPP
+#define EXTRACT_STRATEGY_HPP
+
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <memory>
+
+#include <osmium/io/file.hpp>
+#include <osmium/io/reader.hpp>
+#include <osmium/io/writer.hpp>
+#include <osmium/memory/buffer.hpp>
+#include <osmium/memory/item.hpp>
+#include <osmium/osm/node.hpp>
+#include <osmium/osm/object.hpp>
+#include <osmium/osm/relation.hpp>
+#include <osmium/osm/types.hpp>
+#include <osmium/osm/way.hpp>
+#include <osmium/util/progress_bar.hpp>
+#include <osmium/util/verbose_output.hpp>
+
+#include "extract.hpp"
+
+template <typename T>
+class ExtractData : public T {
+
+    Extract* m_extract_ptr;
+
+public:
+
+    explicit ExtractData(Extract& extract) :
+        T(),
+        m_extract_ptr(&extract) {
+    }
+
+    bool contains(const osmium::Location& location) const noexcept {
+        return m_extract_ptr->contains(location);
+    }
+
+    void write(const osmium::memory::Item& item) {
+        m_extract_ptr->write(item);
+    }
+
+    void close() {
+        m_extract_ptr->close_file();
+    }
+
+}; // class ExtractData
+
+
+class ExtractStrategy {
+
+public:
+
+    ExtractStrategy() = default;
+
+    virtual ~ExtractStrategy() = default;
+
+    virtual const char* name() const noexcept = 0;
+
+    virtual void show_arguments(osmium::util::VerboseOutput& /*vout*/) {
+    }
+
+    virtual void run(osmium::util::VerboseOutput& vout, bool display_progress, const osmium::io::File& input_file) = 0;
+
+}; // class ExtractStrategy
+
+
+template <typename TStrategy, typename TChild>
+class Pass {
+
+    TStrategy& m_strategy;
+
+    void run_impl(osmium::ProgressBar& progress_bar, osmium::io::Reader& reader) {
+        while (osmium::memory::Buffer buffer = reader.read()) {
+            progress_bar.update(reader.offset());
+            for (const auto& object : buffer) {
+                switch (object.type()) {
+                    case osmium::item_type::node:
+                        self().node(static_cast<const osmium::Node&>(object));
+                        for (auto& e : extracts()) {
+                            self().enode(e, static_cast<const osmium::Node&>(object));
+                        }
+                        break;
+                    case osmium::item_type::way:
+                        self().way(static_cast<const osmium::Way&>(object));
+                        for (auto& e : extracts()) {
+                            self().eway(e, static_cast<const osmium::Way&>(object));
+                        }
+                        break;
+                    case osmium::item_type::relation:
+                        self().relation(static_cast<const osmium::Relation&>(object));
+                        for (auto& e : extracts()) {
+                            self().erelation(e, static_cast<const osmium::Relation&>(object));
+                        }
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+    }
+
+protected:
+
+    using extract_data = typename TStrategy::extract_data;
+
+    TStrategy& strategy() {
+        return m_strategy;
+    }
+
+    std::vector<extract_data>& extracts() {
+        return m_strategy.m_extracts;
+    }
+
+    TChild& self() {
+        return *static_cast<TChild*>(this);
+    }
+
+    void node(const osmium::Node&) {
+    }
+
+    void way(const osmium::Way&) {
+    }
+
+    void relation(const osmium::Relation&) {
+    }
+
+    void enode(extract_data&, const osmium::Node&) {
+    }
+
+    void eway(extract_data&, const osmium::Way&) {
+    }
+
+    void erelation(extract_data&, const osmium::Relation&) {
+    }
+
+public:
+
+    explicit Pass(TStrategy& strategy) :
+        m_strategy(strategy) {
+    }
+
+    template <typename... Args>
+    void run(osmium::ProgressBar& progress_bar, Args ...args) {
+        osmium::io::Reader reader{std::forward<Args>(args)...};
+        run_impl(progress_bar, reader);
+        reader.close();
+    }
+
+}; // class Pass
+
+
+#endif // EXTRACT_STRATEGY_HPP
diff --git a/src/extract/strategy_complete_ways.cpp b/src/extract/strategy_complete_ways.cpp
new file mode 100644
index 0000000..bb70fb4
--- /dev/null
+++ b/src/extract/strategy_complete_ways.cpp
@@ -0,0 +1,176 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <osmium/handler/check_order.hpp>
+#include <osmium/util/file.hpp>
+
+#include "strategy_complete_ways.hpp"
+
+namespace strategy_complete_ways {
+
+    void Data::add_relation_parents(osmium::unsigned_object_id_type id, const osmium::index::RelationsMapIndex& map) {
+        map.for_each_parent(id, [&](osmium::unsigned_object_id_type parent_id) {
+            if (! relation_ids.get(parent_id)) {
+                relation_ids.set(parent_id);
+                add_relation_parents(parent_id, map);
+            }
+        });
+    }
+
+    Strategy::Strategy(const std::vector<std::unique_ptr<Extract>>& extracts, const osmium::util::Options& /*options*/) :
+        ExtractStrategy() {
+        m_extracts.reserve(extracts.size());
+        for (const auto& extract : extracts) {
+            m_extracts.emplace_back(*extract);
+        }
+    }
+
+    const char* Strategy::name() const noexcept {
+        return "complete_ways";
+    }
+
+    class Pass1 : public Pass<Strategy, Pass1> {
+
+        osmium::handler::CheckOrder m_check_order;
+        osmium::index::RelationsMapStash m_relations_map_stash;
+
+    public:
+
+        Pass1(Strategy& strategy) :
+            Pass(strategy) {
+        }
+
+        void node(const osmium::Node& node) {
+            m_check_order.node(node);
+        }
+
+        void enode(extract_data& e, const osmium::Node& node) {
+            if (e.contains(node.location())) {
+                e.node_ids.set(node.positive_id());
+            }
+        }
+
+        void way(const osmium::Way& way) {
+            m_check_order.way(way);
+        }
+
+        void eway(extract_data& e, const osmium::Way& way) {
+            for (const auto& nr : way.nodes()) {
+                if (e.node_ids.get(nr.positive_ref())) {
+                    e.way_ids.set(way.positive_id());
+                    for (const auto& nr : way.nodes()) {
+                        e.extra_node_ids.set(nr.ref());
+                    }
+                    return;
+                }
+            }
+        }
+
+        void relation(const osmium::Relation& relation) {
+            m_check_order.relation(relation);
+            m_relations_map_stash.add_members(relation);
+        }
+
+        void erelation(extract_data& e, const osmium::Relation& relation) {
+            for (const auto& member : relation.members()) {
+                switch (member.type()) {
+                    case osmium::item_type::node:
+                        if (e.node_ids.get(member.positive_ref())) {
+                            e.relation_ids.set(relation.positive_id());
+                            return;
+                        }
+                        break;
+                    case osmium::item_type::way:
+                        if (e.way_ids.get(member.positive_ref())) {
+                            e.relation_ids.set(relation.positive_id());
+                            return;
+                        }
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        osmium::index::RelationsMapStash& relations_map_stash() noexcept {
+            return m_relations_map_stash;
+        }
+
+    }; // class Pass1
+
+    class Pass2 : public Pass<Strategy, Pass2> {
+
+    public:
+
+        Pass2(Strategy& strategy) :
+            Pass(strategy) {
+        }
+
+        void enode(extract_data& e, const osmium::Node& node) {
+            if (e.node_ids.get(node.positive_id()) ||
+                e.extra_node_ids.get(node.positive_id())) {
+                e.write(node);
+            }
+        }
+
+        void eway(extract_data& e, const osmium::Way& way) {
+            if (e.way_ids.get(way.positive_id())) {
+                e.write(way);
+            }
+        }
+
+        void erelation(extract_data& e, const osmium::Relation& relation) {
+            if (e.relation_ids.get(relation.positive_id())) {
+                e.write(relation);
+            }
+        }
+
+    }; // class Pass2
+
+    void Strategy::run(osmium::util::VerboseOutput& vout, bool display_progress, const osmium::io::File& input_file) {
+        vout << "Running 'complete_ways' strategy in two passes...\n";
+        const size_t file_size = osmium::util::file_size(input_file.filename());
+        osmium::ProgressBar progress_bar{file_size * 2, display_progress};
+
+        vout << "First pass...\n";
+        Pass1 pass1{*this};
+        pass1.run(progress_bar, input_file, osmium::io::read_meta::no);
+        progress_bar.file_done(file_size);
+
+        // recursively get parents of all relations that are in an extract
+        auto relations_map = pass1.relations_map_stash().build_index();
+        for (auto& e : m_extracts) {
+            for (osmium::unsigned_object_id_type id : e.relation_ids) {
+                e.add_relation_parents(id, relations_map);
+            }
+        }
+
+        progress_bar.remove();
+        vout << "Second pass...\n";
+        Pass2 pass2{*this};
+        pass2.run(progress_bar, input_file);
+
+        progress_bar.done();
+    }
+
+} // namespace strategy_complete_ways
+
diff --git a/src/extract/strategy_complete_ways.hpp b/src/extract/strategy_complete_ways.hpp
new file mode 100644
index 0000000..79c179d
--- /dev/null
+++ b/src/extract/strategy_complete_ways.hpp
@@ -0,0 +1,65 @@
+#ifndef EXTRACT_STRATEGY_COMPLETE_WAYS_HPP
+#define EXTRACT_STRATEGY_COMPLETE_WAYS_HPP
+
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <memory>
+#include <vector>
+
+#include <osmium/index/id_set.hpp>
+#include <osmium/index/relations_map.hpp>
+
+#include "strategy.hpp"
+
+namespace strategy_complete_ways {
+
+    struct Data {
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> node_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> extra_node_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> way_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> relation_ids;
+
+        void add_relation_parents(osmium::unsigned_object_id_type id, const osmium::index::RelationsMapIndex& map);
+    };
+
+    class Strategy : public ExtractStrategy {
+
+        template<typename S, typename T> friend class ::Pass;
+        friend class Pass1;
+
+        using extract_data = ExtractData<Data>;
+        std::vector<extract_data> m_extracts;
+
+    public:
+
+        explicit Strategy(const std::vector<std::unique_ptr<Extract>>& extracts, const osmium::util::Options& /*options*/);
+
+        const char* name() const noexcept override final;
+
+        void run(osmium::util::VerboseOutput& vout, bool display_progress, const osmium::io::File& input_file) override final;
+
+    }; // class Strategy
+
+} // namespace strategy_complete_ways
+
+#endif // EXTRACT_STRATEGY_COMPLETE_WAYS_HPP
diff --git a/src/extract/strategy_complete_ways_with_history.cpp b/src/extract/strategy_complete_ways_with_history.cpp
new file mode 100644
index 0000000..8b4eccf
--- /dev/null
+++ b/src/extract/strategy_complete_ways_with_history.cpp
@@ -0,0 +1,186 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <memory>
+#include <vector>
+
+#include "strategy_complete_ways_with_history.hpp"
+
+namespace strategy_complete_ways_with_history {
+
+    void Data::add_relation_parents(osmium::unsigned_object_id_type id, const osmium::index::RelationsMapIndex& map) {
+        map.for_each_parent(id, [&](osmium::unsigned_object_id_type parent_id) {
+            if (! relation_ids.get(parent_id)) {
+                relation_ids.set(parent_id);
+                add_relation_parents(parent_id, map);
+            }
+        });
+    }
+
+    Strategy::Strategy(const std::vector<std::unique_ptr<Extract>>& extracts, const osmium::util::Options& /*options*/) :
+        ExtractStrategy() {
+        m_extracts.reserve(extracts.size());
+        for (const auto& extract : extracts) {
+            m_extracts.emplace_back(*extract);
+        }
+    }
+
+    const char* Strategy::name() const noexcept {
+        return "complete_ways";
+    }
+
+    class Pass1 : public Pass<Strategy, Pass1> {
+
+        osmium::index::RelationsMapStash m_relations_map_stash;
+        std::vector<osmium::unsigned_object_id_type> m_current_way_nodes;
+        osmium::unsigned_object_id_type m_current_way_id = 0;
+
+    public:
+
+        Pass1(Strategy& strategy) :
+            Pass(strategy) {
+        }
+
+        void add_extra_nodes() {
+            for (auto& e : extracts()) {
+                if (e.way_ids.get(m_current_way_id)) {
+                    for (const auto& id : m_current_way_nodes) {
+                        e.extra_node_ids.set(id);
+                    }
+                }
+            }
+            m_current_way_nodes.clear();
+        }
+
+        void enode(extract_data& e, const osmium::Node& node) {
+            if (e.contains(node.location())) {
+                e.node_ids.set(node.positive_id());
+            }
+        }
+
+        void way(const osmium::Way& way) {
+            if (m_current_way_id != way.positive_id()) {
+                add_extra_nodes();
+                m_current_way_id = way.id();
+            }
+
+            for (const auto& wn : way.nodes()) {
+                m_current_way_nodes.push_back(wn.positive_ref());
+            }
+        }
+
+        void eway(extract_data& e, const osmium::Way& way) {
+            for (const auto& nr : way.nodes()) {
+                if (e.node_ids.get(nr.positive_ref())) {
+                    e.way_ids.set(way.positive_id());
+                    return;
+                }
+            }
+        }
+
+        void relation(const osmium::Relation& relation) {
+            m_relations_map_stash.add_members(relation);
+        }
+
+        void erelation(extract_data& e, const osmium::Relation& relation) {
+            for (const auto& member : relation.members()) {
+                switch (member.type()) {
+                    case osmium::item_type::node:
+                        if (e.node_ids.get(member.positive_ref())) {
+                            e.relation_ids.set(relation.positive_id());
+                            return;
+                        }
+                        break;
+                    case osmium::item_type::way:
+                        if (e.way_ids.get(member.positive_ref())) {
+                            e.relation_ids.set(relation.positive_id());
+                            return;
+                        }
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        osmium::index::RelationsMapStash& relations_map_stash() noexcept {
+            return m_relations_map_stash;
+        }
+
+    }; // class Pass1
+
+    class Pass2 : public Pass<Strategy, Pass2> {
+
+    public:
+
+        Pass2(Strategy& strategy) :
+            Pass(strategy) {
+        }
+
+        void enode(extract_data& e, const osmium::Node& node) {
+            if (e.node_ids.get(node.positive_id()) ||
+                e.extra_node_ids.get(node.positive_id())) {
+                e.write(node);
+            }
+        }
+
+        void eway(extract_data& e, const osmium::Way& way) {
+            if (e.way_ids.get(way.positive_id())) {
+                e.write(way);
+            }
+        }
+
+        void erelation(extract_data& e, const osmium::Relation& relation) {
+            if (e.relation_ids.get(relation.positive_id())) {
+                e.write(relation);
+            }
+        }
+
+    }; // class Pass2
+
+    void Strategy::run(osmium::util::VerboseOutput& vout, bool display_progress, const osmium::io::File& input_file) {
+        vout << "Running 'complete_ways' strategy in two passes...\n";
+        const size_t file_size = osmium::util::file_size(input_file.filename());
+        osmium::ProgressBar progress_bar{file_size * 2, display_progress};
+
+        vout << "First pass...\n";
+        Pass1 pass1{*this};
+        pass1.run(progress_bar, input_file, osmium::io::read_meta::no);
+        progress_bar.file_done(file_size);
+        pass1.add_extra_nodes();
+
+        // recursively get parents of all relations that are in an extract
+        auto relations_map = pass1.relations_map_stash().build_index();
+        for (auto& e : m_extracts) {
+            for (osmium::unsigned_object_id_type id : e.relation_ids) {
+                e.add_relation_parents(id, relations_map);
+            }
+        }
+
+        progress_bar.remove();
+        vout << "Second pass...\n";
+        Pass2 pass2{*this};
+        pass2.run(progress_bar, input_file);
+        progress_bar.done();
+    }
+
+} // namespace strategy_complete_ways_with_history
diff --git a/src/extract/strategy_complete_ways_with_history.hpp b/src/extract/strategy_complete_ways_with_history.hpp
new file mode 100644
index 0000000..393605a
--- /dev/null
+++ b/src/extract/strategy_complete_ways_with_history.hpp
@@ -0,0 +1,65 @@
+#ifndef EXTRACT_STRATEGY_COMPLETE_WAYS_WITH_HISTORY_HPP
+#define EXTRACT_STRATEGY_COMPLETE_WAYS_WITH_HISTORY_HPP
+
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <memory>
+#include <vector>
+
+#include <osmium/index/id_set.hpp>
+#include <osmium/index/relations_map.hpp>
+
+#include "strategy.hpp"
+
+namespace strategy_complete_ways_with_history {
+
+    struct Data {
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> node_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> extra_node_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> way_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> relation_ids;
+
+        void add_relation_parents(osmium::unsigned_object_id_type id, const osmium::index::RelationsMapIndex& map);
+    };
+
+    class Strategy : public ExtractStrategy {
+
+        template<typename S, typename T> friend class ::Pass;
+        friend class Pass1;
+
+        using extract_data = ExtractData<Data>;
+        std::vector<extract_data> m_extracts;
+
+    public:
+
+        explicit Strategy(const std::vector<std::unique_ptr<Extract>>& extracts, const osmium::util::Options& /*options*/);
+
+        const char* name() const noexcept override final;
+
+        void run(osmium::util::VerboseOutput& vout, bool display_progress, const osmium::io::File& input_file) override final;
+
+    }; // class Strategy
+
+} // namespace strategy_complete_ways_with_history
+
+#endif // EXTRACT_STRATEGY_COMPLETE_WAYS_WITH_HISTORY_HPP
diff --git a/src/extract/strategy_simple.cpp b/src/extract/strategy_simple.cpp
new file mode 100644
index 0000000..9e4d8b5
--- /dev/null
+++ b/src/extract/strategy_simple.cpp
@@ -0,0 +1,113 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <osmium/handler/check_order.hpp>
+
+#include "strategy_simple.hpp"
+
+namespace strategy_simple {
+
+    Strategy::Strategy(const std::vector<std::unique_ptr<Extract>>& extracts, const osmium::util::Options& /*options*/) :
+        ExtractStrategy() {
+        m_extracts.reserve(extracts.size());
+        for (const auto& extract : extracts) {
+            m_extracts.emplace_back(*extract);
+        }
+    }
+
+    const char* Strategy::name() const noexcept {
+        return "simple";
+    }
+
+    class Pass1 : public Pass<Strategy, Pass1> {
+
+        osmium::handler::CheckOrder m_check_order;
+
+    public:
+
+        Pass1(Strategy& strategy) :
+            Pass(strategy) {
+        }
+
+        void node(const osmium::Node& node) {
+            m_check_order.node(node);
+        }
+
+        void enode(extract_data& e, const osmium::Node& node) {
+            if (e.contains(node.location())) {
+                e.write(node);
+                e.node_ids.set(node.positive_id());
+            }
+        }
+
+        void way(const osmium::Way& way) {
+            m_check_order.way(way);
+        }
+
+        void eway(extract_data& e, const osmium::Way& way) {
+            for (const auto& nr : way.nodes()) {
+                if (e.node_ids.get(nr.positive_ref())) {
+                    e.write(way);
+                    e.way_ids.set(way.positive_id());
+                }
+                return;
+            }
+        }
+
+        void relation(const osmium::Relation& relation) {
+            m_check_order.relation(relation);
+        }
+
+        void erelation(extract_data& e, const osmium::Relation& relation) {
+            for (const auto& member : relation.members()) {
+                switch (member.type()) {
+                    case osmium::item_type::node:
+                        if (e.node_ids.get(member.positive_ref())) {
+                            e.write(relation);
+                        }
+                        return;
+                    case osmium::item_type::way:
+                        if (e.way_ids.get(member.positive_ref())) {
+                            e.write(relation);
+                        }
+                        return;
+                    default:
+                        break;
+                }
+            }
+        }
+
+    }; // class Pass1
+
+    void Strategy::run(osmium::util::VerboseOutput& vout, bool display_progress, const osmium::io::File& input_file) {
+        vout << "Running 'simple' strategy in one pass...\n";
+        const size_t file_size = osmium::util::file_size(input_file.filename());
+        osmium::ProgressBar progress_bar{file_size, display_progress};
+
+        Pass1 pass1{*this};
+        pass1.run(progress_bar, input_file);
+
+        progress_bar.done();
+    }
+
+} // namespace strategy_simple
+
diff --git a/src/extract/strategy_simple.hpp b/src/extract/strategy_simple.hpp
new file mode 100644
index 0000000..2735415
--- /dev/null
+++ b/src/extract/strategy_simple.hpp
@@ -0,0 +1,60 @@
+#ifndef EXTRACT_STRATEGY_SIMPLE_HPP
+#define EXTRACT_STRATEGY_SIMPLE_HPP
+
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <memory>
+#include <vector>
+
+#include <osmium/index/id_set.hpp>
+
+#include "strategy.hpp"
+
+namespace strategy_simple {
+
+    struct Data {
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> node_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> way_ids;
+    };
+
+    class Strategy : public ExtractStrategy {
+
+        template<typename S, typename T> friend class ::Pass;
+        friend class Pass1;
+
+        using extract_data = ExtractData<Data>;
+        std::vector<extract_data> m_extracts;
+
+    public:
+
+        explicit Strategy(const std::vector<std::unique_ptr<Extract>>& extracts, const osmium::util::Options& /*options*/);
+
+        const char* name() const noexcept override final;
+
+        void run(osmium::util::VerboseOutput& vout, bool display_progress, const osmium::io::File& input_file) override final;
+
+    }; // class Strategy
+
+} // namespace strategy_simple
+
+#endif // EXTRACT_STRATEGY_SIMPLE_HPP
diff --git a/src/extract/strategy_smart.cpp b/src/extract/strategy_smart.cpp
new file mode 100644
index 0000000..f3f63da
--- /dev/null
+++ b/src/extract/strategy_smart.cpp
@@ -0,0 +1,255 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <osmium/handler/check_order.hpp>
+#include <osmium/util/string.hpp>
+
+#include "strategy_smart.hpp"
+
+namespace strategy_smart {
+
+    void Data::add_relation(const osmium::Relation& relation) {
+        for (const auto& member : relation.members()) {
+            const auto ref = member.positive_ref();
+            switch (member.type()) {
+                case osmium::item_type::node:
+                    extra_node_ids.set(ref);
+                    break;
+                case osmium::item_type::way:
+                    extra_way_ids.set(ref);
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+
+    void Data::add_relation_parents(osmium::unsigned_object_id_type id, const osmium::index::RelationsMapIndex& map) {
+        map.for_each_parent(id, [&](osmium::unsigned_object_id_type parent_id) {
+            if (! relation_ids.get(parent_id) &&
+                ! extra_relation_ids.get(parent_id)) {
+                relation_ids.set(parent_id);
+                add_relation_parents(parent_id, map);
+            }
+        });
+    }
+
+    Strategy::Strategy(const std::vector<std::unique_ptr<Extract>>& extracts, const osmium::util::Options& options) :
+        ExtractStrategy(),
+        m_types() {
+        m_extracts.reserve(extracts.size());
+        for (const auto& extract : extracts) {
+            m_extracts.emplace_back(*extract);
+        }
+
+        const auto types = options.get("types");
+        if (types == "") {
+            m_types = {"multipolygon"};
+        } else if (types != "any") {
+            m_types = osmium::split_string(types, ',', true);
+        }
+    }
+
+    const char* Strategy::name() const noexcept {
+        return "smart";
+    }
+
+    bool Strategy::check_type(const osmium::Relation& relation) const noexcept {
+        if (m_types.empty()) {
+            return true;
+        }
+
+        const char* type = relation.tags()["type"];
+        if (!type) {
+            return false;
+        }
+        const auto it = std::find(m_types.begin(), m_types.end(), type);
+        return it != m_types.end();
+    }
+
+    void Strategy::show_arguments(osmium::util::VerboseOutput& vout) {
+        if (m_types.empty()) {
+            vout << "    types: any\n";
+        } else {
+            vout << "    types:\n";
+            for (const auto& type : m_types) {
+                vout << "      " << type << '\n';
+            }
+        }
+    }
+
+    class Pass1 : public Pass<Strategy, Pass1> {
+
+        osmium::handler::CheckOrder m_check_order;
+        osmium::index::RelationsMapStash m_relations_map_stash;
+
+    public:
+
+        Pass1(Strategy& strategy) :
+            Pass(strategy) {
+        }
+
+        void node(const osmium::Node& node) {
+            m_check_order.node(node);
+        }
+
+        void enode(extract_data& e, const osmium::Node& node) {
+            if (e.contains(node.location())) {
+                e.node_ids.set(node.positive_id());
+            }
+        }
+
+        void way(const osmium::Way& way) {
+            m_check_order.way(way);
+        }
+
+        void eway(extract_data& e, const osmium::Way& way) {
+            for (const auto& nr : way.nodes()) {
+                if (e.node_ids.get(nr.positive_ref())) {
+                    e.way_ids.set(way.positive_id());
+                    return;
+                }
+            }
+        }
+
+        void relation(const osmium::Relation& relation) {
+            m_check_order.relation(relation);
+            m_relations_map_stash.add_members(relation);
+        }
+
+        void erelation(extract_data& e, const osmium::Relation& relation) {
+            for (const auto& member : relation.members()) {
+                switch (member.type()) {
+                    case osmium::item_type::node:
+                        if (e.node_ids.get(member.positive_ref())) {
+                            e.relation_ids.set(relation.positive_id());
+                            if (strategy().check_type(relation)) {
+                                e.add_relation(relation);
+                            }
+                            return;
+                        }
+                        break;
+                    case osmium::item_type::way:
+                        if (e.way_ids.get(member.positive_ref())) {
+                            e.relation_ids.set(relation.positive_id());
+                            if (strategy().check_type(relation)) {
+                                e.add_relation(relation);
+                            }
+                            return;
+                        }
+                        break;
+                    default:
+                        break;
+                }
+            }
+        }
+
+        osmium::index::RelationsMapStash& relations_map_stash() noexcept {
+            return m_relations_map_stash;
+        }
+
+    }; // class Pass1
+
+    class Pass2 : public Pass<Strategy, Pass2> {
+
+    public:
+
+        Pass2(Strategy& strategy) :
+            Pass(strategy) {
+        }
+
+        void eway(extract_data& e, const osmium::Way& way) {
+            if (e.way_ids.get(way.positive_id()) ||
+                e.extra_way_ids.get(way.positive_id())) {
+                for (const auto& nr : way.nodes()) {
+                    e.extra_node_ids.set(nr.ref());
+                }
+            }
+        }
+
+    }; // class Pass2
+
+    class Pass3 : public Pass<Strategy, Pass3> {
+
+    public:
+
+        Pass3(Strategy& strategy) :
+            Pass(strategy) {
+        }
+
+        void enode(extract_data& e, const osmium::Node& node) {
+            if (e.node_ids.get(node.positive_id()) ||
+                e.extra_node_ids.get(node.positive_id())) {
+                e.write(node);
+            }
+        }
+
+        void eway(extract_data& e, const osmium::Way& way) {
+            if (e.way_ids.get(way.positive_id()) ||
+                e.extra_way_ids.get(way.positive_id())) {
+                e.write(way);
+            }
+        }
+
+        void erelation(extract_data& e, const osmium::Relation& relation) {
+            if (e.relation_ids.get(relation.positive_id()) ||
+                e.extra_relation_ids.get(relation.positive_id())) {
+                e.write(relation);
+            }
+        }
+
+    }; // class Pass3
+
+    void Strategy::run(osmium::util::VerboseOutput& vout, bool display_progress, const osmium::io::File& input_file) {
+        vout << "Running 'smart' strategy in three passes...\n";
+        const size_t file_size = osmium::util::file_size(input_file.filename());
+        osmium::ProgressBar progress_bar{file_size * 3, display_progress};
+
+        vout << "First pass...\n";
+        Pass1 pass1{*this};
+        pass1.run(progress_bar, input_file, osmium::io::read_meta::no);
+        progress_bar.file_done(file_size);
+
+        // recursively get parents of all relations that are in an extract
+        auto relations_map = pass1.relations_map_stash().build_index();
+        for (auto& e : m_extracts) {
+            for (osmium::unsigned_object_id_type id : e.relation_ids) {
+                e.add_relation_parents(id, relations_map);
+            }
+        }
+
+        progress_bar.remove();
+        vout << "Second pass...\n";
+        Pass2 pass2{*this};
+        pass2.run(progress_bar, input_file, osmium::osm_entity_bits::way, osmium::io::read_meta::no);
+        progress_bar.file_done(file_size);
+
+        progress_bar.remove();
+        vout << "Third pass...\n";
+        Pass3 pass3{*this};
+        pass3.run(progress_bar, input_file);
+
+        progress_bar.done();
+    }
+
+} // namespace strategy_smart
+
diff --git a/src/extract/strategy_smart.hpp b/src/extract/strategy_smart.hpp
new file mode 100644
index 0000000..ddd9607
--- /dev/null
+++ b/src/extract/strategy_smart.hpp
@@ -0,0 +1,75 @@
+#ifndef EXTRACT_STRATEGY_SMART_HPP
+#define EXTRACT_STRATEGY_SMART_HPP
+
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include <osmium/index/id_set.hpp>
+#include <osmium/index/relations_map.hpp>
+
+#include "strategy.hpp"
+
+namespace strategy_smart {
+
+    struct Data {
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> node_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> extra_node_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> way_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> extra_way_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> relation_ids;
+        osmium::index::IdSetDense<osmium::unsigned_object_id_type> extra_relation_ids;
+
+        void add_relation(const osmium::Relation& relation);
+        void add_relation_parents(osmium::unsigned_object_id_type id, const osmium::index::RelationsMapIndex& map);
+    };
+
+    class Strategy : public ExtractStrategy {
+
+        template<typename S, typename T> friend class ::Pass;
+        friend class Pass1;
+
+        using extract_data = ExtractData<Data>;
+        std::vector<extract_data> m_extracts;
+
+        std::vector<std::string> m_types;
+
+        bool check_type(const osmium::Relation& relation) const noexcept;
+
+    public:
+
+        explicit Strategy(const std::vector<std::unique_ptr<Extract>>& extracts, const osmium::util::Options& options);
+
+        const char* name() const noexcept override final;
+
+        void show_arguments(osmium::util::VerboseOutput& vout) override final;
+
+        void run(osmium::util::VerboseOutput& vout, bool display_progress, const osmium::io::File& input_file) override final;
+
+    }; // class Strategy
+
+} // namespace strategy_smart
+
+#endif // EXTRACT_STRATEGY_SMART_HPP
diff --git a/src/io.cpp b/src/io.cpp
index bdd57ee..c9df0f1 100644
--- a/src/io.cpp
+++ b/src/io.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -36,6 +36,7 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 #include "cmd.hpp"
 #include "exception.hpp"
+#include "util.hpp"
 
 void with_single_osm_input::setup_input_file(const boost::program_options::variables_map& vm) {
     if (vm.count("input-filename")) {
@@ -118,7 +119,7 @@ void with_multiple_osm_inputs::show_multiple_inputs_arguments(osmium::util::Verb
     vout << "    file format: " << m_input_format << "\n";
 }
 
-void with_osm_output::setup_output_file(const po::variables_map& vm) {
+void with_osm_output::init_output_file(const po::variables_map& vm) {
     if (vm.count("generator")) {
         m_generator = vm["generator"].as<std::string>();
     }
@@ -142,15 +143,23 @@ void with_osm_output::setup_output_file(const po::variables_map& vm) {
     if (vm.count("fsync")) {
         m_fsync = osmium::io::fsync::yes;
     }
+}
 
+void with_osm_output::check_output_file() {
     if ((m_output_filename == "-" || m_output_filename == "") && m_output_format.empty()) {
-        throw argument_error{"When writing to STDOUT you need to use the --output-format/-f option to declare the file format."};
+        throw argument_error{"When writing to STDOUT you need to use the --output-format/-f option\n"
+                             "to declare the file format. Or did you miss the --output/-O option?"};
     }
 
     m_output_file = osmium::io::File(m_output_filename, m_output_format);
     m_output_file.check();
 }
 
+void with_osm_output::setup_output_file(const po::variables_map& vm) {
+    init_output_file(vm);
+    check_output_file();
+}
+
 po::options_description with_osm_output::add_output_options() {
     po::options_description options("OUTPUT OPTIONS");
 
@@ -188,13 +197,3 @@ void with_osm_output::setup_header(osmium::io::Header& header) const {
     }
 }
 
-std::size_t file_size_sum(std::vector<osmium::io::File> files) {
-    std::size_t sum = 0;
-
-    for (const auto& file : files) {
-        sum += osmium::util::file_size(file.filename());
-    }
-
-    return sum;
-}
-
diff --git a/src/main.cpp b/src/main.cpp
index 9b01212..deb30ba 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -1,9 +1,9 @@
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -86,7 +86,7 @@ int main(int argc, char *argv[]) {
     if (command == "version") {
         std::cout << get_osmium_long_version() << '\n'
                   << get_libosmium_version() << '\n'
-                  << "Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>\n"
+                  << "Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>\n"
                   << "License: GNU GENERAL PUBLIC LICENSE Version 3 <https://gnu.org/licenses/gpl.html>.\n"
                   << "This is free software: you are free to change and redistribute it.\n"
                   << "There is NO WARRANTY, to the extent permitted by law.\n";
diff --git a/src/util.cpp b/src/util.cpp
new file mode 100644
index 0000000..3f36bde
--- /dev/null
+++ b/src/util.cpp
@@ -0,0 +1,69 @@
+/*
+
+Osmium -- OpenStreetMap data manipulation command line tool
+http://osmcode.org/osmium-tool/
+
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+*/
+
+#include <iostream>
+#include <string>
+#include <vector>
+
+#include <osmium/io/file.hpp>
+#include <osmium/util/file.hpp>
+
+#include "util.hpp"
+
+/**
+ * Get the suffix of the given file name. The suffix is everything after
+ * the *first* dot (.). So multiple suffixes will all be returned.
+ *
+ * france.poly    -> poly
+ * planet.osm.bz2 -> osm.bz2
+ * some/path/planet.osm.bz2 -> osm.bz2
+ */
+std::string get_filename_suffix(const std::string& file_name) {
+    auto slash = file_name.find_last_of('/');
+    if (slash == std::string::npos) {
+        slash = 0;
+    }
+    const auto dot = file_name.find_first_of('.', slash);
+    if (dot == std::string::npos) {
+        return "";
+    }
+    return file_name.substr(dot + 1);
+}
+
+const char* yes_no(bool choice) noexcept {
+    return choice ? "yes\n" : "no\n";
+}
+
+void warning(const char* text) {
+    std::cerr << "WARNING: " << text;
+}
+
+std::size_t file_size_sum(const std::vector<osmium::io::File>& files) {
+    std::size_t sum = 0;
+
+    for (const auto& file : files) {
+        sum += osmium::util::file_size(file.filename());
+    }
+
+    return sum;
+}
+
diff --git a/src/exception.hpp b/src/util.hpp
similarity index 58%
copy from src/exception.hpp
copy to src/util.hpp
index ebe8970..70295ab 100644
--- a/src/exception.hpp
+++ b/src/util.hpp
@@ -1,12 +1,12 @@
-#ifndef EXCEPTION_HPP
-#define EXCEPTION_HPP
+#ifndef UTIL_HPP
+#define UTIL_HPP
 
 /*
 
 Osmium -- OpenStreetMap data manipulation command line tool
-http://osmcode.org/osmium
+http://osmcode.org/osmium-tool/
 
-Copyright (C) 2013-2016  Jochen Topf <jochen at topf.org>
+Copyright (C) 2013-2017  Jochen Topf <jochen at topf.org>
 
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
@@ -23,22 +23,16 @@ along with this program.  If not, see <https://www.gnu.org/licenses/>.
 
 */
 
-#include <stdexcept>
+#include <string>
+#include <vector>
 
-/**
- *  Thrown when there is a problem with the command line arguments.
- */
-struct argument_error : std::runtime_error {
+namespace osmium { namespace io {
+    class File;
+}}
 
-    explicit argument_error(const char* message) :
-        std::runtime_error(message) {
-    }
+std::string get_filename_suffix(const std::string& file_name);
+const char* yes_no(bool choice) noexcept;
+void warning(const char* text);
+std::size_t file_size_sum(const std::vector<osmium::io::File>& files);
 
-    explicit argument_error(const std::string& message) :
-        std::runtime_error(message) {
-    }
-
-};
-
-
-#endif // EXCEPTION_HPP
+#endif // UTIL_HPP
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 5d91c6b..63940e0 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -8,13 +8,14 @@
 
 include_directories(include)
 include_directories(../src)
+include_directories(../src/extract)
 include_directories(../include)
 
-file(GLOB ALL_SETUP_TESTS */test_setup.cpp)
-file(GLOB ALL_COMMANDS ../src/command_*.cpp ../src/io.cpp ../src/cmd.cpp ../src/cmd_factory.cpp)
-add_executable(unit_tests unit_tests.cpp ${ALL_COMMANDS} ${ALL_SETUP_TESTS} ${PROJECT_BINARY_DIR}/src/version.cpp)
+file(GLOB ALL_UNIT_TESTS */test_setup.cpp */test_unit.cpp)
+file(GLOB ALL_COMMANDS ../src/command_*.cpp ../src/io.cpp ../src/cmd.cpp ../src/cmd_factory.cpp ../src/util.cpp ../src/extract/*.cpp)
+add_executable(unit_tests unit_tests.cpp ${ALL_COMMANDS} ${ALL_UNIT_TESTS} ${PROJECT_BINARY_DIR}/src/version.cpp)
 target_link_libraries(unit_tests ${Boost_LIBRARIES} ${OSMIUM_LIBRARIES})
-add_test(NAME unit_tests COMMAND unit_tests)
+add_test(NAME unit_tests COMMAND unit_tests WORKING_DIRECTORY "${PROJECT_SOURCE_DIR}")
 
 
 #-----------------------------------------------------------------------------
diff --git a/test/extract/empty-root.geojson b/test/extract/empty-root.geojson
new file mode 100644
index 0000000..0967ef4
--- /dev/null
+++ b/test/extract/empty-root.geojson
@@ -0,0 +1 @@
+{}
diff --git a/test/extract/empty.geojson b/test/extract/empty.geojson
new file mode 100644
index 0000000..e69de29
diff --git a/test/extract/empty.osm.opl b/test/extract/empty.osm.opl
new file mode 100644
index 0000000..e69de29
diff --git a/test/extract/empty.poly b/test/extract/empty.poly
new file mode 100644
index 0000000..e69de29
diff --git a/test/extract/invalid-root.geojson b/test/extract/invalid-root.geojson
new file mode 100644
index 0000000..19765bd
--- /dev/null
+++ b/test/extract/invalid-root.geojson
@@ -0,0 +1 @@
+null
diff --git a/test/extract/invalid.geojson b/test/extract/invalid.geojson
new file mode 100644
index 0000000..3104da2
--- /dev/null
+++ b/test/extract/invalid.geojson
@@ -0,0 +1 @@
+FOOBAR
diff --git a/test/extract/missing-end-polygon.poly b/test/extract/missing-end-polygon.poly
new file mode 100644
index 0000000..fd076b8
--- /dev/null
+++ b/test/extract/missing-end-polygon.poly
@@ -0,0 +1,8 @@
+foo
+1
+10.0 10.0
+19.0 10.0
+19.0 19.0
+10.0 19.0
+10.0 10.0
+END
diff --git a/test/extract/missing-end-ring.poly b/test/extract/missing-end-ring.poly
new file mode 100644
index 0000000..f3003dd
--- /dev/null
+++ b/test/extract/missing-end-ring.poly
@@ -0,0 +1,7 @@
+foo
+1
+10.0 10.0
+19.0 10.0
+19.0 19.0
+10.0 19.0
+10.0 10.0
diff --git a/test/extract/multipolygon.osm.opl b/test/extract/multipolygon.osm.opl
new file mode 100644
index 0000000..be91df6
--- /dev/null
+++ b/test/extract/multipolygon.osm.opl
@@ -0,0 +1,17 @@
+n10 x10.0 y10.0
+n11 x19.0 y10.0
+n12 x19.0 y19.0
+n13 x10.0 y19.0
+n14 x11.0 y11.0
+n15 x18.0 y11.0
+n16 x18.0 y18.0
+n17 x11.0 y18.0
+n20 x20.0 y20.0
+n21 x29.0 y20.0
+n22 x29.0 y29.0
+n23 x20.0 y29.0
+w40 Nn10,n11,n12,n13,n10
+w41 Nn14,n15,n16,n17,n14
+w42 Nn20,n21,n22,n23,n20
+r90 Ttype=multipolygon Mw40@,w41@
+r91 Ttype=multipolygon Mw42@
diff --git a/test/extract/no-polygon.osm.opl b/test/extract/no-polygon.osm.opl
new file mode 100644
index 0000000..7da808a
--- /dev/null
+++ b/test/extract/no-polygon.osm.opl
@@ -0,0 +1,5 @@
+n10 x10.0 y10.0
+n11 x19.0 y10.0
+n12 x19.0 y19.0
+n13 x10.0 y19.0
+w40 Nn10,n11,n12,n13
diff --git a/test/extract/one-line.poly b/test/extract/one-line.poly
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/test/extract/one-line.poly
@@ -0,0 +1 @@
+foo
diff --git a/test/extract/polygon-crlf.poly b/test/extract/polygon-crlf.poly
new file mode 100644
index 0000000..5de0a3f
--- /dev/null
+++ b/test/extract/polygon-crlf.poly
@@ -0,0 +1,9 @@
+foo
+1
+10.0 10.0
+19.0 10.0
+19.0 19.0
+10.0 19.0
+10.0 10.0
+END
+END
diff --git a/test/extract/polygon-one-outer.poly b/test/extract/polygon-one-outer.poly
new file mode 100644
index 0000000..e729b7f
--- /dev/null
+++ b/test/extract/polygon-one-outer.poly
@@ -0,0 +1,9 @@
+foo
+1
+10.0 10.0
+19.0 10.0
+19.0 19.0
+10.0 19.0
+10.0 10.0
+END
+END
diff --git a/test/extract/polygon-outer-inner.poly b/test/extract/polygon-outer-inner.poly
new file mode 100644
index 0000000..44753c9
--- /dev/null
+++ b/test/extract/polygon-outer-inner.poly
@@ -0,0 +1,16 @@
+foo
+1
+10.0 10.0
+19.0 10.0
+19.0 19.0
+10.0 19.0
+10.0 10.0
+END
+!1s
+11.0 11.0
+18.0 11.0
+18.0 18.0
+11.0 18.0
+11.0 11.0
+END
+END
diff --git a/test/extract/polygon-two-outer.poly b/test/extract/polygon-two-outer.poly
new file mode 100644
index 0000000..4746ead
--- /dev/null
+++ b/test/extract/polygon-two-outer.poly
@@ -0,0 +1,16 @@
+foo
+1
+10.0 10.0
+19.0 10.0
+19.0 19.0
+10.0 19.0
+10.0 10.0
+END
+2
+20.0 20.0
+29.0 20.0
+29.0 29.0
+20.0 29.0
+20.0 20.0
+END
+END
diff --git a/test/extract/polygon-two-ways.osm.opl b/test/extract/polygon-two-ways.osm.opl
new file mode 100644
index 0000000..edaa4d2
--- /dev/null
+++ b/test/extract/polygon-two-ways.osm.opl
@@ -0,0 +1,10 @@
+n10 x10.0 y10.0
+n11 x19.0 y10.0
+n12 x19.0 y19.0
+n13 x10.0 y19.0
+n20 x20.0 y20.0
+n21 x29.0 y20.0
+n22 x29.0 y29.0
+n23 x20.0 y29.0
+w40 Nn10,n11,n12,n13,n10
+w41 Nn20,n21,n22,n23,n20
diff --git a/test/extract/polygon-way.osm.opl b/test/extract/polygon-way.osm.opl
new file mode 100644
index 0000000..6c37e87
--- /dev/null
+++ b/test/extract/polygon-way.osm.opl
@@ -0,0 +1,5 @@
+n10 x10.0 y10.0
+n11 x19.0 y10.0
+n12 x19.0 y19.0
+n13 x10.0 y19.0
+w40 Nn10,n11,n12,n13,n10
diff --git a/test/extract/test_unit.cpp b/test/extract/test_unit.cpp
new file mode 100644
index 0000000..01d18d3
--- /dev/null
+++ b/test/extract/test_unit.cpp
@@ -0,0 +1,277 @@
+
+#include "test.hpp" // IWYU pragma: keep
+
+#include <osmium/memory/buffer.hpp>
+
+#include "error.hpp"
+#include "poly_file_parser.hpp"
+#include "osm_file_parser.hpp"
+#include "geojson_file_parser.hpp"
+
+TEST_CASE("Parse poly files") {
+    osmium::memory::Buffer buffer{1024};
+
+    SECTION("Missing file") {
+        REQUIRE_THROWS({
+            PolyFileParser parser(buffer, "test/extract/missing.poly");
+            parser();
+        });
+    }
+
+    SECTION("Empty file") {
+        PolyFileParser parser{buffer, "test/extract/empty.poly"};
+        REQUIRE_THROWS_AS({
+            parser();
+        }, poly_error);
+    }
+
+    SECTION("One line file") {
+        PolyFileParser parser{buffer, "test/extract/one-line.poly"};
+        REQUIRE_THROWS_AS({
+            parser();
+        }, poly_error);
+    }
+
+    SECTION("Two line file") {
+        PolyFileParser parser{buffer, "test/extract/two-line.poly"};
+        REQUIRE_THROWS_AS({
+            parser();
+        }, poly_error);
+    }
+
+    SECTION("Missing END ring") {
+        PolyFileParser parser{buffer, "test/extract/missing-end-ring.poly"};
+        REQUIRE_THROWS_AS({
+            parser();
+        }, poly_error);
+    }
+
+    SECTION("Missing END polygon") {
+        PolyFileParser parser{buffer, "test/extract/missing-end-polygon.poly"};
+        REQUIRE_THROWS_AS({
+            parser();
+        }, poly_error);
+    }
+
+    SECTION("File with one polygon with one outer ring") {
+        PolyFileParser parser{buffer, "test/extract/polygon-one-outer.poly"};
+        REQUIRE(parser() == 0);
+        const osmium::Area& area = buffer.get<osmium::Area>(0);
+        const auto nr = area.num_rings();
+        REQUIRE(nr.first == 1);
+        REQUIRE(nr.second == 0);
+
+        auto it = area.outer_rings().begin();
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(10.0, 10.0));
+        ++it;
+        REQUIRE(it == area.outer_rings().end());
+    }
+
+    SECTION("File with one polygons with two outer rings") {
+        PolyFileParser parser{buffer, "test/extract/polygon-two-outer.poly"};
+        REQUIRE(parser() == 0);
+        const osmium::Area& area = buffer.get<osmium::Area>(0);
+        const auto nr = area.num_rings();
+        REQUIRE(nr.first == 2);
+        REQUIRE(nr.second == 0);
+
+        auto it = area.outer_rings().begin();
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(10.0, 10.0));
+        ++it;
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(20.0, 20.0));
+        ++it;
+        REQUIRE(it == area.outer_rings().end());
+    }
+
+    SECTION("File with one polygon with outer and inner rings") {
+        PolyFileParser parser{buffer, "test/extract/polygon-outer-inner.poly"};
+        REQUIRE(parser() == 0);
+        const osmium::Area& area = buffer.get<osmium::Area>(0);
+        const auto nr = area.num_rings();
+        REQUIRE(nr.first == 1);
+        REQUIRE(nr.second == 1);
+
+        auto it = area.outer_rings().begin();
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(10.0, 10.0));
+        const auto& inner_ring = *area.inner_rings(*it).begin();
+        REQUIRE(inner_ring.front().location() == osmium::Location(11.0, 11.0));
+        ++it;
+        REQUIRE(it == area.outer_rings().end());
+    }
+
+    SECTION("Two concatenated files") {
+        PolyFileParser parser{buffer, "test/extract/two-polygons.poly"};
+        REQUIRE(parser() == 0);
+        const osmium::Area& area = buffer.get<osmium::Area>(0);
+        const auto nr = area.num_rings();
+        REQUIRE(nr.first == 2);
+        REQUIRE(nr.second == 1);
+
+        auto it = area.outer_rings().begin();
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(10.0, 10.0));
+        const auto& inner_ring = *area.inner_rings(*it).begin();
+        REQUIRE(inner_ring.front().location() == osmium::Location(11.0, 11.0));
+        ++it;
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(20.0, 20.0));
+        ++it;
+        REQUIRE(it == area.outer_rings().end());
+    }
+
+    SECTION("Two concatenated files with empty line in between") {
+        PolyFileParser parser{buffer, "test/extract/two-polygons-empty-line.poly"};
+        REQUIRE(parser() == 0);
+        const osmium::Area& area = buffer.get<osmium::Area>(0);
+        const auto nr = area.num_rings();
+        REQUIRE(nr.first == 2);
+        REQUIRE(nr.second == 1);
+    }
+
+}
+
+TEST_CASE("Parse OSM files") {
+    osmium::memory::Buffer buffer{1024};
+
+    SECTION("Missing OSM file") {
+        REQUIRE_THROWS({
+            OSMFileParser parser(buffer, "test/extract/missing.osm.opl");
+            parser();
+        });
+    }
+
+    SECTION("Empty OSM file") {
+        OSMFileParser parser{buffer, "test/extract/empty.osm.opl"};
+        REQUIRE_THROWS({
+            parser();
+        });
+    }
+
+    SECTION("OSM file without polygon") {
+        OSMFileParser parser{buffer, "test/extract/no-polygon.osm.opl"};
+        REQUIRE_THROWS({
+            parser();
+        });
+    }
+
+    SECTION("OSM file with simple polygon") {
+        OSMFileParser parser{buffer, "test/extract/polygon-way.osm.opl"};
+        REQUIRE(parser() == 0);
+        const osmium::Area& area = buffer.get<osmium::Area>(0);
+        const auto nr = area.num_rings();
+        REQUIRE(nr.first == 1);
+        REQUIRE(nr.second == 0);
+
+        auto it = area.outer_rings().begin();
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(10.0, 10.0));
+        ++it;
+        REQUIRE(it == area.outer_rings().end());
+    }
+
+    SECTION("OSM file with two simple polygons") {
+        OSMFileParser parser{buffer, "test/extract/polygon-two-ways.osm.opl"};
+        REQUIRE(parser() == 0);
+        const osmium::Area& area = buffer.get<osmium::Area>(0);
+        const auto nr = area.num_rings();
+        REQUIRE(nr.first == 2);
+        REQUIRE(nr.second == 0);
+
+        auto it = area.outer_rings().begin();
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(10.0, 10.0));
+        ++it;
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(20.0, 20.0));
+        ++it;
+        REQUIRE(it == area.outer_rings().end());
+    }
+
+    SECTION("OSM file with multipolygon relation") {
+        OSMFileParser parser{buffer, "test/extract/multipolygon.osm.opl"};
+        REQUIRE(parser() == 0);
+        const osmium::Area& area = buffer.get<osmium::Area>(0);
+        const auto nr = area.num_rings();
+        REQUIRE(nr.first == 2);
+        REQUIRE(nr.second == 1);
+
+        auto it = area.outer_rings().begin();
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(10.0, 10.0));
+        const auto& inner_ring = *area.inner_rings(*it).begin();
+        REQUIRE(inner_ring.front().location() == osmium::Location(11.0, 11.0));
+        ++it;
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(20.0, 20.0));
+        ++it;
+        REQUIRE(it == area.outer_rings().end());
+    }
+
+    SECTION("File with CRLF") {
+        PolyFileParser parser{buffer, "test/extract/polygon-crlf.poly"};
+        REQUIRE(parser() == 0);
+        const osmium::Area& area = buffer.get<osmium::Area>(0);
+        const auto nr = area.num_rings();
+        REQUIRE(nr.first == 1);
+        REQUIRE(nr.second == 0);
+
+        auto it = area.outer_rings().begin();
+        REQUIRE(it != area.outer_rings().end());
+        REQUIRE(it->front().location() == osmium::Location(10.0, 10.0));
+        ++it;
+        REQUIRE(it == area.outer_rings().end());
+    }
+
+}
+
+TEST_CASE("Parse GeoJSON files") {
+    osmium::memory::Buffer buffer{1024};
+
+    SECTION("Missing GeoJSON file") {
+        REQUIRE_THROWS({
+            GeoJSONFileParser parser(buffer, "test/extract/missing.geojson");
+            parser();
+        });
+    }
+
+    SECTION("Empty GeoJSON file") {
+        GeoJSONFileParser parser{buffer, "test/extract/empty.geojson"};
+        REQUIRE_THROWS({
+            parser();
+        });
+    }
+
+    SECTION("Invalid GeoJSON file") {
+        GeoJSONFileParser parser{buffer, "test/extract/invalid.geojson"};
+        REQUIRE_THROWS_AS({
+            parser();
+        }, geojson_error);
+    }
+
+    SECTION("Invalid GeoJSON file: Root not an object") {
+        GeoJSONFileParser parser{buffer, "test/extract/invalid-root.geojson"};
+        REQUIRE_THROWS_AS({
+            parser();
+        }, geojson_error);
+    }
+
+    SECTION("Invalid GeoJSON file: Empty root object") {
+        GeoJSONFileParser parser{buffer, "test/extract/empty-root.geojson"};
+        REQUIRE_THROWS_AS({
+            parser();
+        }, geojson_error);
+    }
+
+    SECTION("Invalid GeoJSON file: Wrong geometry type") {
+        GeoJSONFileParser parser{buffer, "test/extract/wrong-geometry-type.geojson"};
+        REQUIRE_THROWS_AS({
+            parser();
+        }, geojson_error);
+    }
+
+}
+
diff --git a/test/extract/two-line.poly b/test/extract/two-line.poly
new file mode 100644
index 0000000..3bd1f0e
--- /dev/null
+++ b/test/extract/two-line.poly
@@ -0,0 +1,2 @@
+foo
+bar
diff --git a/test/extract/two-polygons-empty-line.poly b/test/extract/two-polygons-empty-line.poly
new file mode 100644
index 0000000..4edbec9
--- /dev/null
+++ b/test/extract/two-polygons-empty-line.poly
@@ -0,0 +1,26 @@
+foo
+1
+10.0 10.0
+19.0 10.0
+19.0 19.0
+10.0 19.0
+10.0 10.0
+END
+!1s
+11.0 11.0
+18.0 11.0
+18.0 18.0
+11.0 18.0
+11.0 11.0
+END
+END
+
+bar
+2
+20.0 20.0
+29.0 20.0
+29.0 29.0
+20.0 29.0
+20.0 20.0
+END
+END
diff --git a/test/extract/two-polygons.poly b/test/extract/two-polygons.poly
new file mode 100644
index 0000000..c7e68f1
--- /dev/null
+++ b/test/extract/two-polygons.poly
@@ -0,0 +1,25 @@
+foo
+1
+10.0 10.0
+19.0 10.0
+19.0 19.0
+10.0 19.0
+10.0 10.0
+END
+!1s
+11.0 11.0
+18.0 11.0
+18.0 18.0
+11.0 18.0
+11.0 11.0
+END
+END
+bar
+2
+20.0 20.0
+29.0 20.0
+29.0 29.0
+20.0 29.0
+20.0 20.0
+END
+END
diff --git a/test/extract/wrong-geometry-type.geojson b/test/extract/wrong-geometry-type.geojson
new file mode 100644
index 0000000..b86c1f5
--- /dev/null
+++ b/test/extract/wrong-geometry-type.geojson
@@ -0,0 +1,6 @@
+{
+    "type": "Feature",
+    "geometry": {
+        "type": "Point"
+    }
+}
diff --git a/test/include/catch.hpp b/test/include/catch.hpp
index 2e6fe8d..3d18ead 100644
--- a/test/include/catch.hpp
+++ b/test/include/catch.hpp
@@ -1,6 +1,6 @@
 /*
- *  Catch v1.5.8
- *  Generated: 2016-10-26 12:07:30.938259
+ *  Catch v1.5.9
+ *  Generated: 2016-11-29 12:14:38.049276
  *  ----------------------------------------------------------
  *  This file has been merged from multiple headers. Please don't edit it directly
  *  Copyright (c) 2012 Two Blue Cubes Ltd. All rights reserved.
@@ -3428,6 +3428,7 @@ namespace Catch {
 #include <streambuf>
 #include <ostream>
 #include <fstream>
+#include <memory>
 
 namespace Catch {
 
@@ -3995,9 +3996,12 @@ namespace Clara {
         inline void convertInto( std::string const& _source, std::string& _dest ) {
             _dest = _source;
         }
+        char toLowerCh(char c) {
+            return static_cast<char>( ::tolower( c ) );
+        }
         inline void convertInto( std::string const& _source, bool& _dest ) {
             std::string sourceLC = _source;
-            std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), ::tolower );
+            std::transform( sourceLC.begin(), sourceLC.end(), sourceLC.begin(), toLowerCh );
             if( sourceLC == "y" || sourceLC == "1" || sourceLC == "true" || sourceLC == "yes" || sourceLC == "on" )
                 _dest = true;
             else if( sourceLC == "n" || sourceLC == "0" || sourceLC == "false" || sourceLC == "no" || sourceLC == "off" )
@@ -7578,7 +7582,7 @@ namespace Catch {
         return os;
     }
 
-    Version libraryVersion( 1, 5, 8, "", 0 );
+    Version libraryVersion( 1, 5, 9, "", 0 );
 
 }
 
@@ -9166,6 +9170,7 @@ namespace Catch {
     public:
         XmlReporter( ReporterConfig const& _config )
         :   StreamingReporterBase( _config ),
+            m_xml(_config.stream()),
             m_sectionDepth( 0 )
         {
             m_reporterPrefs.shouldRedirectStdOut = true;
@@ -9185,7 +9190,6 @@ namespace Catch {
 
         virtual void testRunStarting( TestRunInfo const& testInfo ) CATCH_OVERRIDE {
             StreamingReporterBase::testRunStarting( testInfo );
-            m_xml.setStream( stream );
             m_xml.startElement( "Catch" );
             if( !m_config->name().empty() )
                 m_xml.writeAttribute( "name", m_config->name() );
diff --git a/test/util/test_unit.cpp b/test/util/test_unit.cpp
new file mode 100644
index 0000000..1718fdb
--- /dev/null
+++ b/test/util/test_unit.cpp
@@ -0,0 +1,25 @@
+
+#include "test.hpp" // IWYU pragma: keep
+
+#include "util.hpp"
+
+TEST_CASE("Get suffix from filename") {
+    REQUIRE(get_filename_suffix("foo.bar") == "bar");
+}
+
+TEST_CASE("Get suffixes from filename") {
+    REQUIRE(get_filename_suffix("foo.bar.baz") == "bar.baz");
+}
+
+TEST_CASE("Get suffixes from file path") {
+    REQUIRE(get_filename_suffix("/usr/local/foo.bar.baz") == "bar.baz");
+}
+
+TEST_CASE("Get suffixes from relative path") {
+    REQUIRE(get_filename_suffix("../somewhere/foo.bar.baz") == "bar.baz");
+}
+
+TEST_CASE("Get suffixes from path with dots in the middle") {
+    REQUIRE(get_filename_suffix("anything/../somewhere/foo.bar.baz") == "bar.baz");
+}
+
diff --git a/zsh_completion/_osmium b/zsh_completion/_osmium
index 5d37301..e3425cc 100644
--- a/zsh_completion/_osmium
+++ b/zsh_completion/_osmium
@@ -14,10 +14,11 @@
 #
 
 osmium_file_glob="'*.(osm|osh|osc|o5m|o5c|pbf|osm.pbf) *.(osm|osh|osc|o5m|o5c).(bz2|gz)'"
+polygon_file_glob="'*.json *.geojson *.poly *.(osm|osh|osc|o5m|o5c|pbf|osm.pbf) *.(osm|osh|osc|o5m|o5c).(bz2|gz)'"
 
 _osmium() {
     local -a osmium_commands
-    osmium_commands=(add-locations-to-ways apply-changes cat diff changeset-filter check-refs derive-changes fileinfo getid help merge merge-changes renumber show sort time-filter)
+    osmium_commands=(add-locations-to-ways apply-changes cat diff changeset-filter check-refs derive-changes extract fileinfo getid help merge merge-changes renumber show sort time-filter)
     if (( CURRENT > 2 )); then
         # Remember the subcommand name
         local cmd=${words[2]}
@@ -53,7 +54,9 @@ _osmium-multiple-inputs-options() {
 }
 
 _osmium-output-options() {
-    echo '--generator[generator setting for output file header]:generator:'
+    echo '--fsync[call fsync after writing output file(s)]'
+    echo '--generator[generator setting for output file header]:'
+    echo "*--output-header[add option to output header]:"
     echo "(--output)-o[output file name]:output OSM file:_files -g ${osmium_file_glob}"
     echo "(-o)--output[output file name]:output OSM file:_files -g ${osmium_file_glob}"
     echo '(--overwrite)-O[allow overwriting of existing output file]'
@@ -76,8 +79,8 @@ _osmium-add-locations-to-ways() {
         ${(f)"$(_osmium-multiple-inputs-options)"} \
         ${(f)"$(_osmium-output-format-options)"} \
         ${(f)"$(_osmium-output-options)"} \
-        '(--index-type -I --show-index-types)-i[set index type]:index_types:_osmium_index_types' \
-        '(-i -I --show-index-types)--index-type[set index type]:index_types:_osmium_index_types' \
+        '(--index-type -I --show-index-types)-i[set index type]:index types:_osmium_index_types' \
+        '(-i -I --show-index-types)--index-type[set index type]:index types:_osmium_index_types' \
         '(--show-index-types -i --index-type -n --keep-untagged-nodes)-I[show available index types]' \
         '(-I -i --index-type -n --keep-untagged-nodes)--show-index-types[show available index types]' \
         '(--keep-untagged-nodes -I --show-index-types)-n[keep untagged nodes in output]' \
@@ -174,6 +177,27 @@ _osmium-diff() {
         '*--object-type[read only objects of given output types]:OSM entity type:_osmium_object_type'
 }
 
+_osmium-extract() {
+    _arguments : \
+        ${(f)"$(_osmium-common-options)"} \
+        ${(f)"$(_osmium-single-input-options)"} \
+        ${(f)"$(_osmium-output-options)"} \
+        ${(f)"$(_osmium-output-format-options)"} \
+        '(--config -c --directory -d --polygon -p --bbox)-b[bounding box]:bounding box (format\: LEFT,BOTTOM,RIGHT,TOP):' \
+        '(--config -c --directory -d --polygon -p -b)--bbox[bounding box]:bounding box (format\: LEFT,BOTTOM,RIGHT,TOP):' \
+        '(--bbox -b --polygon -p --output -o --output-format -f --config)-c[specify config file]:config file:_files -g "*.json"' \
+        '(--bbox -b --polygon -p --output -o --output-format -f -c)--config[specify config file]:config file:_files -g "*.json"' \
+        '(--output -o --output-format -f --bbox -b --polygon -p --directory)-d[output directory]:directory:_path_files -/' \
+        '(--output -o --output-format -f --bbox -b --polygon -p -d)--directory[output directory]:directory:_path_files -/' \
+        "(--config -c --directory -d --bbox -b --polygon)-p[polygon file]:polygon file:_files -g ${polygon_file_glob}" \
+        "(--config -c --directory -d --bbox -b -p)--polygon[polygon file]:polygon file:_files -g ${polygon_file_glob}" \
+        '(--strategy)-s[use strategy for computing extract]:extract strategy:_osmium_extract_strategy' \
+        '(-s)--strategy[use strategy for computing extract]:extract strategy:_osmium_extract_strategy' \
+        '*-S[set strategy option]:' \
+        '*--option[set strategy option]:' \
+        '--with-history[input and output files are OSM history files]'
+}
+
 _osmium-fileinfo() {
     _arguments : \
         ${(f)"$(_osmium-common-options)"} \
@@ -336,19 +360,26 @@ _osmium_entity_type() {
         'changeset'
 }
 
+_osmium_extract_strategy() {
+    _values 'extract strategy' \
+        'simple' \
+        'complete_ways' \
+        'smart'
+}
+
 _osmium_fileinfo_variables() {
     _values 'variable' $(osmium fileinfo --show-variables)
 }
 
 _osmium_index_types() {
-    _values 'index-types' $(osmium add-locations-to-ways --show-index-types)
+    _values 'index types' $(osmium add-locations-to-ways --show-index-types)
 }
 
 _osmium-help() {
     local -a osmium_help_topics
-    osmium_help_topics=(add-locations-to-ways apply-changes cat diff changeset-filter check-refs derive-changes fileinfo getid help merge merge-changes renumber show sort time-filter file-formats)
+    osmium_help_topics=(add-locations-to-ways apply-changes cat diff changeset-filter check-refs derive-changes extract fileinfo getid help merge merge-changes renumber show sort time-filter file-formats)
     _describe -t osmium-help-topics 'osmium help topics' osmium_help_topics
 }
 
-#_osmium "$@"
+_osmium "$@"
 

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/osmium-tool.git



More information about the Pkg-grass-devel mailing list